IPC通信之共享内存

共享内存

什么是共享内存?

共享内存就是使得多个进程可以访问同一块内存空间,是最快的可用IPC形式。是针对其他通信机制运行效率较低而设计的。往往与其它通信机制,如信号量结合使用,来达到进程间的同步及互斥。

我们通过一张图来表示这个关系

IPC通信之共享内存

共享内存和消息队列,信号量一样都属于XSI IPC。内核都为他们维护了一套数据结构

IPC通信之共享内存

同样,系统还是会维护一个key用来标识,我们还是通过ftok()函数来得到这个key。

IPC通信之共享内存

其中pathname是路径名。一般设为当前路径".";proj_id则是项目id。

共享内存的操作函数

1.shmget()获取共享内存

IPC通信之共享内存

返回值:成功返回共享存储区的shmid,若失败,返回-1。 

参数key即为我们调用ftok函数得到的标识符,作为系统对此共享存储资源的唯一标识。 

参数size是要申请的共享存储区的大小,以字节为单位,这个数一般为系统页长的整数倍(4K的整数倍)。若申请的size不是页长的整数倍,系统也是按整数倍进行分配的,但是最后一页余下的部分不可用。如果创建一个新的资源,则必须指定size,且段的内容会被初始化为0。若引用一个已经存在的,则将size置为0。 

参数flag与消息队列相同,有两个选项,IPC_CREAT和IPC_EXCL。使用的时候有两种情况: 

① IPC_CREAT和IPC_EXCL一起使用(IPC_CREAT|IPC_EXCL),表示申请创建一个新的IPC资源,若要申请的资源已经存在,则错误返回。若不存在,则创建。 

② IPC_CREAT单独使用,表示申请创建一个IPC资源,若要申请的IPC资源已经存在,则直接使用;若不存在,则创建新的。 一般我们还会在后面加上资源的默认权限(如0666)。

2.shmctl()删除共享内存

IPC通信之共享内存

返回值:成功返回0,失败返回-1。 

参数shmid即为我们调用shmget得到的共享存储的id。 

参数cmd指定了要执行了命令,当cmd被设置为IPC_RMID时,该函数执行删除动作。此时,第三个参数设为NULL。 

除此之外,cmd还有IPC_STAT,IPC_SET等命令。

3.shmat()将进程挂到共享内存上

IPC通信之共享内存

返回值:若成功,返回指向共享内存存储的指针,若失败,返回-1。 

shmid不再多说,attr参数决定了共享存储段连接到调用进程的那个地址。一般我们设置为NULL或0,表示此段连接到由内核选择的第一个可用地址上。这是推荐的使用方式。 

如果参数flag指定了SHM_RDONLY,则以只读方式连接,否则以读写的方式连接。 

如果此函数成功执行的话,那么内核将使与该共享存储段相关的shmid_ds结构中的shm_nattch计数器值加1。

4.shmdt()将进程从共享内存上移除

IPC通信之共享内存

返回值:成功返回0,失败返回-1。 

参数addr是之前调用shmat的返回值。如果函数执行成功的话,shmdt将使相关shmid_ds结构中的shm_nattch计数器减1。 

当使用完共享存储后,调用此函数令进程与它分离,但共享存储并不会消失,其标识符仍然还在,直到有进程调用shmctl将其删除。

模拟实现共享内存

头文件

IPC通信之共享内存

源文件

IPC通信之共享内存

客户机client.c

IPC通信之共享内存

服务器server.c

IPC通信之共享内存

运行client和server之后,我们会发现

IPC通信之共享内存

IPC通信之共享内存

然后我们通过监视去看

IPC通信之共享内存

我们发现这个共享内存的shmid为688144,挂载了两个进程nattch为2。