进程之间使用共享内存通信
目录
共享内存
共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。
但是它并未提供同步机制,即在某一个进程结束对共享内存的写操作之前,并不能可以阻止第二个进程开始对它进行读取,因此我们需要人为阻止通过其他机制进行同步。
shmget()函数
用于得到一个共享内存标识符或创建一个共享内存。
函数原型:
int shmget(key_t key, size_t size, int shmflg);
参数 | 说明 |
---|---|
key | 标识共享内存的键值,shmget()函数成功时返回一个与key相关的共享内存标识符(非负整数),用于后续的共享内存函数。调用失败返回-1. |
size | 指明共享内存的大小,以字节为单位 |
shmflg | 权限标志 IPC_CREAT 如果共享内存不存在,则创建一个共享内存,否则打开操作。 IPC_EXCL 只有在共享内存不存在的时候,新的共享内存才建立,否则就产生错误。 |
shmat()函数
第一次创建完共享内存后,它还不能被任何进程访问,shmat()
函数的作用就是用来启动对该共享内存的访问,并把共享内存连接到当前进程的地址空间。
原型:
void *shmat(int shmid, const void *shmaddr, int shmflg)
参数 | 说明 |
---|---|
shmid | 共享存储的id,如shmget()函数的返回值 |
shmaddr | 指定共享内存连接到当前进程中的地址位置,一般为0,表示让内核自己决定一个合适的地址位置 |
shmflg | SHM_RDONLY:为只读模式,其他为读写模式,如0 |
调用成功时返回一个指向共享内存第一个字节的指针,如果调用失败返回-1。
shmdt()函数
与shmat
函数相反,是用来断开与共享内存附加点的地址,禁止本进程访问此片共享内存,但是并不是删除该共享内存。
原型:
int shmdt(const void *shmaddr);
shmaddr:连接的共享内存的起始地址,如shmat()
函数的返回值。调用成功的时候返回0,失败时返回-1。
shmctl()函数
控制共享内存。
原型:
int shmctl(int shm_id, int command, struct shmid_ds *buf);
参数 | 说明 |
---|---|
shm_id | 共享内存标识符,如shmget()的返回值 |
command | 一些命令,如 IPC_STAT:得到共享内存的状态,把共享内存的shmid_ds结构复制到buf中 IPC_SET:如果进程有足够的权限,就把共享内存的当前关联值设置为shmid_ds结构中给出的值 IPC_RMID:删除这片共享内存 |
buf | 共享内存管理结构体。 |
shmid_ds结构 至少包括以下成员:
struct shmid_ds
{
uid_t shm_perm.uid;
uid_t shm_perm.gid;
mode_t shm_perm.mode;
};
例如一个简单的例子,父子进程通过共享内存的方式进行通信:
/*
* @Author: YaleXin
* @Date: 2020-04-22 14:22:16
* @LastEditTime: 2020-04-22 21:38:34
* @LastEditors: YaleXin
* @Description:
* @FilePath: \my_c_workspace\others\shareMemory.c
* @祈祷不出现BUG
*/
#include <stdio.h>
#include <string.h>
#include <sys/shm.h>
#include <unistd.h>
struct shareMemoryDate {
int mod;
char msg[100];
};
int main() {
int *shm = NULL;
struct shareMemoryDate *share;
// 创建一个共享内存
// IPC_CREAT表示如果共享内存不存在,则创建一个共享内存,否则打开操作。
// 需要与IPC对象存取权限(如0600)进行|运算来确定信号量集的存取权限
int shm_id =
shmget((key_t)0, sizeof(struct shareMemoryDate), 0600 | IPC_CREAT);
if (shm_id == -1) {
printf("创建共享内存失败,我也不知为什么\n");
return 0;
}
int pId = fork();
if (pId == -1) {
printf("创建子进程失败,我也不知为什么\n");
} else if (pId == 0) {
//子进程
// 将共享内存连接到当前进程的地址空间
shm = shmat(shm_id, 0, 0);
if (shm == NULL) {
printf("链接失败,我也不知为什么\n");
return 0;
}
share = (struct shareMemoryDate *)shm;
char buffer[100];
printf("我是子进程,请输入想要写入共享内存的内容:\n");
fgets(buffer, sizeof(buffer), stdin);
strcpy(share->msg, buffer);
// 表示将数据写入完毕
share->mod = 1;
// 把共享内存从当前进程中分离
if (shmdt(shm) == -1) {
printf("分离失败,我也不知为什么\n");
}
} else if (pId > 0) {
//父进程
// 将共享内存连接到当前进程的地址空间
shm = shmat(shm_id, 0, 0);
if (shm == NULL) {
printf("链接失败,我也不知为什么\n");
// 删除共享内存
if (shmctl(shm_id, IPC_RMID, 0) == -1) {
printf("(无法链接)删除共享内存失败,我也不知为什么\n");
return 0;
}
}
share = (struct shareMemoryDate *)shm;
//等待子进程写入完毕
while (share->mod != 1) sleep(1);
printf("我是父进程,让我看看我的子进程在共享内存里边写了什么东西:\n");
printf("%s\n", share->msg);
printf("噢,原来是这些鬼玩意\n");
// 删除共享内存
if (shmctl(shm_id, IPC_RMID, 0) == -1) {
printf("(读取完毕)删除共享内存失败,我也不知为什么\n");
}
}
return 0;
}
调试:
yalexin@yalexin-PC:/media/yalexin/软件/my_c_workspace/others$ gcc -o share shareMemory.c
yalexin@yalexin-PC:/media/yalexin/软件/my_c_workspace/others$ ./share
我是子进程,请输入想要写入共享内存的内容:
你好啊
我是父进程,让我看看我的子进程在共享内存里边写了什么东西:
你好啊
噢,原来是这些鬼玩意
yalexin@yalexin-PC:/media/yalexin/软件/my_c_workspace/others$
本文由「黄阿信」创作,创作不易,请多支持。
如果您觉得本文写得不错,那就点一下「赞赏」请我喝杯咖啡~
商业转载请联系作者获得授权,非商业转载请附上原文出处及本链接。
关注公众号,获取最新动态!
历史评论
开始评论