Linux操作系统~基于systemV共享内存的进程间通信
创始人
2024-03-17 20:33:36

目录

一.进程间通信有哪些方式

二.什么是systemV

三.共享内存-双向通信-大致实现思路

四.4个函数about共享内存

1.shmget函数-创建

 ftok函数

​编辑

 e.g.

ipcs/ipcrm指令(ipc资源会被回收吗)

2.shmctl函数-删除/释放

3.shmat函数-挂接

4.shmdt函数-去挂接

5.e.g.

五.总结/共享内存的特性

Q:为什么共享内存的大小建议是4KB整数倍?

六.共享内存的内核数据结构

Q:为什么我们看到的shmid是0,1,2,3,4递增的呢?


一.进程间通信有哪些方式

首先我们先来看一下进程间通信总共有哪些方式?

管道

  1. 匿名管道pipe
  2. 命名管道

System V IPC

  1. System V 消息队列
  2. System V 共享内存
  3. System V 信号量

POSIX IPC

  1. 消息队列
  2. 共享内存
  3. 信号量
  4. 互斥量
  5. 条件变量
  6. 读写锁

这里我讨论的是基于systemV共享内存的进程间通信


二.什么是systemV

        命名管道和匿名管道都是基于文件的通信方式,还有systemV标准的通信方式:

  1. 在OS层面专门为进程间通信设计的一个方案
  2. OS不相信任何用户,给用户提供功能的时候,采用系统调用
  3. System V进程间通信,一定会存在专门用来通信的接口(system call)

在同一主机内的进程间通信方案——system V方案

注意进程间通信的本质:让不同进程看到同一份资源


三.共享内存-双向通信-大致实现思路

1.通过某种调用,在内存中创建一份内存空间

2.通过某种调用,让进程(参与通信的多个进程)“挂接”到这份新开辟的内存空间上(挂接就是通过页表映射到这片物理空间)-让不同的进程看到了同一份资源

3.不用共享内存后,我们需要去关联(去掉页表中的映射)

4.释放共享内存


四.4个函数about共享内存

1.shmget函数-创建

用于创建一个共享内存

  • key:用来唯一表示这个共享内存块(就像路径+文件名),需要用户自己设定。我们可以调用ftok函数,将自定义路径名和项目id,经过算法转换成一个唯一的key,这个key会设置到管理shm共享内存的数据结构中(注意这里的key和shmid是不一样的)。

注:

key_id实际上是一个有符号整数

key只是用来在系统层面进行标识唯一性的,不能用来管理shm

shmid是OS给用户返回的id,用来在用户层面对shm进行管理

        如果硬是要类比的话,shmid就像是文件描述符,key就像是struct_file的地址,OS是使用shmid对struct_file进行管理的

  • size:共享内存大小,建议是4KB的整数倍
  • shmflg:由九个权限标志构成,它们的用法和创建文件时使用的mode模式标志是一样的
  • 返回值:shmid,管理当前共享内存的id;成功返回一个非负整数,即该共享内存段的标识码;失败返回-1

这里常用两个权限标志:

  1. IPC_CREATE:不存在则创建:如果单独使用IPC_CREAT,或者flg为0:创建一个共享内存,如果创建的共享内存已经存在,则直接返回当前已经存在的共享内存。(基本不会空手而归)
  2. IPC_EXCL(单独使用没有意义)

 ftok函数

pathname:路径名

proj_id:自定义项目名ID

        路径名+项目名ID应该是唯一的,通过这两个参数生成的key也就是唯一的了,用于唯一标识共享内存块


 e.g.

1.生成同一个key

我们在server和client中使用同一个获取key值的方法ftok(并且传入相同的路径和项目ID),这样也就保证了两个不同的进程可以看到同一份资源。

int main()
{key_t key = ftok(PATH_NAME, PROJ_ID);if(key < 0){perror("ftok");return 1;}printf("%u\n", key);return 0;
}

2.创建一个共享内存

int shmid = shmget(key, SIZE, IPC_CREAT|IPC_EXCL|0666); //创建全新的shm,如果和系统已经存在ID冲突,我们出错返回,这个0666表示共享内存的权限

if(shmid < 0){perror("shmget");return 2;
}

ipcs/ipcrm指令(ipc资源会被回收吗)

ipcs可以看到systemV进程通信设施状态的指令(信号量,共享内存,消息队列都属于ipc资源)

  1. ipcs -s可以显示信号量的状态
  2. ipcs -m显示共享内存的状态
  3. ipcs -q显示消息队列的状态

        systemV的IPC资源,生命周期是随内核的,只能通过,程序员显示的释放(释放IPC资源的指令或者system call系统调用)或者是OS重启。文件,堆空间,进程退出的时候,这些资源都会被操作系统回收,但是IPC资源不会。

ipcrm指令:

用于删除ipc资源参数:

-M   以shmkey删除共享内存

-m   以shmid删除共享内存

-Q   以msgkey删除消息队列

-q    以msgid删除消息队列

-S    以semkey删除信号量

-s    以semid删除信号量


2.shmctl函数-删除/释放

  • shmid:前面shmget函数的返回值,描述共享内存数据结构中的shmid
  • cmd:cmd指令,我们这里只用删除指令IPC_RMID
  • *buf:操作系统用于描述当亲共享内存的数据结构

shmctl(shmid, IPC_RMID, NULL);

printf("key: 0x%x, shmid: %d -> shm delete success\n", key, shmid);

这样就能够删除shmid对应的共享内存(释放空间)


3.shmat函数-挂接

  • shmid:前面shmget函数的返回值,描述共享内存数据结构中的shmid
  • shmaddr:挂接到哪个位置,一般设为NULL(我们自己不太清楚,但是操作系统清楚)
  • shmflg:由九个权限标志构成,它们的用法和创建文件时使用的mode模式标志是一样的,我们一般传0
  • 返回值:成功返回创建共享内存的起始地址(虚拟地址),就像是malloc的返回值

char *mem = (char*)shmat(shmid, NULL, 0);

printf("attaches shm success\n");

这样就得到了一个指向共享内存的地址mem


 4.shmdt函数-去挂接

        并不是释放共享内存,而是删除共享内存和进程的关系(删除进程页表上进程虚拟地址和共享内存物理地址映射的页表项)

  • shmid:前面shmget函数的返回值,描述共享内存数据结构中的shmid
  • shmaddr:去挂接哪个位置,一般不设置,用缺省值(我们自己不太清楚,但是操作系统清楚)
  • shmflg:由九个权限标志构成,它们的用法和创建文件时使用的mode模式标志是一样的,我们一般不传

    shmdt(mem);

这样当前进程共享内存的挂接就解除了


 5.e.g.

这里模拟实现一个用共享队列进行通信的服务端和客户端。服务端读数据,客户端写数据

server.c

#include 
#include 
#include 
#include #define PATH_NAME "./"  //用于ftok函数,表示当前的路径名,路径名+项目ID应该是唯一的,随之生成的key也就是唯一的
#define PROJ_ID 0x1234  //用于ftok函数,表示当前的路径名,路径名+项目ID应该是唯一的,随之生成的key也就是唯一的
#define SIZE 4097int main()
{key_t key = ftok(PATH_NAME, PROJ_ID);if(key < 0){perror("ftok");return 1;}int shmid = shmget(key, SIZE, IPC_CREAT|IPC_EXCL|0666); //创建全新的shm,加上了IPC_EXCL选项,如果和系统已经存在ID冲突,则出错返回,这个0666表示共享内存的权限if(shmid < 0){perror("shmget");return 2;}printf("key: %u, shmid: %d\n", key, shmid);//sleep(1);char *mem = (char*)shmat(shmid, NULL, 0);  //将共享内存挂接到当前进程printf("attaches shm success\n");//sleep(15);//此处进行通信逻辑,直接像数组一样对共享内存进行操作即可,服务端进行读操作while(1){sleep(1);printf("%s\n", mem); //server 认为共享内存里面放的是一个长字符串 }shmdt(mem);  //去挂接printf("detaches shm success\n");//sleep(5);shmctl(shmid, IPC_RMID, NULL);  //释放共享内存printf("key: 0x%x, shmid: %d -> shm delete success\n", key, shmid);//sleep(10);return 0;
}

client.c

#include 
#include 
#include 
#include #define PATH_NAME "./"
#define PROJ_ID 0x1234
#define SIZE 4097
int main()
{key_t key = ftok(PATH_NAME, PROJ_ID);if(key < 0){perror("ftok");return 1;}printf("%u\n", key);//client这里只需要进行获取即可,只需要加上IPC_CREATE选项即可int shmid = shmget(key, SIZE, IPC_CREAT);if(shmid < 0){perror("shmget");return 1;}char *mem = (char*)shmat(shmid, NULL, 0);//sleep(5);printf("client process attaches success!\n");//此处进行通信逻辑,直接像数组一样对共享内存进行操作即可,客户端进行写操作,每隔两秒,多写入一个字母char c = 'A';while(c <= 'Z'){mem[c-'A'] = c;c++;mem[c-'A'] = 0;sleep(2);}shmdt(mem);//sleep(5);printf("client process detaches success\n");//client不需要释放共享内存的空间,由服务端释放return 0;
}

运行结果:我们2s写一次,1s读一次,我们读的时候并没有把数据拿走,所以会读两次

        这里server会一直读取共享内存中的内容,即使共享内存中没有内容可以读,所以不会退出while循环,也不会执行去挂接和释放共享内存的操作。(需要后期自己用ipcrm指令删除,也可以考虑在循环中定义一个计数器,几s后自动退出循环,进行去挂接,释放共享内存的操作)

        这里我们读的时候直接读的是mem这个连续的地址空间,和前面管道中的不同,管道是基于文件的,在一次打开中,读过的内容不会再读了。管道中的内容都不会像普通文件一样保存在磁盘中,读取过的数据就会失效,不能重复读取(包括匿名管道和命名管道)


五.总结/共享内存的特性

1.我们在使用共享内存的时候,没有调用类似pipe or fifo中的read过样的接口,因为匿名管道和命名管道都是基于文件的,read和write也是基于文件的,本质是将数据从内核拷贝到用户,或者从用户拷贝到内核。共享内存并不是基于文件的。

2.共享内存一旦建立好并映射进自己进程的地址空间,进行通信的进程就可以直接看到该共享内存,就如同自己malloc的空间一般,不需要任何系统调用接口,直接当数组用都行。所以共享内存是所有的进程间通信中速度最快的!(共享内存最多拷贝一次,管道可能要4次或者更多)

3.当client没有写入,甚至没有启动的时候,server端也会直接读取shm,不会像管道那样等待client写入。共享内存不提供任何司步或者互斥儿制,需要程序员自行保证数据的安全!

Q:为什么共享内存的大小建议是4KB整数倍?

        共享内存在内核中申请的基本单位是页,内存页,大小为4KB,内核给你的大小是4KB的整数倍。比如我申请4097个字节->内核会给你4096byte*2的空间


六.共享内存的内核数据结构

用户层:是操作系统层的子集

ipc_perm中存放的uid是用户id,gid是组id,key就是我们传入的用于唯一标识共享内存块的id。

除此之外还有当前共享内存的一些基本信息

在内核中,所有的ipc资源都是通过数组组织起来的,这三种资源结构体都不一样,怎么放在同一个数组中呢?原因就是下面的ipc_perm

SystemV三种通信方式的ipc资源,描述ipc资源的数据结构很相似,而且第一个成员都是ipc_perm,这个ipc_perm是完全相同的。

这个数组实际上是一个ipc_perm*类型的数组,也就是一个指针数组,然后根据对应的类型强制类型转换。(用C语言实现的切片技术)

Q:为什么我们看到的shmid是0,1,2,3,4递增的呢?

        shmid为什么是0,1,2,3,4递增的呢?因为说白了这个shmid就是ipc_perm*数组的下标,共享内存是ipc资源,所有ipc资源都是用一个ipc_perm*数组管理的,shmid就是直接取了这个数组的下标。

相关内容

热门资讯

埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
世界上最漂亮的人 世界上最漂亮... 此前在某网上,选出了全球265万颜值姣好的女性。从这些数量庞大的女性群体中,人们投票选出了心目中最美...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
猫咪吃了塑料袋怎么办 猫咪误食... 你知道吗?塑料袋放久了会长猫哦!要说猫咪对塑料袋的喜爱程度完完全全可以媲美纸箱家里只要一有塑料袋的响...
长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...
应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...
脚上的穴位图 脚面经络图对应的... 人体穴位作用图解大全更清晰直观的标注了各个人体穴位的作用,包括头部穴位图、胸部穴位图、背部穴位图、胳...
demo什么意思 demo版本... 618快到了,各位的小金库大概也在准备开闸放水了吧。没有小金库的,也该向老婆撒娇卖萌服个软了,一切只...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...