Linux进程通信(二)

消息队列

消息队列基本概念
1. 系统V消息队列是随内核持续的,只有在内核重起或者显示删除一个消息队列时,
该消息队列才会真正被删除。因此系统中记录消息队列的数据结构(struct ipc_ids msg_ids)位于内核中,系统中的所有消息队列都可以在结构msg_ids中找到访问入口。
2. 消息队列就是一个消息的链表。每个消息队列都有一个队列头,用结构struct
msg_queue来描述(参见附录 2)。队列头中包含了该消息队列的大量信息,包括消息队列键值、用户ID、组ID、消息队列中消息数目等等,甚至记录了最近对消息队列读写进程的ID。读者可以访问这些信息,也可以设置其中的某些信息。
3. 下图说明了内核与消息队列是怎样建立起联系的:
其中:struct ipc_ids msg_ids是内核中记录消息队列的全局数据结构;struct msg_queue是每个消息队列的队列头。

操作消息队列

操作消息队列
对消息队列的操作无非有下面三种类型:
1、打开或创建消息队列
消息队列的内核持续性要求每个消息队列都在系统范围内对应唯一的键值,所以,要获得一个消息队列的描述字,只需提供该消息队列的键值即可; 注:消息队列描述字是由在系统范围内唯一的键值生成的,而键值可以看作对应系统内的一条路经。
2、读写操作
消息读写操作非常简单,对开发人员来说,每个消息都类似如下的数据结构: struct msgbuf{ long mtype; char mtext[1]; };
mtype成员代表消息类型,从消息队列中读取消息的一个重要依据就是消息的类型;mtext是消息内容,当然长度不一定为1。因此,对于发送消息来说,首先预置一个msgbuf缓冲区并写入消息类型和内容,调用相应的发送函数即可;对读取消息来说,首先分配这样一个msgbuf缓冲区,然后把消息读入该缓冲区即可。
3、获得或设置消息队列属性:
消息队列的信息基本上都保存在消息队列头中,因此,可以分配一个类似于消息队列头的结构(struct msqid_ds),来返回消息队列的属性;同样可以设置该数据结构。

消息队列API

1、文件名到键值 
#include   #include key_t ftok (char*pathname, char proj);   
            它返回与路径pathname相对应的一个键值。该函数不直接对消息队列操作,但在调用ipc(MSGGET,…)或msgget()来获得消息队列描述字前,往往要调用该函数。典型的调用代码是: 
key=ftok(path_ptr, 'a');      ipc_id=ipc(MSGGET, (int)key, flags,0,NULL,0);     …   
2、通信的三种方式  linux为操作系统V进程间通信的三种方式(消息队列、信号灯、共享内存区)提供了一个统一的用户界面:  int ipc(unsigned int call, int first, int second, int third, void * ptr, long fifth);   第一个参数指明对IPC对象的操作方式,对消息队列而言共有四种操作:MSGSND、MSGRCV、MSGGET以及MSGCTL,分别代表向消息队列发送消息、从消息队列读取消息、打开或创建消息队列、控制消息队列;first参数代表唯一的IPC对象;下面将介绍四种操作。
LINUX环境进程间通信 
1.int ipc( MSGGET, intfirst, intsecond, intthird, void*ptr, longfifth);  
与该操作对应的系统V调用为:int msgget( (key_t)first,second)。
2.int ipc( MSGCTL, intfirst, intsecond, intthird, void*ptr, longfifth)  
与该操作对应的系统V调用为:int msgctl( first,second, (struct msqid_ds*) ptr)。 
3.int ipc( MSGSND, intfirst, intsecond, intthird, void*ptr, longfifth);  
与该操作对应的系统V调用为:int msgsnd( first, (struct msgbuf*)ptr, second, third)。
4.int ipc( MSGRCV, intfirst, intsecond, intthird, void*ptr, longfifth);  与该操作对应的系统V调用为:int msgrcv( first,(struct msgbuf*)ptr, second, fifth,third)

消息队列的限制

每个消息队列的容量(所能容纳的字节数)都有限制,该值因系统不同而不同。在后面的应用实例中,输出了redhat 8.0的限制。另一个限制是每个消息队列所能容纳的最大消息数:在redhad 8.0中,该限制是受消息队列容量制约的:消息个数要小于消息队列的容量(字节数)。 注:上述两个限制是针对每个消息队列而言的,系统对消息队列的限制还有系统范围内的最大消息队列个数,以及整个系统范围内的最大消息数。一般来说,实际开发过程中不会超过这个限制。

信号灯

1、信号灯概述 信号灯与其他进程间通信方式不大相同,它主要提供对进程间共享资源访问控制机制。相当于内存中的标志,进程可以根据它判定是否能够访问某些共享资源,同时,进程也可以修改该标志。除了用于访问控制外,还可用于进程同步。
信号灯有以下两种类型:
(1) 二值信号灯:最简单的信号灯形式,信号灯的值只能取0或1,类似于互斥锁。 注:二值信号灯能够实现互斥锁的功能,但两者的关注内容不同。信号灯强调共享资源,只要共享资源可用,其他进程同样可以修改信号灯的值;互斥锁更强调进程,占用资源的进程使用完资源后,必须由进程本身来解锁。
(2)计算信号灯:信号灯的值可以取任意非负值(当然受内核本身的约束)。
2、Linux信号灯 linux对信号灯的支持状况与消息队列一样,在red had 8.0发行版本中支持的是系统V的信号灯。因此,本文将主要介绍系统V信号灯及其相应API。在没有声明的情况下,以下讨论中指的都是系统V信号灯。 注意,通常所说的系统V信号灯指的是计数信号灯集。
3、信号灯与内核
(1)、系统V信号灯是随内核持续的,只有在内核重起或者显示删除一个信号灯集时,该信号灯集才会真正被删除。因此系统中记录信号灯的数据结构(struct ipc_ids sem_ids)位于内核中,系统中的所有信号灯都可以在结构sem_ids中找到访问入口。
(2)、下图说明了内核与信号灯是怎样建立起联系的: 其中:struct ipc_ids sem_ids是内核中记录信号灯的全局数据结构;描述一个具体的信号灯及其相关信息。
4、操作信号灯 对消息队列的操作无非有下面三种类型: 1、打开或创建信号灯 与消息队列的创建及打开基本相同,不再详述。 2、信号灯值操作 linux可以增加或减小信号灯的值,相应于对共享资源的释放和占有。具体参见后面的semop系统调用。 3、获得或设置信号灯属性: 系统中的每一个信号灯集都对应一个struct sem_array结构,该结构记录了信号灯集的各种信息,存在于系统空间。为了设置、获得该信号灯集的各种信息及属性,在用户空间有一个重要的联合结构与之对应,即union semun。
5、信号灯的限制
(1)、一次系统调用semop可同时操作的信号灯数目SEMOPM,semop中的参数nsops如果超过了这个数目,将返回E2BIG错误。SEMOPM的大小特定与系统,redhat 8.0为32。
(2)、信号灯的最大数目:SEMVMX,当设置信号灯值超过这个限制时,会返回ERANGE错误。在redhat 8.0中该值为32767。
(3)、系统范围内信号灯集的最大数目SEMMNI以及系统范围内信号灯的最大数目SEMMNS。超过这两个限制将返回ENOSPC错误。redhat 8.0中该值为32000。
(4)、每个信号灯集中的最大信号灯数目SEMMSL,redhat 8.0中为250。 SEMOPM以及SEMVMX是使用semop调用时应该注意的;SEMMNI以及SEMMNS是调用semget时应该注意的。SEMVMX同时也是semctl调用应该注意的。
6、竞争问题 第一个创建信号灯的进程同时也初始化信号灯,这样,系统调用semget包含了两个步骤:创建信号灯;初始化信号灯。由此可能导致一种竞争状态:第一个创建信号灯的进程在初始化信号灯时,第二个进程又调用semget,并且发现信号灯已经存在,此时,第二个进程必须具有判断是否有进程正在对信号灯进行初始化的能力。在参考文献[1]中,给出了绕过这种竞争状态的方法:当semget创建一个新的信号灯时,信号灯结构semid_ds的sem_otime成员初始化后的值为0。因此,第二个进程在成功调用semget后,可再次以IPC_STAT命令调用semctl,等待sem_otime变为非0值,此时可判断该信号灯已经初始化完毕。

你可能感兴趣的:(概念信息,linux,进程通信)