System V共享内存

本文详细介绍了System V共享内存的概念,包括它的优势、创建与打开、使用、分离以及控制。强调了共享内存作为进程间通信快速手段的特性,以及在使用过程中需要注意的同步问题。还讲解了如何通过shmat和shmdt函数进行共享内存的attach和分离操作,以及shmctl函数在控制共享内存中的作用。
摘要由CSDN通过智能技术生成

1. 共享内存概述

共享内存是所有IPC手段中最快的一种。它之所以快是因为共享内存一旦映射到进程的地址空间,进程之间数据的传递就不需要涉及内核了。

管道、FIFO和消息队列,任意两个进程之间想要交换信息,都必须通过内核,内核在其中发挥了中转站的作用:

  • 发送信息的一方,通过系统调用(write或msgsnd)将信息从用户层拷贝到内核层,由内核暂存这部分信息。

  • 提取信息的一方,通过系统调用(read或msgrcv)将信息从内核层提取到应用层。

总结就是:在一个通信周期内,上述过程至少牵扯到两次内存拷贝(从用户拷贝到内核空间和从内核空间拷贝到用户空间)和两次系统调用,这其中的开销不容小觑。

内核提出了一个新的思路:共享内存,内核负责构建出一片内存区域,两个或多个进程可以将这块内存区域映射到自己的虚拟地址空间,直接通过共享内存的数据交换来进行通信。

进程从此就像操作普通进程的地址空间一样操作这块共享内存,一个进程可以将信息写入这块内存区域,而另一个进程也可以看到共享内存里面的信息以及修改信息,从而达到通信的目的。

允许多个进程同时操作共享内存,就不得不防范竞争条件的出现,比如有两个进程同时执行更新操作,或者一个进程在执行读取操作时,另一个进程正在执行更新操作。因此,共享内存这种进程间通信的手段通常不会单独出现,总是和信号量、文件锁等同步手段配合使用。

2. 创建或打开共享内存

shmget函数负责创建或打开共享内存段,其接口定义如下:

#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, size_t size, int shmflg);

其中第二个参数size必须是正整数,表示要创建的共享内存的大小。内核以页面大小的整数倍来分配共享内存,因此,实际size会被向上取整为页面大小的整数倍。

第三个参数支持IPC_CREAT和IPC_EXCL标志位。如果没有设置IPC_CREAT标志位,那么第二个参数size对共享内存段并无实际意义,但是必须小于或等于共享内存的大小,否则会有EINVAL错误。

和消息队列及信号量一样,对于创建共享内存,系统也存在一些限制。

3. 使用共享内存

shmget函数,不过是茫茫内存中创建了或找到了一块共享内存区域,但是这块内存和进程尚没有任何关系。要想使用该共享内存,必须先把共享内存映射到进程的地址空间,这就是attach操作。

attach操作的接口定义如下:

#include <sys/types.h>
#include <sys/shm.h>
void *shmat(int shmid, const void *shmaddr, int shmflg);

其中,第二个参数是用来指定将共享内存放到虚拟地址空间的什么位置的。把第二个参数设置为NULL,表示用户并不在意,一切交由内核做主。

当shmaddr的地址不是NULL的时候,表示进程希望将共享内存attach到改地址。但是改地址必须是系统分页的整数倍,否则会返回EINVAL错误。内核提供了一个shmflg为SHM_RND,表示该地址不是系统分页的整数倍也没关系,系统就会在用户给出的地址附近,就近找一个系统分页整数倍的地址。

如果指定的shmaddr落在已经用的地址范围内,就会导致EINVAL错误。

shmat如果调用成功,则返回进程虚拟地址内的一个地址。如果失败,就会返回(void*)-1,并且设置errno。

**如何通过shmat返回的地址来使用共享内存?**答案是像使用malloc一样来使用共享内存。我们都使用过malloc,调用malloc时,会指定分配空间的大小,malloc成功后,就可以正常的使用返回的地址。

使用共享内存和使用malloc分配的空间还是有区别的。共享内存段用于多个进程间的通信,因此,写入共享内存的内容要事先约定好,读取进程才可以正常地解析写入进程写入的内容。malloc分配的内存区域完全归调用进程所有,其他进程不可见,但共享内存则不然,其他进程也可能会同时操作该共享内存,因此使用者要有进程间同步的觉悟。

**共享内存和System V消息队列及System V信号量由不同之处,共享内存维护了attach该共享内存的进程个数,见下面输出的nattach列:

ipcs -m
------ Shared Memory Segments --------
key shmid owner perms bytes nattch status
0x07021999 196608 root 644 1712 2

存在引用计数,那么共享内存的删除和消息队列及信号量的删除是不同的。

正是因为attach操作会影响删除的行为,因此,使用共享内存的进程如果确认不再使用了,应该及时地将共享内存分离,使其离开进程的地址空间,这就是分离操作。分离会使共享内存的引用计数减一。

通过fork函数创建的子进程,会继承父进程attach的共享内存。因此在fork之前创建共享内存,后面父子进程就可以使用这块共享内存进行通信了。

4. 分离共享内存

分离操作的接口定义如下:

#include <sys/types.h>
#include <sys/shm.h>
int shmdt(const void *shmaddr);

shmdt函数仅仅是使进程和共享内存脱离关系,并未删除共享内存。shmdt函数的作用是将共享内存的引用计数减1.如前所述,只有共享内存的引用计数为0时,调用shmctl函数的IPC_RMID命令才会真正的删除共享内存。

进程执行exec之后,所有attach的共享内存都会被分离。当进程终止之后,共享内存也会自动被分离。

5. 控制共享内存

shmctl函数用来控制共享内存,函数接口定义如下:

#include <sys/ipc.h>
#include <sys/shm.h>
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值