Linux下的进程通信方式之System_V共享内存

Linux下的通信方式之 System_V 共享内存

共享内存是 IPC 三种通信方式中,性能最好的一种。其允许两个或以上的进程通过访问一块公共的内存空间(通常称为段)来达到通信的方式。

由于一个共享内存段会成为一个进程的用户空间内存的一部分,所以这种通信方式是无需内核参与的。这也是共享内存这种通信方式和消息队列、信号量的巨大不同。后两者是需要将数据从用户空间复制到内核空间的。

另一方面,共享内存这种 IPC机制不由内核控制意味着通常需要通过某些同步方法使得进程不会出现同时访问共享内存的情况(如两个进程同时执行更新操作或者一个进程在从共享内存中获取数据的同时另一个进程正在更新这些数据)。System V信号量天生就是用来完成这种同步的一种方法。当然,还可以使用其他方法,如 POSIX信号量和文件锁。(没有同步机制是共享内存的一个缺陷)

概述

为使用一个共享内存段通常需要执行下面的步骤:

● 调用 shmget()创建一个新共享内存段或取得一个既有共享内存段的标识符(即由其他进程创建的共享内存段)。这个调用将返回后续调用中需要用到的共享内存标识符。

● 使用 shmat()来附上共享内存段,即使该段成为调用进程的虚拟内存的一部分。

● 此刻在程序中可以像对待其他可用内存那样对待这个共享内存段。为引用这块共享内存,程序需要使用由 shmat()调用返回的 addr值,它是一个指向进程的虚拟地址空间中该共享内存段的起点的指针。

● 调用 shmdt()来分离共享内存段。在这个调用之后,进程就无法再引用这块共享内存了。这一步是可选的,并且在进程终止时会自动完成这一步。

● 调用 shmctl()来删除共享内存段。只有当当前所有附加内存段的进程都与之分离之后内存段才会被销毁。只有一个进程需要执行这一步。

相关函数

shmget()

1
2
3
4
5
6
7
8
#include <sys/shm.h>
#include <sys/types>

int shmget(key_t key, int size, int shmflg)
第一个参数key:有效地为共享内存段命名
第二个参数size:以字节为单位制定需要共享的内存容量。(实现通常将其向上取为系统页长的整数倍。但是,若应用指定的size值并非系统页长的整数倍,那么最后最后一页余下部分不可使用。)
第三个参数shmflg:权限标志
返回值: 成功:非负整数即共享内存标识符; 失败:-1

作用:创建新的共享内存或引用一个现有的共享内存。当创建一个新的共享内存时,初始化shmid_ds结构下列成员。

ipc_perm结构。其中mode按shmflg中的相应权限位设置

shm_lpid、shm_nattach、shm_atime和shm_dtime都设置为0.

shm_ctime设置为当前时间

shm_segsz设置为请求的size

如果创建一个新的共享内存段(通常在服务器进程中),则必须指定其size。如果正在引用一个现存的段(一个客户进程),则将size指定为0。当创建一个新共享内存时,段内的内容初始化为0.

shmat()

1
2
3
4
5
6
7
8
9
#include <sys/shm.h>

char *shmat(int shmid, const void*shmaddr,int shmflg)
第一个参数shmid:共享内存标识符
第二个参数shmaddr:将共享内存映射到指定位置  如果shmaddr为0,则此段连接到由内核选择的第一个可用地址上。(这是推荐的使用方式)  如果shmaddr非0,并且没有指定SHM_RND,则此段连接到shmaddr所指定的地址上。  如果shmaddr非0,并且指定了SHM_RND,则此段连接到(shmaddr—(shmaddr mod SHMLBA))所表示的地方。  SHM_RND命令的意思是“取整”。SHMLBA的意思是“低边界地址倍数”(了解以下。现在基本使用这一种方式)
第三个地址:SHM_RND(这个标志与shm_addr联合使用,用来控制共享内存连接的地址)
      SHM_RDONLY(使它连接的内存只读)
      默认0:共享内存可读可写
返回值:返回一个指向共享内存第一个字节的指针; 失败:-1

作用:第一次创建共享内存时,它不能被任何进程访问。要想启用该共享内存的访问,必须将其连接到一个进程的地址空间中。

shmdt()

1
2
3
4
5
6
#include <sys/shm.h>

int shmdt(const void *shmaddr)
第一个参数shmaddr:
shmaddr:被映射的共享内存段地址,在调用shmat时的返回值。
返回值: 成功:0; 失败:-1

作用:将共享内存从当前进程中分离(并不是删除)。 如果调用成功,shmdt将使相关shmid_ds结构中的shm_nattch计数器值减1。如果想要删除这个共享内存则需要使用函数 shmctl()

shmctl()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <sys/shm.h>

int shmctl(int shm_id, int command, struct shmid_ds *buf);
第一个参数shm_id:shmget返回的共享内存标识符
第二个参数command:要采取的动作,可以取三个值
IPC_STAT:把shmid_ds结构中的数据设置为共享内存的当前关联值
IPC_SET:如果进程有足够的权限,就把共享内存的当前关联值设置为shmid_ds结构中给出的值
IPC_RMID:删除共享内存段
第三个参数buf:是一个指针,它指向包含共享内存模式和访问权限的结构
返回值:成功:0;失败:-1
struct shmid_ds{
  uid_t shm_perm.uid,
  uid_t shm_perm.gid;
  mode_t shm_perm.mode;
};

Linux下的进程通信方式之System_V共享内存
https://ysc2.github.io/ysc2.github.io/2024/08/17/Linux下的进程通信方式之System_V共享内存/
作者
Ysc
发布于
2024年8月17日
许可协议