进程间通信(三)之共享内存

共享内存是一种高效的进程间通信(IPC)方式,允许多个进程共享同一存储区域,避免了数据复制。使用时需注意同步访问,通常借助信号量实现。 shmget()用于获取共享内存标识,shmat()将内存段连接到地址空间,shmdt()用于断开连接,shmctl()执行包括删除在内的各种操作。连接地址通常由内核选择,确保跨平台兼容性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

上接进程间通信(二)

四、共享内存

共享内存也称共享存储,共享内存就是指允许两个或多个进程共享一定的存储区。因为数据不需要客户进程和服务器进程之间复制,所以说共享内存时最快的一种IPC。使用共享内存时要掌握的唯一要点,就是多个进程之间对一定存储区的同步访问。若服务器进程正在将数据放入共享存储区,则在它做完这一操作之前,客户进程不应该去取这些数据。通常,信号量被用来实现对共享内存的同步访问。(当然记录锁也可用于这种场合)

内核为每个共享存储段设置了一个shmid_ds结构;

struct shmid_ds {
    struct ipc_perm    shm_perm;    //权限控制
    size_t             shm_segsz;   //共享存储段大小(字节为单位)
    pid_t              shm_lpid;    //最后一个调用shmop()函数的进程
    pid_t              shm_cpid;    //创建该段的进程的进程ID
    shmatt_t           shm_nattch;  //当前附加到该段的进程个数
    time_t             shm_atime;   //最后一个进程附加到段的时间
    time_t             shm_dtime;   //最后一个进程离开该段的时间
    time_t             shm_ctime;   //最后一个进程修改该段的时间
    ...
};

(按照支持共享存储段的需要,每种实现会在shmid_ds结构中增加其他成员。)

shmatt_t 类型定义为不带符号整型,它至少与unsigned short一样大。

为了获取一个共享存储标识,调用的第一个函数通常是shmget()。

#include<sys/shm.h>

int shmget(key_t key,size_t size,int flag); //成功返回共享存储的ID,出错返回-1

当创建一个新的共享存储段时,初始化shmid_ds结构的下列成员:

·ipc_perm结构中的mode成员按flag中的相应权限位设置。

·shm_lpid、shm_nattach、shm_stime、以及shm_dtime都设置为0.

`shm_ctime设置为当前时间。

·shm_segsz设置为请求的长度(size)。

参数size是该共享存储段的长度(单位:字节)。实现通常将其向上取为系统页长的整数倍。但是,若应用指定的size值并非系统页长的正数倍,那么最后一页的余下部分是不可能使用的。如果正在创建一个新段(一般在服务器进程中),则必须指定size。如果正在引入一个现存的段(一个客户进程),则将size指定为0。当创建一新段时,段内的内容初始化为0。

shmctl()函数对共享内存段执行多种操作。

#include<sys/shm.h>

int shmctl(int shmid,int cmd,struct shmid_ds *buf);//成功返回0,出错返回-1

cmd参数指定下列5中命令中的一种,使其在shmid指定的段上执行。

IPC_STAT     取此段的shmid_ds结构,并将它存放在由buf指向的结构中。

IPC_SET       按buf指向结构中的值设置与此段相关结构中的下列三个字段:shm_perm.uid、shm_perm.gid以及shm_perm.mode。此命令只能由下列两种进程执行:一种是其有效用户ID等于shm_perm.cuid或shm_perm.uid的进程;另一类是具有超级用户特权的进程。

IPC_RMID     从系统中删除该共享存储段。因为每个共享存储段有一个连接计数(shmid_ds机构中的shm_nattch字段),所以除非使用该段的最后一个进程终止或与该段脱节,否则不会实际上删除该存储段。不管此段是否仍在使用,该段标识符立即被删除,所以不能再使用shmat与该段连接。此命令只能由下列两种进程执行:一种是其有效用户ID等于shm_perm.cuid或shm_perm.uid的进程;另一类是具有超级用户特权的进程。

Linux和Solaris提供了下列两种命令,UNIX中并没有。

SHM_LOCK         将共享存储段锁定在内存中。此命令只能超级用户执行。

SHM_UNLOCK    解锁共享存储段。此命令只能由超级用户执行。

一旦创建了一个共享存储空段,进程就可调用shmat将其连接到它的地址空间中。

#include<sys/shm.h>

void *shmat(int shmid,const void *addr,int flag);//若成功返回该共享存储段的指针,出错返回-1 

共享存储段连接到调用进程的哪个地址与addr参数以及在flag中是否制定SHM_RND位有关。

·如果addr为0,则指定连接到由内核选择的第一个可用地址上。这是推荐的使用方式。

·如果addr非0,并且没有指定SHM_RND,则次段连接到addr指定的地址上。

·如果addr非0,并且指定了SHM_RND,则此段连接到(addr-(addr mod ulus SHMLBA))表示的地址上。SHN_RND命令的意思是“取整”。SHMLBA的意思是“低边界地址倍数”,它总是2的乘方。该算式是将地址向下取最近一个SHMLBA的倍数。

除非只计划在一种硬件上运行应用程序,否则不应该指定共享段所连接到的地址。所以一般应指定addr为0,以便由内核选择地址。

如果在flag中指定了SHM_RDONLY位,则以只读方式连接此段。否则以读写方式连接此段。

shmat()的返回值是该段所连接的实际地址,如果出错返回-1。如果shmat执行成功,那么内核将使该共享存储段shmid_ds结构中的shm_nattch计数器值加1。

当共享存储段的操作已经结束时,则调用shmdt()脱接该段。注意,这并不是从系统中删除其标识符以及其数据结构,该标识符仍然存在,直至某个进程(一般是服务器进程)调用shmctl()(带命令IPC_RMID)特地删除它。

#include<sys/shm.h>

int shmdt(void *addr); //成功返回0,出错返回-1

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

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值