Linux 进程通信,System V 第五节------>共享内存区—转
一:简介
前面已经学过:pipe,FIFO,msg queue, 今天要学的是“共享内存区”
1.共享内存区是进程通信中最快的方式,而且传递的信息量是很大的!
2.是通过内存区间映射到进程空间来实现的!因此这种进程间的通信不再涉及到内核!
(即进程不是通过执行任何的进入内核的系统调用来传递数据的。这样内核就必须建立允许各个进程之间的
共享内存区的映射关系,然后一直管理该内存区!同时也要保证所谓的同步,有序,而且没有死锁!)
3.简单的实现过程:
<1>.server获得访问共享内存区的权限
<2>.server从输入文件中读取数据到共享内存区(需要一次copy内容 )
<3>.server输入数据OK后,通知用户进程
<4>.最后用户进程从共享内存区中取出data( 需要一次copy内容)
但是与pipe,FIFO以及msg queue的区别是:
此三者需要进程的操作是:server和client的发送和接受data都是需要进行一次copy,所以一共有4次数据拷贝,所以效率不如“共享内存区”
4.数据结构:
1 #include<sys/shm.h> 2 3 structshmid_ds 4 { 5 structipc_perm shm_perm; //!> 权限设置结构体 6 size_t shm_segsz; //!> 内存块大小 7 pid_t shm_lpid; //!> 最后一次操作的进程ID 8 pid_t shm_cpid; //!> 创建进程的ID 9 shmatt_t shm_nattch; //!> 当前的附接数 10 shmatt_t shm_cnattch; //!> 内核的附接数 11 time_t shm_atime; //!> 最后一次关联时间 12 time_t shm_dtime; //!> 分离时间 13 time_t shm_ctime; //!> 最后一次改变时间 14 };5.共享内存区的创建和操作:
#include<sys/types>
#include<sys/ipc.h>
#include<sys/shm.h>
int shmget(key_t key, size_t size, int oflag );
参数:
key:ftok返回值或者IPC_PRIVATE
size:共享内存区大小( 字节为单位,if访问一个已经存在的,那么就是0 )
oflag:权限的组合(同前面讲的 )
创建OK后,那么就可以使用shmat函数来链接到它的地址空间!
void *shmat( int shmid, const void * addr, int flag);
//!> -------->链接共享区到哪个地址上与addr参数以及flag中是否指定SHM_RND有关!
//!> if addr == 0,那么连接到内核选择的第一个可用地址上
//!> if addr!= 0 && 没有SHM_RND,那么连接到addr上
//!> if addr !=0 && 指定SHM_RND,那么连接到addr-( addr modSHMLBA )上,SHM_RND是取整意思
//!>SHMLBA是指最低边界地址倍数。所以此算式表示靠近addr的一个边界地址上
使用OK后就可以断开链接:shmdt函数
int shmdt(const void * addr); //!> 注意参数是shmat的返回值!!!
最后要删除内存:shmctl函数
int shmctl(int shmid, int cmd, struct shmid_ds * buff );
参数:
shmid:就是创建或者打开是shmget函数的返回值
cmd:有多种取值
buff:主要用在IPC_STAT取回结构体的值和IPC_SET设置结构体值中使用!
cmd:
IPC_STAT:取得shmid_ds的结构体,放在buff中
IPC_SET:按照buff设置结构体权限值-> sem_perm.uid, sem_perm.gid, sem_perm.mode;注意其允许执行的权限进程( 与前面一样)
IPC_RMID:删除共享内存区,注意其删除是与文件inode差不多,只有当计数值为0才删除,否则仅仅是删除一个标志顺便计数器--就OK!
SHM_LOCK:锁住共享内存区,只有super用户权限才OK!
SHM_UNLOCK: 解锁,权限用户为super
二.
//-------------------------------------------------------------------------------------------------
//简单的函数应用
////// 创建实例
1 #include <stdio.h> 2 #include <sys/types.h> 3 #include <sys/ipc.h> 4 #include <stdlib.h> 5 #include <sys/shm.h> 6 7 int main( int argc, char ** argv ) 8 { 9 int semid; 10 int flag; 11 size_t len; 12 13 if( argc !=3 ) //!> 表示我们要输入2个字符串参数(因为第一个是默认的程序运行的全路径名 ) 14 { 15 printf("usage:shmget <pathname><length>\n" ); 16 exit(EXIT_FAILURE ); 17 } 18 19 len = atoi(argv[2] ); //!> 第二个参数作为长度而已 20 flag =IPC_CREAT; //!> 创建标志( 具有唯一性 )------->注意IPC_EXCL:决定了唯一性! | IPC_EXCL 21 22 //!>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>. 23 //!> Create the sempore 24 if( ( sema' ), len, flag ) ) == -1 ) 25 { 26 printf("\nCreate semaprore error...\n"); 27 exit(EXIT_FAILURE ); 28 } 29 30 printf("\nThe semid = %d\n", semid ); 31 32 //!>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>. 33 //!> Display attribute 34 intdis; 35 structshmid_ds buff[10]; 36 if( ( dis =shmctl( semid, IPC_STAT, buff ) ) == -1 ) 37 { 38 printf("\nDisplay the attribute error...\n" ); 39 exit(EXIT_FAILURE ); 40 } 41 42 //!>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>. 43 //!> Delete the semapore 44 intdel; 45 if( ( del =shmctl( semid, IPC_RMID, NULL ) ) == -1 ) 46 { 47 printf("\nDelete error... \n"); 48 exit(EXIT_FAILURE ); 49 } 50 51 return0; 52 }
三:
//-------------------------------------------------------------------------------------------------
// 生产者与消费者
1.core简介:
我们可以知道在server(生产者)中我们给的最大的src就是5个,所以if我们仅仅是只执行serever,那么执行5次后必须要等待,因为P不到src了,但是if有client(消费者)存在,那么就可以,因为消费者消费OK后
归还src,那么server又可以执行下去了。
2.
CODE:
////// producer
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 #include <fcntl.h> 5 #include <unistd.h> 6 #include <sys/types.h> 7 #include <sys/ipc.h> 8 #include <sys/sem.h> 9 #include <sys/shm.h> 10 #include <sys/stat.h> 11 12 #define MAXSHM 5 //!> 定义缓冲区变量个数 13 14 union semun //!> 此处我们主要是为了SETVALUE使用 15 { 16 int val; //!>设置信号的值 17 structsemid_ds* buf; //!> buffer:IPC_STAT, IPC_SET 18 unsignedshort* array; //!> GETALL, SETALL的数组 19 }; 20 21 int main() 22 { 23 key_t ipckey; //!> ipc 的 key 24 key_t semkey; //!> 信号量的key 25 26 int shmid; //!> ipc(此处是共享内存区模式导致)的ID 27 int semid; //!> 共享内存区ID 28 29 char* addr_c; //!> 共享区的地址 30 31 //!>>>>>>>>>>>>>>>>>>>>>>>>>> 32 //!> 对于共享区的处理(key and id and addr. ) 33 34 ipckey =ftok( "/tmp/Linux/ipc", 368); //!> get ipc key 35 if( ipckey== -1 ) 36 { 37 printf("\nCreate IPC key error...\n" ); 38 exit(EXIT_FAILURE ); 39 } 40 41 shmid =shmget( ipckey, 1024, IPC_CREAT | 0666); //!> get ipc id 42 if( shmid ==-1 ) 43 { 44 printf("\nCreate IPC id error...\n" ); 45 exit(EXIT_FAILURE ); 46 } 47 48 addr_c = (char * )shmat( shmid, NULL, 0); //!> 链接到第一个可用的地址上 49 if( *( ( int* )addr_c ) == -1) //!> Set addr... 50 { 51 printf("\nSet addr. error...\n" ); 52 exit(EXIT_FAILURE ); 53 } 54 55 //!>>>>>>>>>>>>>>>>>>>>>>>>>> 56 //!> 对于信号量的处理(key and id) 57 58 structsembuf P, V; //!> P V 操作变量 59 union semun arg1, arg2,arg3; //!> 设置semid此信号集合中的三种信号量 60 //!> 具体的下面有解释 61 62 semkey =ftok( "/tmp/Linux/sem", 368); //!> get sempore key 63 if( semkey== -1 ) 64 { 65 printf("\nCeate sem. key error...\n" ); 66 exit(EXIT_FAILURE ); 67 } 68 69 //!> 请注意此处创建的一个信号量集合! 70 //!> 里面可以有不同的信号处理不同事件!!!! 71 semid =semget( semkey, 3, IPC_CREAT | 0666); //!> get sem. id 72 //!> 信号集合中信号为3种 73 if( semid< 0 ) 74 { 75 printf("\nCreate sem. id error...\n" ); 76 exit(EXIT_FAILURE ); 77 } 78 79 //!>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 80 //!> 初始化信号灯中信号量 81 82 83 84 arg1.val =0; //!> 缓冲区无数据,( 即信号满 ) 85 if( semctl(semid, 0, SETVAL, arg1 ) == -1) //!> 设置VALUE = arg1 = 0 86 { //!> sem 编号为0 87 printf("\nSelValue (信号满) error...\n" ); 88 exit(EXIT_FAILURE ); 89 } 90 91 arg2.val =MAXSHM; //!> 缓冲区 5 个空闲元素 92 if( semctl(semid, 1, SETVAL, arg2 ) == -1) //!> 设置VALUE == arg2 = 5 93 { //!> sem 编号为1 94 printf("\nSetValue (信号空) error...\n" ); 95 exit(EXIT_FAILURE ); 96 } 97 98 arg3.val =1; //!> 这个相当于是互斥使用缓冲区 99 if( semctl(semid, 2, SETVAL, arg3 ) == -1 ) 100 { 101 printf("\nCreate 互斥缓冲区 error...\n" ); 102 exit(EXIT_FAILURE ); 103 } 104 105 //!>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 106 //!> 初始化P V 操作 107 108 P.sem_num =0; 109 P.sem_op =-1; //!> 注意:P的操作是 -- 操作( 就是-1处理) 110 P.sem_flg =SEM_UNDO; 111 112 V.sem_num =0; 113 V.sem_op= 1; //!> 注意:V的操作是 ++ 操作( 就是+1处理 ) 114 V.sem_flg =SEM_UNDO; 115 116 //!>下面进行的就是简单的PV操作 117 int i =0; 118 while( i< 10) //!> 进行10次操作 119 { 120 P.sem_num =1; //!> 注意P操作的是index=1的信号量 121 //!> 也就是存在元素的集合(--操作) 122 semop(semid, &P, 1); //!> 进行P操作一次 123 124 P.sem_num =2; //!> 此处是互斥操作信号量 125 semop(semid, &P, 1); //!> 只让一个进程操作,其他的等待 126 127 addr_c[i] =i + 'a'; //!> 仅仅是为了输出显示而已 128 printf("\n产生空间 addr_c[%d] =%c \n", i, addr_c[i]); 129 130 V.sem_num =2; //!> 对互斥操作进行V 131 semop(semid, &V, 1 ); 132 133 V.sem_num =0; //!> P一个src后,就要加入开始没有元素的信号量中 134 semop(semid, &V, 1 ); 135 136 i++; 137 sleep( 1); 138 } 139 140 sleep( 60); 141 142 //!>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 143 //!> 退出共享区失败 144 145 if( shmdt(addr_c ) == -1) 146 { 147 printf("\n退出共享区失败\n" ); 148 exit(EXIT_FAILURE ); 149 } 150 151 //!>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 152 //!> 删除共享区 153 154 if( shmctl(shmid, IPC_RMID, NULL ) == -1) 155 { 156 printf("\n删除共享区失败\n" ); 157 exit(EXIT_FAILURE ); 158 } 159 160 //!>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 161 //!> 删除信号集 162 163 if( semctl(semid, 0, IPC_RMID, 0 ) == -1 ) 164 { 165 printf("\n撤销信号集失败\n" ); 166 exit(EXIT_FAILURE ); 167 } 168 169 exit(EXIT_SUCCESS ); 170 } 171 172 173 174 ///// consumer 175 176 #include <stdio.h> 177 #include <string.h> 178 #include <stdlib.h> 179 #include <fcntl.h> 180 #include <sys/stat.h> 181 #include <sys/ipc.h> 182 #include <sys/sem.h> 183 #include <sys/shm.h> 184 #include <sys/types.h> 185 186 #define MAXSHM 5 187 188 union semun //!> 此处我们主要是为了SETVALUE使用 189 { 190 int val; //!>设置信号的值 191 structsemid_ds* buf; //!> buffer:IPC_STAT, IPC_SET 192 unsignedshort* array; //!> GETALL, SETALL的数组 193 }; 194 195 int main() 196 { 197 key_t ipckey; 198 key_t semkey; 199 200 int shmid; 201 int semid; 202 203 char* addr_c; 204 205 //!>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 206 //!> 得到IPC/SEM的key和id 207 208 ipckey =ftok( "/tmp/Linux/ipc", 368); //!> get ipc key 209 if( ipckey== -1 ) 210 { 211 printf("\nCreate IPC key error...\n"); 212 exit(EXIT_FAILURE ); 213 } 214 215 shmid =shmget( ipckey, 1024, IPC_EXCL | 0666 ); 216 if( shmid ==-1 ) 217 { 218 printf("\nCreate IPC ID error...\n" ); 219 exit(EXIT_FAILURE ); 220 } 221 222 addr_c = (char * )shmat( shmid, NULL, 0 ); 223 if( *( ( int* ) addr_c ) == -1 ) 224 { 225 printf("\nCreate addr error...\n" ); 226 exit(EXIT_FAILURE ); 227 } 228 229 //!>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 230 //!> 定义信号量的数据结构 231 232 structsembuf P, V; 233 234 semkey =ftok( "/tmp/Linux/sem", 368 ); 235 if( semkey== -1 ) 236 { 237 printf("\nCreate sem. key error... \n"); 238 exit(EXIT_FAILURE ); 239 } 240 241 semid =semget( semkey, 0, IPC_EXCL | 0666); 242 //!> 在“生产者”中已经创建了,所以此处只要引用就好,所以第二参数0 243 if( semid< 0 ) 244 { 245 printf("\nCreate sem. ID error...\n" ); 246 exit(EXIT_FAILURE ); 247 } 248 249 //!>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 250 //!> 初始化P V 操作 251 252 P.sem_num =0; 253 P.sem_op =-1; //!> 减去1操作 254 P.sem_flg =SEM_UNDO; 255 256 V.sem_num =0; 257 V.sem_op= 1; //!> 加上1操作 258 V.sem_flg =SEM_UNDO; 259 260 int i =0; 261 while( i< 10 ) 262 { 263 //!> 先需要等待 264 P.sem_num =0; //!> 注意开始我们知道这里面没有空间, 265 //!> 所以要等待server端的V操作,整体 266 //!> 看来我们知道,server P10次V10次, 267 //!> 此处就连续的读写就可以了 268 semop( semid, &P, 1 ); 269 270 P.sem_num =2; //!> 此处是互斥操作 271 semop( semid, &P, 1 ); 272 273 printf("\n消费空间 addr_c[%d] = %c\n", i,addr_c[i]); 274 275 V.sem_num =2; //!> 释放互斥区 276 semop( semid, &V, 1 ); 277 278 V.sem_num =1; //!> 那么 原来装有src信号量有了新的空间 279 semop( semid, &V, 1); //!> 也就是 ++ 处理 280 281 i++; 282 sleep( 2 ); 283 } 284 285 //!>>>>>>>>>>>>>>>>>>>>>>>> 286 //!> 下面释放链接 287 288 if( shmdt(addr_c ) == -1 ) 289 { 290 printf("\n退出共享区失败\n" ); 291 exit(EXIT_FAILURE ); 292 } 293 294 exit(EXIT_FAILURE ); 295 }
- 上一篇 »Java内存释放机制
- 下一篇 »linux 下共享库创建及使用