1、对消息队列的操作有下面三种类型:
(1) 打开或创建消息队列。消息队列的内核持续性要求每个消息队列都在系统范围内对应唯一的键值,所以,要获得一个消息队列的描述字,只需提供该消息队列的键值即可。
注:消息队列描述字是由在系统范围内唯一的键值生成的,而键值可以看作对应系统内的一条路经。
(2)读写操作。消息读写操作非常简单,对开发人员来说,每个消息都类似如下数据结构:
struct msgbuf
{
long mtype;
char mtext[1];
}
mtype成员代表消息类型,从消息队列中读取消息的一个重要依据就是消息的类型mtext是消息内容,当然长度不一定为1。因此,对于发送消息来说,首先预置一个msgbuf缓冲区并写入消息类型和内容,调用相应的发送函数即可;对读取消息来说,首先分配这样一个msgbuf缓冲区,然后把消息读入该缓冲区即可。
(3)获得或设置消息队列属性。消息队列的信息基本上都保存在消息队列头中,因此可以分配一个类似于消息队列头的结构,如图13-5-2所示,来返回消息队列的属性;同样可以设置该数据结构。
2、消息队列API:
(1)ftok函数:用于将文件名转换成键值;
(2)msgget函数:用于创建消息队列;
(3)msgrcv函数:用于读出消息队列的数据;
(4)msgsnd函数:用于往消息队列写入数据;
(5)msgctl函数:用于控制消息队列。
3、消息队列的限制:
每个消息队列的容量(所能容纳的字节数)都有限制,该值因系统不同而不同。在后面的应用实例中,输出了RedHat8.0的限制另一个限制是每个消息队列所能容纳的最大消息数在RedHad8.0中,该限制是受消息队列容量制约的:消息个数要小于消息队列的容量(字节数)。注:上述两个限制是针对每个消息队列而言的,系统对消息队列的限制还有系统范围内的最大消息队列个数,以及整个系统范围内的最大消息数。一般来说,实际开发过程中不会超过这个限制。
4、mmap()及其相关系统调用:
mmap()系统调用使得进程之间可以通过映射同一个普通文件实现共享内存。普通文件被映射到进程地址空间后,进程可以向访间普通内存一样对文件进行访问,不必再调用read()、write(()等操作注:实际上,mmap()系统调用并不是完全为了用于共享内存而设计的,它本身提供了不同于一般对普通文件的访问方式,进程可以像读写内存一样对普通文件的操作。而POSIX或系统V的共享内存IPC则纯粹用于共享目的,当然mmap()实现共享内存也是其主要应用之一。
5、系统调用mmap()用于共享内存的两种方式:
(1)使用普通文件提供的内存映射:适用于任何进程之间;此时,需要打开或创建一个文件,然后再调用mmap()。
(2)用特殊文件提供匿名内存映射:适用于具有亲缘关系的进程之间,由于父子进程特殊的亲缘关系,在父进程中先调用mmap(),然后调用fork()。那么在调用fork(之后,子进程继承父进程匿名映射后的地址空间,同样也继承mmap()返回的地址,这样,父子进程就可以通过映射区域进行通信了。注意,这里不是一般的继承关系,一般来说,子进程单独维护从父进程继承下来的一些变量,而mmap()返回的地址,却由父子进程共同维护对于具有亲缘关系的进程实现共享内存最好的方式应该是采用匿名内存映射的方式。此时,不必指定具体的文件,只要设置相应的标志即可。
6、系统V共享内存的原理:
进程间需要共享的数据被放在一个叫作IPC共享内存区域的地方,所有需要访问该共享区域的进程都要把该共享区域映射到本进程的地址空间中去。系统V共享内存通过shmget获得或创建一个IPC共享内存区域,并返回相应的标识符。内核在保证shmget获得或创建个共享内存区,初始化该共享内存区相应的Ishmikernel结构注同时,还将在特殊文件系统hm中,创建并打开一个同名文件,并在内存中建立起该文件的相应dentry及inode结构新打开的文件不属于任何一个进程(任何进程都可以访问该共享内存区)。所有这一切都是通过系统调用shmget完成的。注:每一个共享内存区都有一个控制结构Jstructshmikernel,shmikernel是共享内存区域中非常重要的一个数据结构,它是存储管理和文件系统结合起来的桥梁。
7、系统V共享内存API
(1)shmget函数:用于创建共享内存;
(2)shmat函数:用于映射共享内存。
shmget()用来获得共享内存区域的D,如果不存在指定的共享区域就创建相应的区域shmat()把共享内存区域映射到调用进程的地址空间中去,这样,进程就可以方便地对共享区或进行访问操作。shmdt(调用用来解除进程对共享内存区域的映射。shtetl实现对共享内存区域的控制操作。这里我们不对这些系统调用做具体的介绍,读者可参考相应的手册,后面的范例中将给出它们的调用方法。
8、信号灯的两种类型:
(1)一值信号灯:最简单的信号灯形式,信号灯的值只能取0或1,类似于互斥锁。 注:二值信号灯能够实现互斥锁的功能,但两者的关注内容不同。信号灯强调共享资源只要共享资源可用,其他进程同样可以修改信号灯的值;互斥锁更强调进程,占用资源的进程使用完资源后,必须由进程本身来解锁。
(2)计算信号灯:信号灯的值可以取任意非负值(当然受内核本身的约束)。Linu对信号灯的支持状况与消息队列一样,在RedHad8.0发行版本中支持的是系统V的信号灯。因此,本文将主要介绍系统V信号灯及其相应API。在没有声明的情况下,以下讨论中指的都是系统V信号灯。
注意:通常所说的系统V信号灯指的是计数信号灯集。
9、对信号灯的操作无非有下面三个步骤
(1)打开或创建信号灯。与消息队列的创建及打开基本相同,不再详述;
(2)信号灯值操作。Linux可以增加或减小信号灯的值,相应于对共享资源的释放和占有。具体参见后面的semop系统调用;
(3)获得或设置信号灯属性。系统中的每一个信号灯集都对应一个structsemarray结构,该结构记录了信号灯集的各种信息,存在于系统空间。为了设置、获得该信号灯集的各种信息及属性,在用户空间有一个重要的联合结构与之对应,即unionsemen。
10、信号灯的API:
(1)semget函数用于配置信号灯;
(2)semop函数用于信号灯处理;
(3)semctl函数用于控制信号灯。