不同进程之间通信,通常可以使用共享内存/消息队列/信息量/管道等方法。
共享内存允许两个或多个进程共享一个给定的存储区,这一段存储区可以被两个或两个以上的进程映射至自身的地址空间中,一个进程写入共享内存的信息,可以被其他使用这个共享内存的进程,通过一个简单的内存读取,从而实现了进程间的通信。
相比于其他几种方法,共享内存的特点在于:是一种最为高效的进程间通信方式,进程可以直接读写内存,而不需要任何数据的拷贝。
共享内存的实现步骤:
①–> 创建/打开共享内存
②–> 映射共享内存,即把指定的共享内存映射到进程的地址空间用于访问
③–> 撤销共享内存映射
④–> 删除共享内存对象
所需头文件 | #include #include #include |
---|---|
函数原型 | int shmget(key_t key, int size, int shmflg); |
函数返回值 | 成功:共享内存段标识符 出错:-1 |
函数参数:
key
IPC_PRIVATE
):会建立新共享内存对象shmflg
来确定操作,通常要求此值来源于ftop()
函数返回的IPC
键值size
shmflg
IPC_CREAT
:当shmflg & IPC_CREAT
为真时,如果内核中不存在键值与key
相等的共享内存,则新建一个共享内存;如果存在这样的共享内存,返回此共享内存的标识符IPC_CREAT | IPC_EXCL
:如果内核中不存在键值与key
相等的共享内存,则新建一个共享内存;如果存在这样的共享内存则报错其中ftok()
函数:
函数原型 | key_t ftok(char *fname, int id); |
---|---|
函数参数 | fname :指定的文件名(该文件必须存在而且可以访问)id :至少8位的项目id,不能为0 |
函数返回值 | 成功:返回一个key_t 值出错:-1 |
ftok
中的参数可以随便填写,但是要符合格式,ftok只是利用参数,再运用一套算法,算出一个唯一的key值返回。这个key值可以传给共享内存参数,作为stuct ipc_perm中唯一标识共享内存的key。
为什么已经有一个
key
来标识共享内存了,还需要一个返回值来标识共享内存?
因为key
是内核级别的,供内核标识,shmget
返回值是用户级别的,供用户使用的。
所需头文件 | #include #include #include |
---|---|
函数原型 | void *shmat(int shmid, const void *shmaddr, int shmflg); |
函数参数 | shmid :要映射的共享内存区标识符shmaddr :将共享内存映射到指定地址(若为NULL,则表示由系统自动完成映射)shmflg :权限,常见的有两个------------- SHM_RDONLY :共享内存只读------------- SHM_REMAP :重新映射一个进程地址空间,这样shmaddr 不能为空-------------默认0:共享内存可读写 |
函数返回值 | 成功:共享内存段标识符 出错:-1 |
所需头文件 | #include #include #include |
---|---|
函数原型 | void shmdt(const void *shmaddr); |
函数参数 | shmaddr :共享内存映射后的地址 |
函数返回值 | 成功:0 出错:-1 |
所需头文件 | #include #include #include |
---|---|
函数原型 | int shmctl(int shmid, int cmd, struct shmid_ds *buf); |
函数参数 | shmid :要操作的共享内存区标识符cmd :要进行的操作--------- IPC_STAT (获取对象属性)--------- IPC_SET (设置对象属性)--------- IPC_RMID (删除对象)buf :指定IPC_STAT /IPC_SET 时用以保存/设置属性 |
函数返回值 | 成功:0 出错:-1 |
IPC
(进程间通信)资源生命周期不随进程,而是随内核的,不释放会一直占用,除非重启。所以,shmget
创建的共享内存一定要利用shmctl()
释放掉,不然会内存泄漏。
#include
#include
#include
#include
#include
int main() {
/**********实现步骤中的第1步**********/
//利用ftok获得一个IPC键值,
key_t key = ftok("/home", 0x666);
if (key == -1) {
perror("ftok error");
return -1;
}
printf("ftok : %d\n", key);
//获取共享内存区域的ID
int shmId = shmget(key, 4096, IPC_CREAT | 0600);
if (shmId == -1) {
perror("shmget error");
return -1;
}
printf("shmget : %d\n", shmId);
/**********实现步骤中的第2步**********/
//连接共享内存
void *shm_addr = shmat(shmId, NULL, 0);
//创建需写入内存的数据并将其写入共享内存区
char data[30] = "test datas";
memcpy(shm_addr, &data, sizeof(data));
printf("Writing is finish !\n");
/**********实现步骤中的第3步**********/
//断开
shmdt(shm_addr);
// 写入程序中不需要释放共享内存(第4步),否则读取程序无法正确读取数据
return 0;
}
#include
#include
#include
#include
#include
#include
int main() {
/**********实现步骤中的第1步**********/
//ftok的第一个参数必须保证和写入程序中一致,最好别写相对路径
key_t key = ftok("/home", 0x666);
if (key == -1) {
perror("ftok error");
return -1;
}
printf("ftok : %d\n", key);
int shmId = shmget(key, 4096, IPC_CREAT | 0600);
if (shmId == -1) {
perror("shmget error");
return -1;
}
printf("shmget : %d\n", shmId);
/**********实现步骤中的第2步**********/
void *shm_addr = shmat(shmId, NULL, 0);
//取出数据
char data[30] = {0};
memcpy(&data, shm_addr, sizeof(data));
std::cout << "the data : " << data << std::endl;
/**********实现步骤中的第3步**********/
//断开
shmdt(shm_addr);
/**********实现步骤中的第4步**********/
//将共享内存释放
int sh = shmctl(shmId, IPC_RMID, NULL);
if (sh == -1) {
perror("shmctl error");
return -1;
}
printf("shmctl is worked.\n");
return 0;
}
如果在通讯结束后,没有使用shmctl
将共享内存释放掉,那么就会造成内存泄漏,这时需要我们手动去释放,操作如下:
# 查看所有IPC设施
$ ipcs -m
------------ 共享内存段 --------------
键 shmid 拥有者 权限 字节 连接数 状态
0x00000000 32800 zy 600 1048576 2 目标
0x00000000 32805 zy 600 524288 2 目标
0x66030001 98347 zy 600 4096 0
0x00000000 32818 zy 600 6897664 2 目标
0x00000000 65599 zy 600 24576 2 目标
# 释放共享内存:ipcrm -m [shimd]
$ ipcrm -m 98347
$ ipcs -m
------------ 共享内存段 --------------
键 shmid 拥有者 权限 字节 连接数 状态
0x00000000 32800 zy 600 1048576 2 目标
0x00000000 32805 zy 600 524288 2 目标
0x00000000 32818 zy 600 6897664 2 目标
0x00000000 65599 zy 600 24576 2 目标
linux 进程通信-共享内存(shared memory)《Rice linux 学习开发》
【C++服务器入门基础------6.IPC进程间通信–共享内存】
进程间通信之共享内存
linux 进程间通信,示例代码。包括共享内存,消息队列,信号量。及用户态和内核态的通信