Linux进程间通信--共享内存
共享内存(Shared Memory)
共享内存就是允许多个不相关的进程访问同一个逻辑内存。 共享内存是在两个正在运行的进程之间共享和传递数据的一种非常有效的方式。不同进程之间共享的内存通常安排为同一段物理内存。进程可以将同一段共享内存连接到它们自己的地址空间中,所有进程都可以访问共享内存中的地址。 而如果某个进程向共享内存写入数据,所做的改动将立即影响到可以访问同一段共享内存的任何其他进程。

共享内存是最快的进程间通信方式。但共享内存并没有提供同步互斥机制, 也就是说:当一个进程对共享内存的写操作结束前,并没有机制自动阻止另一个进程去读或写这块共享内存,这样就可能造成混乱。所以通常利用其他机制帮助共享内存实现同步,比如利用信号量机制。
1.通信原理
在Linux中,每个进程都有属于自己的进程控制块(PCB)和地址空间,并且都有一个与之对应的页表,负责将进程的虚拟地址与物理地址进行映射,通过内存管理单元进行管理。两个不同的虚拟地址通过页表映射到物理空间的同一区域,它们所指向的这块区域即共享内存。

当两个进程通过页表将虚拟地址映射到物理地址时,在物理地址中有一块共同的内存区,即共享内存,这块内存可以被两个进程同时看到。这样当一个进程修改共享内存的数据时,另一个进程访问共享内存时就会得到新的数据。
2.为什么共享内存通信最快?
因为共享内存是利用不同进程映射到同一块物理内存,不同进程对于共享内存的访问都是通过虚拟地址直接访问,没有涉及拷贝或复制问题, 而其他通信机制利用内核中的缓冲区,这就涉及到用户态和内核态之间的数据拷贝问题,拖慢了效率。所以共享内存最快。
3.特征&优缺点
特征:
- 并无自带同步与互斥, 所以不安全, 需要用户自己控制。如:和信号量结合使用来达到进程间的同步及互斥
- 生命周期随内核(共享内存不会随进程退出而销毁, 除非手动调用函数销毁)
优点 :
- 传输速度快、效率高是最大的优点;
- 共享内存对数据类型没有限制(不算优点的优点);
- 通信方便 , 接口简单
缺点 :
- 没有自带的同步互斥机制, 需要和信号量机制搭配使用, 让信号量来完成共享内存的同步互斥控制
4.共享内存的使用
ipcs //查看所有System V IPC
选项 :
- -m 查看共享队列
- -q 查看消息队列
- -s 查看信号量

ipcrm 选项 id // 删除进程间通信资源
选项:
- -m 删除共享队列
- -q 删除消息队列
- -s 删除信号量
5.共享内存操作流程
- 调用函数shmget()创建一个新共享内存段或者取得一个既有的共享内存段的标识符;
- 调用函数shmat()将共享内存附加到进程的虚拟地址空间中;
- 为了引用共享内存,程序需要使用由shmat()函数返回的addr值,它是一个指向进程的虚拟地址空间中该共享内存段起点的指针;
- 调用函数shmdt()分离共享内存段,调用之后,进程无法再引用这段共享内存。
- 调用函数shmctl()删除共享内存段。只有一个进程需要执行这一步。
共享内存在映射到各个进程的虚拟地址空间时,采用引用计数,映射一个进程的虚拟地址空间则加1,解除与一个进程的连接时则减1。 所以,当删除共享内存时,如果引用计数不为0,则不会马上删除,而是将这块共享内存的key值设置为0,表示这块共享内存不会再与任何进程发生连接(映射)。当这块共享内存的引用计数为0时,则释放。
6.操作共享内存的函数
创建共享内存:shmget()函数
#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, size_t size, int shmflg);
- 功能:创建共享内存或者取得一个既有的共享内存的标识符
- 参数:
key: 程序需要提供一个参数key(非0整数),它有效地为共享内存段命名
key是标识共享内存的键值, (在IPC的通信模式下,不管是使用消息队列还是共享内存,甚至是信号量,每个IPC的对象都有唯一的名字,称为“键”)通过“键”,进程能够识别所用的对象。“键”与IPC对象的关系就如同文件名称之于文件,通过文件名,进程能够读写文件内的数据,甚至多个进程能够共用一个文件。而在IPC的通讯模式下,通过“键”的使用也使得一个IPC对象能为多个进程所共用。
size: 以字节为单位指定需要共享的内存容量,如果正在创建一个新段,则必须指定size。如果正在引用一个现存的段,则size指定为0.当创建一个新段,段内的内容初始化为0;
shmflg: 权限标志,它的作用与open函数的mode参数一样,如果要想在key标识的共享内存不存在时,创建它的话,可以与IPC_CREAT做或操作。共享内存的权限标志与文件的读写权限一样,举例:0644,它表示允许一个进程创建的共享内存被内存创建者所拥有的进程向共享内存读取和写入数据,同时其他用户创建的进程只能读取共享内存。
- 返回值:成功返回与key相关的共享内存标识符(非负整数);失败返回-1
将共享内存映射到地址空间:shmat()函数
#include <sys/types.h>
#include <sys/shm.h>
void *shmat(int shm_id, const void *shm_addr, int shmflg);
- 功能:开始对该共享内存的访问,并把共享内存附加到当前进程的地址空间
- 参数:
shmid: 共享内存标识(shmget()成功时返回的操作句柄)
shmaddr: 指定共享内存连接到当前进程中的地址位置,通常为空,表示让系统来选择共享内存的地址;
shmflg: 一组标志位,通常为0。可能取值是SHM_RND(可读可写)和SHM_RDONLY(只读)
- 返回值:成功返回一个指向共享内存第一个字节的指针;失败返回-1
注意:
- shmaddr为NULL,核心自动选择一个地址
- shmaddr不为NULL且shmflg无SHM_RND标记,则以shmaddr为连接地址
- shmaddr不为NULL且shmflg设置了SHM_RND标记,则连接的地址会自动向下调整为SHMLBA的整数倍, 公式:shmaddr - (shmaddr % SHMLBA)
- shmflg=SHM_RDONLY,表示连接操作用来只读共享内存
解除内存映射:shmdt()函数
#include <sys/types.h>
#include <sys/shm.h>
int shmdt(const void *shmaddr);
- 功能:将共享内存从当前进程中分离。注意:将共享内存分离并不是删除它,只是使该共享内存对当前进程不再可用
- 参数 :
shmaddr: shmat函数返回的地址指针。
- 返回值:成功返回0;失败返回-1
控制共享内存:shmctl()函数
#include <sys/shm.h>
int shmctl(int shm_id, int command, struct shmid_ds *buf);
- 功能:用于控制共享内存
- 参数 :
shmid: shmget函数返回的共享内存标识符;
cmd: 将要采取的动作,有三个可取值:IPC_STAT、IPC_SET、IPC_RMID
buf: 一个结构体指针,指向共享内存模式和访问权限的结构体
- 返回值:成功返回0;失败返回-1
struct shmid_ds
{
uid_t shm_perm.uid;
uid_t shm_perm.gid;
mode_t shm_perm.mode;
};
| 可选值 | 说明 |
|---|---|
| IPC_STAT | 把shmid_ds结构中的关联数据设置为共享内存的当前关联值,即用共享内存的当前关联值覆盖shmid_ds的值 |
| IPC_SET | 如果进程有足够的权限,就把共享内存的当前关联值设置为shmid_ds结构中给出的值 |
| IPC_RMID | 删除共享内存 |
ftok()函数指定key值
系统建立IPC通讯(如消息队列、共享内存时)必须指定一个id,通常情况下,该id通过ftok函数得到。
ftok函数是根据pathname和id来创建一个关键字(类型为 key_t),此关键字在创建信号量,创建消息队列的时候都需要使用。
key_t ftok(const char *pathname, int proj_id);
- 参数 :
pathname : 必须存在的路径或文件
proj_id : 子序号, 虽然是int型, 但只使用后8位, 所以是一个8bit的整数, 范围是0~255
- 返回值 :当函数执行成功,则会返回key值, 否则返回-1
7.共享内存实现
sharedMemory.h
#ifndef _COMM_H_
#define _COMM_H_
#include<stdio.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<unistd.h>
#define PATHNAME "."
#define PROJ_ID 0x6666
int createShm(int size);
int destroyShm(int shmid);
int getShm(int size);
#endif
sharedMemory.c
#include"sharedMemory.h"
static int commShm(int size, int flags){
key_t key = ftok(PATHNAME, PROJ_ID);
if(key < 0){
perror("ftok");
return -1;
}
int shmid = shmget(key, size, flags);
if(shmid < 0){
perror("shmget");
return -2;
}
return shmid;
}
int destroyShm(int shmid){
if(shmctl(shmid, IPC_RMID, NULL) < 0){
perror("shmctl");
return -1;
}
return 0;
}
int createShm(int size){
return commShm(size, IPC_CREAT | IPC_EXCL | 0666);
}
int getShm(int size){
return commShm(size, IPC_CREAT);
}
MemoryWrite.c
#include"sharedMemory.h"
int main(){
int shmid = getShm(4096);
sleep(1);
char* addr = shmat(shmid, NULL, 0);
sleep(2);
int i = 0;
while(i < 26){
addr[i] = '0' + i;
++i;
sleep(1);
}
shmdt(addr);
sleep(2);
destroyShm(shmid);
return 0;
}
MemoryRead.c
#include"comm.h"
int main(){
int shmid = createShm(4096);
char *addr = shmat(shmid, NULL, 0);
sleep(2);
int i = 0;
while(i++ < 26){
printf("Memory# %s\n", addr);
sleep(1);
}
shmdt(addr);
return 0;
}


共享内存是Linux进程间通信最快的方式,允许多个进程访问同一块逻辑内存。虽然没有自带同步互斥机制,但可以与其他机制如信号量结合使用以确保安全。通过 shmget(), shmat(), shmdt(), shmctl() 等函数实现共享内存的创建、映射、解除映射和控制。使用 ipcs 和 ipcrm 命令进行查看和删除。"
111460604,6643997,深入理解pytest:Python测试框架解析与实战,"['测试框架', 'Python开发', '自动化测试', 'pytest插件', '测试工程师']
2888

被折叠的 条评论
为什么被折叠?



