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 }