【Linux】进程间通信(System V IPC)

这节我们开始学习System V IPC方案。
分别是共享内存,消息队列与信号量

会着重讲解共享内存,但是消息队列与信号量只会说明一下原理。

原因:System V是新设计的一套标准

  1. 与文件的整合度不高
  2. 只能进行本地通信

更何况,我们现在有了网络,可以进行更强大的通信。

共享内存:

原理:

首先我们要明确一点,进程间具有独立性,那我们通信必须要申请一块公共内存。下图就是一个简略的简述两个独立的A与B进程

在这里插入图片描述

我们回想一下动态库的加载,我们将加载到内存中的动态库可以分别通过页表映射到A与B的共享区,故动态库也叫做共享库(被多个进程同时拥有)。
那么共享内存当然也可以被加载到共享区,这样两个进程就可以有一块公共的内存空间了!
在这里插入图片描述

所以,通过地址空间的映射,让A和B看到同一份内存就叫做共享内存

理解:

  1. 所有的操作都是OS来完成的。–>因为OS是软硬件的管理者,

  2. OS可以做,但是OS不知道什么时候做,所以OS需要提供系统调用接口,这样就可以由进程发起

  3. 不仅仅只有A与B进程可以通过共享内存进行通信,同理D和C,E和F也都可以!

  4. OS注定要对共享内存进行管理。因为有的共享内存刚创建,有的要删除,有的要通信,这就需要管理起来,而管理就需要先描述在组织。在这里插入图片描述

  5. 共享内存 = 内存空间(数据)+ 共享内存属性


代码 + 理论:

shmget:

先来看看如何申请共享内存
在这里插入图片描述
我们先来分别理解一下

size:这个代表你想要多大字节的内存


shmflg:这是最主要的两个标志
在这里插入图片描述

与文件系统调用的传参是很相似的,都是本质都是位图的应用!

我们将最主要的两个标志分别组合看看代表什么含义:

IPC_CREAT:代表目标共享内存没有就创建,有了就获取
IPC_EXCL:单独使用无意义
IPC_CREAT | IPC_EXCL:代表目标共享内存没有就创建,有了就报错。

什么?那第三个有什么用吗?
答案是有的,他们分别代表了不同的职责。

IPC_CREAT代表的是总能获取一个共享内存,
而两者相或代表这个共享内存一定是全新的,
也可以分别对应上我们的client与server。

我们一般进行创建的时候最好加上权限,也就是|上一个0x666之类的


key:是表示共享内存的唯一标识符

那么该如何理解呢?
我们以一个小三段论来理解(是什么,为什么,怎么办)

是什么:当你想要创建一个共享内存时,OS怎么知道你想创建的是否已经存在了呢?
故需要一个标识符来进行标识!

为什么:为什么这个标识符要由用户生成?
如果是由OS自动生成,当A成功申请了一个共享内存,当要与B建立联系时,必然就要知道这块内存在哪里,可是这是由OS生成,B就没办法知道,也就建立不了联系了。
所以要由用户生成,使用同一个key进行一个规则上的约束,进而互相找到对方。

怎么办:我们使用OS提供的ftok接口进行生成key。
pathname与proj_id自定义即可
在这里插入图片描述

接下来我们代码操作一下
在这里插入图片描述

分别在客服端与服务端打印出来是一样的数字在这里插入图片描述
接下来我们进行shmget:
在这里插入图片描述

那么问题来了,返回值是啥?
返回值也是共享内存标识符,但和key完全不一样,

我们进行打印一下返回值。
在这里插入图片描述
我们另外发现即使创建这个共享内存的进程已经结束了,但是共享内存依旧不可以重新创建,这也就说明共享内存的生命周期是随进程的,不随着进程的结束而结束。

那么这两个标识符到底有什么区别呢?
再开始之前我们先看一下共享内存的删除。

指令删除语法:

ipcrm -m shmid

在这里插入图片描述
经过测试我们发现只有这个返回值shmid才可以删除掉,而key是无法做到的。
现在就可以正式回答这个问题了:
我们用户生成一个标识符给内核用,而内核生成一个标识符给用户用,这也侧面进行了解耦,类似文件中的struct file*与fd

可是使用指令删除也太挫了,我们学习一下如何在进程中删除~

shmtcl:

在这里插入图片描述
没错,虽然没有直接进行删除的接口,但是ctl也就是control的简写,使用这个接口也可以进行除了删除之外的操作。

我们先来了解一下shmctl的各个参数:
shmid:没什么好说的,就是我们刚刚shmget的返回值
cmd:对这个shmid标识的共享内存进行各种操作的控制参数,我们用的删除即是下图的IPC_RMID(rm是remove,id是immediate)
在这里插入图片描述
buf:是一个输入/输出型参数,可以传一个结构体进行对内核数据的获取,也可以进行控制里面的共享内存的各种参数,这里我们不需要管,直接传个nullptr即可~
在这里插入图片描述
代码:
在这里插入图片描述

shmat && shmdt:

shmget是进行对共享内存的创建,而at是进行挂载,将创建好的共享内存映射到共享区!

在这里插入图片描述
参数解释:
shmid:没什么好说的,就是get的返回值
ahmaddr:想要对共享内存挂载的地址,这里我们不需要(传nullptr即可)
shmflg:对于共享内存的权限,同样不需要(传0即可)

void* :虚拟地址空间的共享内存返回值(类似malloc的返回值)

在这里插入图片描述
于是我们终于可以进行通信了,以上也都是通信的准备工作~
但是开始之前我们需要将Getshm改动一下,分别适配server,client
在这里插入图片描述
下图是进行通信的代码,client是写数据,而server是接收数据
在这里插入图片描述
现象:可以发现共享内存并没有像管道一样提供一些保护机制,比如管道没数据就不会读,每次读完就会清空数据…
在这里插入图片描述
于是我们得到了一个结论:共享内存并没有提供数据的保护机制。

但是她就没有优点吗,答案是否定的。
他是所有IPC中最快的通信方式,因为他没有调用系统调用接口,不存在各种缓冲区考来考去,大大减少了拷贝!

此外,虽然共享内存没有提供保护机制,但是我们可以使用一些手段进行对其进行保护,例如我们的命名管道~

消息队列:

我们接下来的消息队列与信号量都是讲解一下原理,不进行操作~

原理:

在这里插入图片描述
同样,这是由OS本身申请,由进程发起。
注意:消息队列也可以有多个,所以也需要进行管理,先描述再组织!

接口:

msgget:

在这里插入图片描述
相信key与flg已经不用介绍了,与shm是非常相似的


msgctl:

在这里插入图片描述
也是与shm一致!

msgsnd && msgrcv:

在这里插入图片描述
msgp:
是指向消息块的结构体。
在这里插入图片描述
当我们想使用时, 像下图一样使用即可在这里插入图片描述
msgsz:结构体的大小,我们直接sizeof即可获得
msgflg:权限

msgtyp:数据块的类型
在这里插入图片描述

指令操作:

与共享内存相似,将m改为q即可,shmid改为msgid。

注意:消息队列的声明周期也是随内核

信号量:

一些概念的渗透:

在这里插入图片描述

对于信号量的理解:

我们回顾一下shm与pipe。都是对一块内存的整体使用

在这里插入图片描述
那我们是不是可以将这大块资源分成一个个小块,让多个执行流访问同一块资源,在公共资源的基础上保证并发度。
所以信号量就是对不整体使用,对小资源进行管理的一种IPC,本质就是计数器。


我们来举个例子:
我们看电影都会买票,只要你买了票不管你去不去,这个座位都是你的。

但是对于这种事件我们最怕一件事:那就是只有25个座位却卖出26张票。

那我们如何进行预防呢?
就是设置一个计数器count = 25。卖出一张票就–,如果count == 0就不卖了。


在这个例子中:
在这里插入图片描述

结论:保证资源被合理使用要做到以下两点

  • 先“买票”
  • 让执行流(进程)与资源一一对应。

所以访问共享资源我们要

  1. 申请信号量
  2. 访问共享内存
  3. 释放信号量

所以信号量的本质就是对资源的预定。
注意:对于信号量我们也分为两种:多元与二元信号量。
多元就像25张票的例子,
而二元呢?就像是一个vip放映厅,只允许一个人进去看,对资源的整体进行使用—>对应的其实就是互斥

我们现在也就从生活中的例子过渡到信号量了


疑问:
刚刚我说信号量本质是一个计数器,那么我可以使用一个全局变量来当做信号量?
答案是否定的

  1. 全局不能被所有进程看到,只有父子进程或其他亲缘关系的进程可以看到,但是会发生写时拷贝,各玩各的,非亲缘关系的进程更不用说了
  2. 对一个变量的++/–操作是非原子的

非原子是什么意思?
转变到汇编语句实际上是有超过2句代码以上的,
像我们定义一个变量并直接初始化是只有一句汇编语句的,也就是原子的

虽然都是IPC范畴内的,但是他与共享内存和消息队列还是有不一样的,并不是用来传递消息,而是用来进行同步互斥
但最终还是IPC范畴的,因为他是公共资源~

现在我们还有最后一个问题:
信号量也是公资源,需要用它来保护其他公共资源,那她自己怎么保护自己的资源?
他的++/–操作被设置为原子的,叫做PV操作!

接口 && 指令:

待更新~

OS对于这三者的管理:

其实在共享内存哪里我们草草的涉及了一下一个很关键的东西。
在shmctl中有一个shmid_ds的结构体。在这里插入图片描述
这个结构体的第一个成员是一个perm结构体在这里插入图片描述
而这个东西不仅仅是只有shm有,其余两个也都有!

在这里插入图片描述
我们来看一个图(是一个关于IPC的 内核图)
在这里插入图片描述
可以看到有一个ipc_ids的结构体(最上边)内指向有一个ipc_id_ary的结构体。这个结构体内有个柔性数组[0] + size。
这个柔性数组每一个成员是一个个指针,是一个个的kern_ipc_perm的指针,也就是我们用户层各个ds结构体的第一个成员,当我们像利用ctl函数进行拷贝时,实际上就是去内核拷贝一份这个给用户。

实际上假如当我们申请一个信号量,就会在柔性数组中寻找一个未被使用的位置指向你申请的sem或者shm或者msg,所以shmid…就是柔性数组下标。
这也就说明我们的这个柔性数组把这个IPC资源统一管理起来l!

所以我们的key也会相互冲突(对柔性数组进行遍历检查),毕竟用的也是同一个算法嘛~

那么我们现在还有一个问题,这个柔性数组内的指针怎么知道自己指向什么?所以我们就可以肯定这个结构体内肯定有进行标识的字段!在这里插入图片描述
至于如何变为对应的结构体(shm,msg,sem),我们就用指针的强转咯~

所以这也就是用C语言实现的多态!

本章完~

相关推荐

最近更新

  1. TCP协议是安全的吗?

    2024-06-06 19:42:05       16 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-06-06 19:42:05       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-06-06 19:42:05       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-06-06 19:42:05       18 阅读

热门阅读

  1. C语言经典例题-8

    2024-06-06 19:42:05       9 阅读
  2. 我入门AI的学习感想

    2024-06-06 19:42:05       5 阅读
  3. Linux 命令 `diff` 的深度解析

    2024-06-06 19:42:05       9 阅读
  4. springboot动态切换数据源

    2024-06-06 19:42:05       9 阅读
  5. make 是啥

    2024-06-06 19:42:05       8 阅读
  6. Android基础-工程目录结构说明

    2024-06-06 19:42:05       8 阅读
  7. 呼叫中心系统如何融入信创国产战略?

    2024-06-06 19:42:05       8 阅读
  8. 程序员高效工作(摸鱼)的 10 个方法

    2024-06-06 19:42:05       7 阅读