进程间通信方式——4、共享内存(shm)

1.什么是共享内存?

共享内存就是允许两个或多个进程共享一定的存储区。就如同 malloc() 函数向不同进程返回了指向同一个物理内存区域的指针。当一个进程改变了这块地址中的内容的时候,其它进程都会察觉到这个更改。因为数据不需要在客户机和服务器端之间复制,数据直接写到内存,不用若干次数据拷贝,所以这是最快的一种IPC。
注:共享内存没有任何的同步与互斥机制,所以要使用信号量来实现对共享内存的存取的同步。

2.与共享内存有关的数据结构

system V版本的通信方式都具有相似的数据结构,参考见:


注:其中的同只是将数据结构中的消息队列msg换成shm而已

3.与共享内存有关的函数

所有的函数共用头文件
  1. #include <sys/types.h>  
  2. #include <sys/ipc.h>  
  3. #include <sys/shm.h>  

3.1创建共享内存——>shmget() 函数
  1. int shmget(key_t key, size_t size, int shmflg);  
  2.                 //成功返回共享内存的ID,出错返回-1          

(1)第一个参数key是长整型(唯一非零),系统建立IPC通讯 ( 消息队列、 信号量和 共享内存) 时必须指定一个ID值。通常情况下,该id值通过ftok函数得到,由内核变成标识符,要想让两个进程看到同一个信号集,只需设置key值不变就可以。

 (2)第二个参数size指定共享内存的大小,它的值一般为一页大小的整数倍(未到一页,操作系统向上对齐到一页,但是用户实际能使用只有自己所申请的大小)。

 (3)第三个参数shmflg是一组标志,创建一个新的共享内存,将shmflg 设置了IPC_CREAT标志后,共享内存存在就打开。而IPC_CREAT | IPC_EXCL则可以创建一个新的,唯一的共享内存,如果共享内存已存在,返回一个错误。一般我们会还或上一个文件权限

3.2操作共享内存———>shmctl()函数
  1. int shmctl(int shm_id, int cmd, struct shmid_ds *buf);   
  2.                 //成功返回0,出错返回-1  


(1)第一个参数,shm_id是shmget函数返回的共享内存标识符。

(2)第二个参数,cmd是要采取的操作,它可以取下面的三个值 :    

IPC_STAT:把shmid_ds结构中的数据设置为共享内存的当前关联值,即用共享内存的当前关联值覆盖shmid_ds的值。    

IPC_SET:如果进程有足够的权限,就把共享内存的当前关联值设置为shmid_ds结构中给出的值    

IPC_RMID:删除共享内存段

(3)第三个参数,buf是一个结构指针,它指向共享内存模式和访问权限的结构。 shmid_ds结构至少包括以下成员 
  1. struct shmid_ds    
  2. {    
  3.     uid_t shm_perm.uid;    
  4.     uid_t shm_perm.gid;    
  5.     mode_t shm_perm.mode;    
  6. };          


3.3挂接操作———>shmat()函数


创建共享存储段之后,将进程连接到它的地址空间
  1. void *shmat(int shm_id, const void *shm_addr, int shmflg);   
  2.                     //成功返回指向共享存储段的指针,出错返回-1  

(1)第一个参数,shm_id是由shmget函数返回的共享内存标识。


(2)第二个参数,shm_addr指定共享内存连接到当前进程中的地址位置,通常为空,表示让系统来选择共享内存的地址。

(3)第三个参数,shm_flg是一组标志位,通常为0

3.4分离操作———>shmdt()函数

该操作不从系统中删除标识符和其数据结构,要显示调用shmctl(带命令IPC_RMID)才能删除它

  1. <span style="color:#999999;">int shmdt(const void *shmaddr);   
  2.             //成功返回0,出错返回-1</span>  


(1)addr参数是以前调用shmat时的返回值

4.模拟实现进程间的通信方式———>共享内存

Server作为发送方,每次向共享内存中,写入A,Client作为接收方,每次读取共享内存中的数据

comm.h

  1. #ifndef __COMM_H__  
  2. #define __COMM_H__  
  3. #include <stdio.h>  
  4. #include <error.h>  
  5. #include <sys/types.h>  
  6. #include <sys/ipc.h>  
  7. #include <sys/shm.h>  
  8. #define PATHNAME "."  
  9. #define PROJ_ID 066  
  10. int CreatShmid(int size);  
  11. int GetShmid(int size);  
  12. int Destory(int shmid);  
  13. #endif  

comm.c

  1. #include "comm.h"  
  2. static int commShmid(int size,int flag)  
  3. {  
  4.     key_t key=ftok(PATHNAME,PROJ_ID);  
  5.     if(key>0)  
  6.     {     
  7.         return shmget(key,size,flag);  
  8.     }     
  9.     else  
  10.     {     
  11.         perror("ftok");  
  12.         return -1;   
  13.     }     
  14. }  
  15.   
  16. int CreatShmid(int size)  
  17. {  
  18.     return commShmid(size,IPC_CREAT|IPC_EXCL|0666);  
  19. }  
  20. int GetShmid(int size)  
  21. {  
  22.     return commShmid(size,IPC_CREAT);  
  23. }  
  24. int Destory(int shmid)  
  25. {  
  26.     return shmctl(shmid,0,IPC_RMID);  
  27. }  

server.c

  1. #include "comm.h"  
  2. int main()  
  3. {  
  4.     int shmid=CreatShmid(4097);  
  5.     if(shmid>0)  
  6.     {     
  7.         int i=0;  
  8.         char *addr=shmat(shmid,NULL,0);  
  9.         while(i<20)  
  10.         {     
  11.             addr[i++]='A';  
  12.             addr[i]='\0';  
  13.             sleep(1);  
  14.         }     
  15.         if(shmdt(addr)==-1)  
  16.         {     
  17.             perror("shmat");  
  18.             return -3;   
  19.         }     
  20.   
  21.     }     
  22.    else  
  23.     {     
  24.         perror("CreatShmid");  
  25.         return -1;  
  26.     }  
  27.     if(Destory(shmid)<0)  
  28.     {  
  29.         perror("Destory");  
  30.         return -2;  
  31.     }  
  32.     return 0;  
  33. }  
  34.    

client.c

  1. #include "comm.h"  
  2. int main()  
  3. {  
  4.     int shmid=GetShmid(4097);  
  5.     if(shmid>0)  
  6.     {     
  7.         int i=0;  
  8.         char *addr=shmat(shmid,NULL,0);  
  9.         while(i<20)  
  10.         {     
  11.             printf("%s\n",addr);  
  12.             sleep(1);  
  13.             i++;  
  14.         }     
  15.         if(shmdt(addr)==-1)  
  16.         {     
  17.             perror("shmdt");  
  18.             return-1;  
  19.         }     
  20.     }     
  21.     else  
  22.     {     
  23.         perror("GetShmid");  
  24.       return -2;  
  25.     }  
  26.     return 0;  
  27. }  



通过查看共享内存状态可以知道操作系统提供给用户的共享内存为4097个字节,实际上操作系统开辟了8K个字节,而且由于接收方client还没挂接,所以挂接数为1。

运行结果:

运行结果分析:

共享内存中的数据并不会像管道或者信号量等被一端读取之后就不存在。

5.使用共享内存的优缺点

优点:我们可以看到使用共享内存进行进程间的通信真的是非常方便,而且函数的接口也简单,数据的共享还使进程间的数据不用传送,而是直接访问内存,也加快了程序的效率。同时,它也不像匿名管道那样要求通信的进程有一定的父子关系(system V版本共有)。

缺点:共享内存没有提供互斥同步的机制,这使得我们在使用共享内存进行进程间通信时,往往要借助其他的手段比如信号量等来进行进程间的同步工作。
 



其他进程间通信方式详解:

进程间的通信方式——1、pipe(管道)

https://blog.csdn.net/Windgs_YF/article/details/7997380

进程间通信方式——2、、消息队列

https://blog.csdn.net/Windgs_YF/article/details/79974018

进程间通信方式——3、信号量(Semaphore)

https://blog.csdn.net/Windgs_YF/article/details/79974049


阅读更多
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Windgs_YF/article/details/79974077
文章标签: 共享内存
个人分类: pipe 管道 linux 网络
上一篇进程间通信方式———3、信号量(Semaphore)
下一篇B树和二叉排序树(如红黑树)、B树和B+树的区别
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭