一、了解 System V消息队列
System V消息队列和共享内存有很多相似之处,都是用来进程间通信的,只不过共享内存是从内存中申请了一块物理内存,而消息队列在内存中是以队列的形式存在的
。
进程A,B之间互相通信,A向B发送数据,数据就会写在一个数据块中,链入队列中,进程B向A发送数据,也是如此。那么,进程A,B怎么样才能够拿到对方发送给自己的数据呢?
这就要对数据块进行标记了,用一个变量(mtype)来标记一下就可以知道是谁发送的数据了。
所以,消息队列是一个进程向另一个进程发送有类型数据块的方式
。
可能会有很多的进程之间都会用消息队列进行通信,那么,有这么多的消息队列,OS也是要管理的。
在用户层面也提供了一种结构体描述消息队列
。我们来看一下。
接下来,就认识一下消息队列的接口。
ipcs -q//查看消息队列
ipcrm -q //删除消息队列
发送数据和接收数据
有类型的数据块
可以看到,这些接口都是很相似的,这里不再做过多介绍。
二、System V 信号量了解
在了解信号量之前,我们需要对一些概念进行理解。
1 并发编程的概念
多个执行流(进程)能看到的同一份公共资源:共享资源
。
被保护起来的资源叫做临界资源
。
那么,什么叫做临界资源呢?
比如:进程A,B之间要互相通信,A向共享资源写入数据,B也向共享资源写入数据,那么,在同一时间,有两个执行流访问同一份资源,岂不是会造成数据的混乱,所以需要对共享资源进行保护,保护起来的资源就叫做临界资源。
保护的方式常见:互斥和同步
。
任何时刻只允许一个执行流访问资源,叫做互斥
。
这就好比ATM,一次只能有一个人取钱。
多个执行流,访问临界资源的时候,具有一定的顺序性,叫做同步
。
比如:食堂阿姨打饭的时候,所有人都必须在窗口前进行排队。
系统中某些资源一次只允许一个进程使用,称这样的资源为临界资源或互斥资源
。
对于临界资源的访问,都是通过代码实现的,在进程中涉及到临界资源的程序叫临界区,不访问临界资源的代码叫做非临界区。
所以,对共享资源的保护,本质是对访问共享资源的代码进行保护(毕竟临界资源的访问是通过代码实现的)
。
2. 信号量的理解
什么是信号量呢?
举一个例子:有去过电影院的小伙伴吧,在进电影院之前,我们必须先买票,才能够进去电影院。可是电影院的位置是有限的,假设只有100个座位,那么,我们就只能卖出100张票,卖出一张票就做一次减法操作,如果卖多了,那就要将多买出的票回收,做一次加法操作。
买票的本质就是对资源的预定机制。
所以,信号量的本质是一种描述临界资源数量的计数器,对资源的预定机制
。
如果这个电影院只有一个座位呢,那么,这个VIP座位一次就只能被一个人使用,那么,当这个座位未被售出时,信号量就应该为1,任何人都可以预定,售出后,信号量为0,别人不能使用,我们把它叫做二元信号量。
利用这个二元信号量,不就可以实现互斥功能了嘛。
信号量分为:多元信号量、二元信号量。
多元信号量:实现互斥或者同步功能。
二元信号量:实现互斥功能。
每一个人看电影都要去买票,那么每一个进程访问公共资源,就必须先申请信号量。所有的进程要申请信号量,就意味着所有的进程要先看到同一个信号量,那么信号量本身不就是共享资源了。那么将来对信号量做P(- -),V(++)操作,要如何保证信号量的安全呢?这就要保证P,V操作的原子性了
。
所有的进程都要看到同一个信号量,进程间通信的本质不就是让不同的进程看到同一份资源吗
,信号量也就属于IPC的范畴了。
注:
1.所有得System V资源,生命周期全部都随内核
。
2.所有的System V资源,都要被OS管理起来
。
三、内核是如何组织管理IPC资源的
那接下来我们就来分析下面的一张图。
我们要创建一个消息队列时,OS就给你分配一个msg_queue
,创建共享内存时,OS就分配一个 shmid_kernel
,创建信号量,就分配一个 sem_array
,不知道大家有没有注意到,描述这三个的结构体第一个成员类型都是一样的,也就是 kern_ipc_perm,在OS内组织IPC资源是用一个kern_ipc_perm* ipc_id_ary[]的数组管理的,这个数组根本就不是指向 kern_ipc_perm的,而是指向msg_queue或者shmid_kernel或者sem_array中的第一个成员,找到数组中未使用的一个下标填入这些结构体中第一个成员的地址
,获取地址时,第一个成员的地址和结构体的地址在数字层面上是相等的。
将来要访问其中的某一个结构体中的第一个成员,只需要找到数组下标进行访问就可以了,那如果要访问一个结构体中的所有成员呢?我们只需要将这个地址强转成这个结构体的地址
,不就可以进行访问了吗!
注意到第二个成员了吗?有人认识吗。这是柔性数组。在计算该结构体的大小时,不会将柔性数组的大小计算在内,所以该结构体的大小是4字节,未来需要多大的空间我们就可以动态申请 malloc 空间,这样就比较灵活。毕竟OS不知道我们有多少共享资源,空间给多了就浪费了,给少了又不够,这样就可以随时随地的申请空间了。
还记得我们之前调用的 shmget
函数吗,它的返回类型是 int ,这个整型代表的就是共享资源在 ipc_id_ary结构体数组中的下标
。
shmid就是数组下标
。
这是一个全局的结构体,OS通过这个结构体中的 entries找到 ipc_id_ary,就可以访问共享资源了
。
调用 ftok 函数形成的键值就被放在了这个结构体里面,两个进程通过 ipc_id_ary找到共享资源的结构体,然后通过相同的键值判断是否是同一个共享资源。
以双链表的形式管理消息队列
。
今天的文章分享到此结束,觉得不错的给个一键三连吧。