0
点赞
收藏
分享

微信扫一扫

linux进程间通信--共享内存


一、IPC(Inter-Process Communication,进程间通信)对象的介绍


System V 的IPC对象有共享内存、消息队列、信号灯。

linux进程间通信--共享内存_#include

注意:在IPC的通信模式下,不管是使用消息队列还是共享内存,甚至是信号灯,每个IPC的对象都有唯一的名字,称为"键"(key)。通过"键",进程能够识别所用的对象。"键"与IPC对象的关系就如同文件名称于文件,通过文件名,进程能够读写文件内的数据,甚至多个进程能够公用一个文件。而在 IPC的通讯模式下,通过"键"的使用也使得一个IPC对象能为多个进程所共用。




二、共享内存的介绍



<1>共享内存是一种最为高效的进程间通信方式,进程可以直接读写内存,而不需要任何数据的拷贝。





为了在多个进程间交换信息,内核专门留出了一块内存区,可以由需要访问的进程将其映射到自己的私有地址空间。进程就可以直接读写这一块内存而不需要进行数据的拷贝,从而大大提高效率。





<3>由于多个进程共享一段内存,因此也需要依靠某种同步机制。





三、共享内存的特点



linux进程间通信--共享内存_linux_02



四、共享内存的操作流程





<1>创建/打开共享内存



<2>映射共享内存,即把指定的共享内存映射到进程的地址空间用于访问



<3>撤销共享内存映射



<4>删除共享内存对象





五、相关API 





A.获取一块共享内存



linux进程间通信--共享内存_linux_03



功能:分配一块共享内存





返回值:





调用成功返回一个shmid(类似打开一个或创建一个文件获得的文件描述符一样);



调用失败返回-1。





参数说明:





<1>key标识共享内存的键值(就像文件的标识是文件名):0  / IPC_PRIVATE。





当key的取值为IPC_PRIVATE,则函数shmget()将创建一块新的共享内存;





如果key的取值为0,而参数shmflg中设置了IPC_CREATE这个标志,则同样创建一块新的共享内存。





通过这种方式分配的共享内存,一般用来亲缘关系的进程间通信。





注意:我们一般是通过ftok这个函数获取键值

功能 : 获取一个IPC对象的键值





参数说明:





pthname就是你指定文件名的路径(该文件必须是存在而且可以访问的),一般情况我们都写一个目录





proj_id  : 和pthname一起完成创建键值的参数,虽然为int,但是只有8个比特被使用。一般我们写一个字符代替。





例如:





案例:

linux进程间通信--共享内存_linux_04



运行的结果:

linux进程间通信--共享内存_键值_05

size是要建立共享内存的长度。所有的内存分配操作都是以页为单位的。所以如果一个进程只申请一块只有一个字节的内存,内存也会分配整整一页(在i386机器中一页的缺省大小PACE_SIZE = 4096字节)。





<3>shmflg有效的标志包括IPC_CREAT 和IPC_EXCL,他们的功能与open()的O_CREAT和O_EXCL相当。





IPC_CREAT      如果共享内存不存在,则创建一个共享内存,否则直接打开已存在的



IPC_EXCL        只有在共享内存不存在的时候,新的共享内存才建立,否则就产生错误

linux进程间通信--共享内存_键值_06

例子一:假设键值为key,创建一个共享内存大小为4k,访问权限为066,如果已经存在则返回其标识号 


int shmid; 
    
 
    

      if( (shmid = shmget(key,4 * 1024,0666 | IPC_CREAT)) < 0) 
    
 
    

      { 
    
 
    

          perror("Fail to shmget"); 
    
 
    

          exit(EXIT_FAILURE) 
    
 
    

      }




例子二、假设键值为key,创建一个共享内存大小为1k,访问权限为0666,如果已经存在则报错


int shmid; 
    
 
    

      if((shmid  = shmget(key,1024,0666 | IPC_CREAT | IPC_EXCL)) < 0) 
    
 
    

      { 
    
 
    

          perror("Fail to shmget"); 
    
 
    

          exit(EXIT_FAILURE); 
    
 
    

      }


B.共享内存的映射


linux进程间通信--共享内存_linux_07



函数shmat将标识号为shmid共享内存映射到调用进程的地址空间中。





参数说明:





shmid  :  要映射的共享内存区标识符





shmaddr  :  将共享内存映射到指定地址(若为NULL,则表示由系统自动完成映射)





shmflg  :  SHM_RDONLY  共享内存只读



                默认0:共享内存可读写。





返回值 :调用成功放回映射后的地址 ,出错放回(void *)-1;





C.取消共享内存与用户进程之间的映射

linux进程间通信--共享内存_#include_08

参数shmaddr是shmat映射成功放回的地址。





而是把相关shmid_ds结构的shm_nattch域的值减1, 当这个值为0时,内核才从物理上删除这个共享段。





D.控制共享内存

linux进程间通信--共享内存_#include_09

参数说明:





shmid  共享内存标识ID





cmd      IPC_STAT得到共享内存的状态



              IPC_SET改变共享内存的状态



   IPC_RMID删除共享内存





buf  是一个结构体指针。IPC_STAT的时候,取得的状态放在这个结构体中。如果要改变共享内存的状态,用这个结构体指定;



linux进程间通信--共享内存_linux_10



注意:





1.IPC_RMID命令实际上不从内核删除一个段,而是仅仅把这个段标记为删除,实际的删除发生最后一个进程离开这个共享段时。





2.当cmd为IPC_RMID时,第三个参数应为NULL。呵呵,大部分我们都是这样做,用这个函数删除共享内存。







案例探究:

此处)折叠或打开

1. #include <sys/shm.h>
2. <stdio.h>
3. <stdlib.h>
4. <string.h>
5. <semaphore.h>
6. <fcntl.h>
7. <sys/stat.h>
8.  
9.  
10.  #define BUFF_SIZE 1024
11.  
12. int father_do_work(int shmid)
13. {
14. *buf;
15. *shmaddr;
16. *prsem;
17. *pwsem;
18.  
19. //有名信号量
20. if((prsem = sem_open("rsem",O_CREAT,0666,0)) == SEM_FAILED)
21. {
22. ("Fail to sem open");
23. -1;
24. }
25.          
26. //有名信号量
27. if((pwsem = sem_open("wsem",O_CREAT,0666,1)) == SEM_FAILED)
28. {
29. ("Fail to sem open");
30. -1;
31. }
32.  
33. //映射共享内存
34. if((shmaddr = shmat(shmid,NULL,0)) == (void *)-1)
35. {
36. ("Fail to shmat");
37. exit(EXIT_FAILURE);
38. }
39.  
40. = (char *)shmaddr;
41.  
42. while(1)
43. {
44. if(sem_wait(pwsem) < 0)
45. {
46. ("Fail to sem wait");
47. ;
48. }
49.  
50. (">");
51. (buf,BUFF_SIZE,stdin);
52. [strlen(buf) - 1] = '\0';
53.          
54. if(sem_post(prsem) < 0)
55. {
56. ("Fail to sem post");
57. ;
58. }
59.          
60. if(strncmp(buf,"quit",4) == 0)
61. {
62. if(shmdt(shmaddr) < 0)
63. {
64. ("Fail to shmaddr");
65. exit(EXIT_FAILURE);
66. }
67.  
68. ;
69. }
70.      
71. (500);
72. }
73.      
74. ;
75. }
76.  
77. int child_do_work(int shmid)
78. {
79. *buf;
80. *shmaddr;
81. *prsem;
82. *pwsem;
83.  
84. //
85. if((prsem = sem_open("rsem",O_CREAT,0666,0)) == SEM_FAILED)
86. {
87. ("Fail to sem open");
88. -1;
89. }
90.  
91. if((pwsem = sem_open("wsem",O_CREAT,0666,1)) == SEM_FAILED)
92. {
93. ("Fail to sem open");
94. -1;
95. }
96.  
97. //映射共享内存
98. if((shmaddr = shmat(shmid,NULL,0)) == (void *)-1)
99. {
100. ("Fail to shmat");
101. exit(EXIT_FAILURE);
102. }
103.  
104. = (char *)shmaddr;
105.  
106. while(1)
107. {
108. if(sem_wait(prsem) < 0)
109. {
110. ("Fail to prsem");
111. ;
112. }
113.  
114. ("read buf : %s.\n",buf);
115.  
116. if(sem_post(pwsem) < 0)
117. {
118. ("Fail to pwsem");
119. ;
120. }
121.  
122. if(strncmp(buf,"quit",4) == 0)
123. {
124. if(shmdt(shmaddr) < 0)
125. {
126. ("Fail to shmaddr");
127. exit(EXIT_FAILURE);
128. }
129.  
130. ;
131. }
132. }
133.      
134. ;
135. }
136.  
137. int main()
138. {
139. int shmid;
140. int pid;
141. *shmaddr;
142.      
143. //创建共享内存
144. if((shmid = shmget(IPC_PRIVATE,BUFF_SIZE,0666 | IPC_CREAT)) < 0)
145. {
146. ("Fail to shmget");
147. exit(EXIT_FAILURE);
148. }
149.  
150.  
151. if((pid = fork()) < 0)
152. {
153.  
154. ("Fail to fork");
155. exit(EXIT_FAILURE);
156.      
157. }else if(pid == 0){
158.      
159. (shmid);
160.      
161. }else{
162.          
163. (shmid);
164. (NULL);
165.  
166. if(shmctl(shmid,IPC_RMID,NULL) < 0)
167. {
168. ("Fail to shmctl");
169. exit(EXIT_FAILURE);
170. }
171. }
172.  
173. exit(EXIT_SUCCESS);
174. }

运行结果:



linux进程间通信--共享内存_linux_11




举报

相关推荐

0 条评论