进程间通信共享内存

1 什么是共享内存                                                              

进程具有独立性,通信的前提就是要保证两份毫不相关的进程看到同一份资源,在物理地址空间开 辟一块空间,这块空间被称之为共享内存,让不同的进程通过某些函数接口访问到这块共享内存,这样 就实现了进程间通信。

目前存在多种共享内存实现方式,其中包括:

   传统 SYSTEM  V 共享内存:早期UNIX系统中提供的共享内存方式,使用系统调用来进行共享内存操 作。它涉及的主要函数包括 shmget  shmat  shmdt  shmctl

     POSIX 共享内存:利用  mmap 系统调用将文件映射到内存中,从而实现共享内存

     merfd_create()接口和 fd跨进程式:使用  memfd_create() 系统调用创建内存文件描述符, 并将其通过文件描述符在多个进程之间共享

   基于 dma_buf 的共享内存:主要应用于多媒体、图形领域,可以实现在不同设备间高效地共享数 

        System V  POSIX 都是  UNIX 系统中的标准,用于规范  UNIX 系统的接口和功能。   System V  AT&T公司发布的 UNIX 系统版本,它包括许多标准化的系统调用和库函数,例如  shmget() 

semget()  msgget() 等,用于实现进程间通信和同步机制。   System V    IPC 机制具有较高的性能 和可定制性,但也较为复杂,使用起来需要一定的经验和技巧。

POSIX  Portable Operating System Interface  的缩写,是一个由  IEEE  组织发布的  UNIX 标准。它规定了操作系统的接口和功能,包括进程管理、文件系统、网络通信、线程、信号等方面。

POSIX 标准的目的是使不同的  UNIX 系统之间具有良好的兼容性,使程序在不同的 UNIX 系统上能够编 译和运行。

POSIX  IPC 机制包括信号量、消息队列、共享内存等,与  System V  IPC 机制类似,但更  加简单和易用。   POSIX 标准还规定了  PthreadsPOSIX threads 线程库,用于实现多线程编程,具 有较高的可移植性和性能。

需要注意的是,   System V  POSIX 是两种不同的标准,它们的  API 和实现方式并不完全相同。

在编写 UNIX 程序时,需要根据具体的需求和环境选择合适的标准和接口。

这里主要介绍一下 SYSTEM V  POSIX 共享内存。

 

共享内存

2.1 SYSTEM  V 共享内存原理

         在Linux中,每个进程都有属于自己的进程控制块  PCB 和地址空间 Addr Space  ,并且都有一个与 之对应的页表,负责将进程的虚拟地址与物理地址进行映射,通过内存管理单元 MMU 进行管理。两个不 同的虚拟地址通过页表映射到物理空间的同一区域,它们所指向的这块区域即共享内存。

当一个进程往该空间写入内容时,另外一进程访问该空间,会得到写入的值,即实现了进程间的通 信。

在使用共享内存通信的时候,没有使用任何接口。双方进程在通信时,是直接对内存进行访问、操 作,属于内存级的读和写。因此在通信的时候,可以减少拷贝次数,不需要和内核进行交互,因此共享 内存是所有进程间通信最快的

注:关于页表的补充:

进程地址空间里有一个内核区域,它们也会在实际物理内存开辟空间,也会有页表与那块空间形成 映射关系,这个页表叫做内核级页表。因为内核只有一个,所以每个进程都相同的。说明进程都共用实 际物理内存上的内核空间。

除内核空间以外的空间,与实际物理空间之间的页表,称为用户级页表。每个进程可能不同。

 2.2  SYSTEM V共享内存存在的缺陷

共享内存并未提供同步机制。也就是说,共享内存需要用户去通过某种同步技术,比如信号量、互 斥量或某种其他允许进程按照某种顺序访问资源的方式,来避免冲突和数据不一致的情况。

共享内存本身没有内建的引用计数机制。引用计数是一种资源管理技术,用于跟踪一个资源(例如 共享内存)被多少个进程或对象引用。当引用计数为零时,资源可以被安全地释放,从而避免资源 泄漏。也就是说,在使用共享内存时,需要开发者自行管理对共享内存的连接和断开,以确保正确 地使用共享内存资源。通常,创建共享内存的进程或对象需要记录连接的进程数或对象数,并在不

再需要共享内存时,适时地断开连接或释放共享内存。

2.3  SYSTEM  V 共享内存接口

·   ftok 函数:算出一个唯一的key返回

 

#include <sys/types.h>

#include <sys/ipc.h>

/**

* @brief: convert a pathname and a project identifier to a System V IPC key

* @param[in] pathname: 地址

* @param[in] proj_id: 至少8为的项目id

* @return key_t: 如果成功返回一个key值,如果失败返回-1 */

key_t ftok(const char *pathnameint proj_id);

shmget 函数:创建一个共享内存

#include <sys/types.h>

#include <sys/ipc.h>

/**

* @brief: 创建或获取一个共享内存区

* @param[in] key: 为共享内存的名字,一般是ftok的返回值

* @param[in] size: 共享内存的大小,以page为单位,大小为4096的整数倍

* @param[in] shmflg: 权限标志,常用两个,IPC_CREATIPC_EXCL,一般后面还加一个权

限,相当于文件的权限,例如:IPC_CREAT | IPC_EXCL | 0666

* @param[in] IPC_CREAT: 创建一个共享内存返回,已存在打开返回

* @param[in] IPC_EXCL: 配合着IPC_CREAT使用,共享内存已存在则出错返回

* @return int: 共享内存区的标识符 */

int shmget(key_t keysize_t sizeint shmflg);

   1 key  是内核级别的,供内核标识,   shmget()  返回值是用户级别的,供用户使用

   2 IPC  资源生命周期不随进程,而是随内核的,不释放会一直占用,除非重启

   3:可以使用  ipcs  -m  查看SYSTEM V共享内存

 

   4 shmget 创建的共享内存要释放掉,不然会内存泄漏

   5:可以用命令行来释放共享内存:   ipcrm  -m  shmid(shmget()返回值)  ,但是如果挂接  进程数不为0时,并不会立即释放共享内存,而是先设置删除状态  dest 直到挂接进程数为 0时才真正释放共享内存

  

shmat 函数 使创建的共享内存与调用该函数进程的进程地址空间参数关联

#include <sys/shm.h>

/**

* @brief: 将共享内存段映射到调用进程的地址空间

* @param[in] shmid: shmget()函数返回的共享内存标识符

* @param[in] shmaddr: 指定映射地址的指针,如果为NULL,则由系统自动选择映射地址

* @param[in] shmflg: 标志参数,用于指定映射方式和权限

* @return void*: 返回指向共享内存段的指针,如果出错则返回(void *)-1 */

void *shmat(int shmidconst void *shmaddrint shmflg);

   shmflg标志参数:

   SHM_RDONLY :只读方式映射共享内存段

   SHM_RND :将映射地址舍入为  SHMLBA 的倍数

SHMLBA 是一个常量,表示共享内存段的基本长度。它是一个系统级别的常量,在 Linux 系统中通常定义为 4096 (即 4KB),具体取决于系统架构和配置。

   SHM_EXEC :允许在映射地址上执行程序代码

   0 :系统默认

   注:在使用完共享内存段后,应使用 shmdt()  函数将其从调用进程的地址空间中分离

     shmdt 函数:删除共享内存与进程地址空间的映射关系,将页表映射关系删除,释放进程地址空间

#include <sys/shm.h>

/**

* @brief: 将共享内存段从调用进程的地址空间中分离

* @param[in] shmaddr: 共享内存映射到进程地址空间的地址,是shmat()返回值

* @return int: 成功返回0,失败返回-1 */

int shmdt(const void *shmaddr);

   1:在使用共享内存时,每个进程都应该在使用完共享内存后使用  shmdt()  函数将其分

离,以释放系统资源。如果一个进程崩溃或退出而没有调用 shmdt()  函数,内核会自动将共 享内存段从该进程的地址空间中分离。但是,这样会导致共享内存的资源泄漏,因此应该尽可 能避免这种情况的发生。

   2:将共享内存段与当前进程脱离不等于删除共享内存段

     shmctl 函数:控制共享内存段的各种属性和操作

 

   cmd命令:

   IPC_STAT :获取共享内存段的状态信息,将其存储在  buf 指向 shmid_ds 结构体 中

   IPC_SET :设置共享内存段的状态信息,使用  buf 指向 shmid_ds 结构体中的值

   IPC_RMID :删除共享内存段,释放其占用的系统资源。

注意:

1. 这里只是将共享内存标记为删除状态,并不是直接删除。

2. 当共享内存被标记为删除状态之后,并不会马上被删除,直到所有的进程全部和共享 内存解除关联,共享内存才会被删除。

上面两条其实是和使用 ipcrm -m shmid 命令一样的。

3. 因为通过  shmctl() 函数只是能够标记删除共享内存,所以在程序中多次调用该操作 是没有关系的

2.4 关键点

前面提到过,  共享内存并未提供同步机制,因此需要应用自己去实现同步机制。在这里,我使用的 是互斥锁 mutex 。这里共享内存的结构示意图如下:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值