一.IPC概述
系统建立IPC通讯(如消息队列、共享内存时),由于通信双方不在同一进程中,为了找到相同的通信工具,必须指定同一个ID值。通常情况下,该id值通过ftok函数得到。
ftok原型如下:
key_t ftok( char * fname, int id )
fname就时你指定的文件名,id是子序号。
在一般的UNIX实现中,是将文件的索引节点号取出,前面加上子序号得到key_t的返回值。
如指定文件的索引节点号为65538,换算成16进制为0x010002,而你指定的ID值为38,换算成16进制为0x26,则最后的key_t返回值为0x26010002。
查询文件索引节点号的方法是: ls -i
当删除重建文件后,索引节点号由操作系统根据当时文件系统的使用情况分配,因此与原来不同,所以得到的索引节点号也不同。
如果要确保key_t值不变,要目确保ftok的文件不被删除,要么不用ftok,指定一个固定的key_t值,比如:
#define IPCKEY 0x111
char path[256];
sprintf( path, "%s/etc/config.ini", (char*)getenv("HOME") );
msgid=ftok( path, IPCKEY );
同一段程序,用于保证两个不同用户下的两组相同程序获得互不干扰的IPC键值。
由于etc/config.ini(假定)为应用系统的关键配置文件,因此不存在被轻易删除的问题——即使被删,也会很快被发现并重建(此时应用系统也将被重起)。
ftok()的设计目的也在于此。
只要ftok的参数相同,那么获得的相同类型通信工具的通信对象就相同。
二.消息队列
1,消息队列模型:
消息队列是消息的链式队列,整个消息队列有两种数据结构:
msqid_ds消息队列数据结构:表示整个消息队列的基本情况,主要包括整个消息队列的权限,包括拥有者和操作者权限,另外还包括两个重要的指针分别指向消息队列的第一个消息和最后一个消息。
msg消息队列的数据结构:整个消息队列的主体,一个消息队列有若干个消息,每个消息数据结构的基本属性包括消息类型,消息大小,消息内容指针和下一个消息数据结构位置。
消息队列的限制:
默认情况下,整个系统中最多只能有16个消息队列
每个消息队列最大为16384个字节
消息队列中每个消息最大为8192个字节
2、消息队列的基本概念
消息队列就是一个消息的链表。有足够写权限的进程可往队列中放置消息,有足够读权限的进程可从队列中取走消息。每个消息是一个记录它由发送者赋予一个优先级。在某个进程往一个队列写入消息之前,并不需要另外某个进程在该队列上等待消息的到达。这跟管道和FIFO是相反的,对后者来说,除非读出者已存在,否则先有写入者是没有意义的。消息队列是随内核持续的。一个进程可以先往某个队列写入一些消息后终止,让另外一个进程在以后某个时刻读出这些消息。这一点管道和FIFO是不支持的。
3、消息队列的分类
目前主要有两种类型的消息队列:POSIX消息队列以及系统V消息队列,系统V消息队列目前被大量使用。考虑到程序的可移植性,新开发的应用程序应尽量使用POSIX消息队列。
4、和消息队列相关的结构
每个消息队列都有一个队列头,用结构struct msg_queue来描述。队列头中包含了该消息队列的大量信息,包括消息队列键值、用户ID、组ID、消息队列中消息数目等等,甚至记录了最近对消息队列读写进程的ID。读者可以访问这些信息,也可以设置其中的某些信息。这个结构存于系统空间。
struct msg_queue {
struct kern_ipc_perm q_perm;
time_t q_stime; /* last msgsnd time */
time_t q_rtime; /* last msgrcv time */
time_t q_ctime; /* last change time */
unsigned long q_cbytes; /* current number of bytes on queue */
unsigned long q_qnum; /* number of messages in queue */
unsigned long q_qbytes; /* max number of bytes on queue */
pid_t q_lspid; /* pid of last msgsnd */
pid_t q_lrpid; /* last receive pid */
struct list_head q_messages;
struct list_head q_receivers;
struct list_head q_senders;
};
5,消息队列的基本属性,,可以通过msgctl()函数使用IPC_INFO
(1)msqid_ds数据结构:
结构msqid_ds用来设置或返回消息队列的信息,存在于用户空间;
struct msqid_ds
{
struct ipc_perm msg_perm; //权限
struct msg *msg_first; /* first message on queue,unused */ //指向消息头
struct msg *msg_last; /* last message in queue,unused */ //指向消息尾
__kernel_time_t msg_stime; /* last msgsnd time */ //最近发送消息的时间
__kernel_time_t msg_rtime; /* last msgrcv time */ //最近接收消息的时间
__kernel_time_t msg_ctime; /* last change time */ //最近修改消息的时间
unsigned long msg_lcbytes; /* Reuse junk fields for 32 bit */
unsigned long msg_lqbytes; /* ditto */
unsigned short msg_cbytes; /* current number of bytes on queue *///当前消息队列的大小
unsigned short msg_qnum; /* number of messages in queue */ //当前消息个数
unsigned short msg_qbytes; /* max number of bytes on queue */ //队列最大值
__kernel_ipc_pid_t msg_lspid; /* pid of last msgsnd */ //最近snd的pid
__kernel_ipc_pid_t msg_lrpid; /* last receive pid */ //最近rcv的pid
};
(2)msg数据结构:
struct msg
{
struct msg *msg_next; /*队列上的下一条消息 */
long msg_type; /*消息类型*/
char *msg_spot; /*消息正文的地址 */
short msg_ts; /*消息正文的大小 */
};
6,消息队列的管理
消息队列是比较高级的一种进程间通信方法,因为它真的可以在进程间传送massege,你传送一个"I seek you"都可以。一个消息队列可以被多个进程所共享(IPC就是在这个基础上进行的);如果一个进程的消息太多一个消息队列放不下,也可以用多于一个的消息队列(不过可能管理会比较复杂)。共享消息队列的进程所发送的消息中除了massege本身外还有一个标志,这个标志可以指明该消息将由哪个进程或者是哪类进程接受。每一个共享消息队列的进程针对这个队列也有自己的标志,可以用来声明自己的身份。
(1)、消息队列相关函数
(a)、创建消息队列
名称:: | msgget |
功能: | 创建消息队列 |
头文件: | #include <sys/types.h> #include <sys/msg.h> #inlcude <sys/ipc.h> |
函数原形: | int msgget(key_t key, int msgflag); |
参数: | key消息队列的键 flag一些标志位 |
返回值: | 若成功则为消息队列描述字若出错则为-1。 |
参数key是一个键值,由ftok获得;msgflg参数是一些标志位。该调用返回与健值key相对应的消息队列描述字。
在以下两种情况下,该调用将创建一个新的消息队列:
1.如果没有消息队列与健值key相对应,并且msgflg中包含了IPC_CREAT标志位;
2.key参数为IPC_PRIVATE;
参数msgflg的地位用来确定消息队列的访问权限,可以为以下:
IPC_CREAT(如果key不存在,创建消息队列;存在的话,返回消息队列ID)
IPC_EXCL( 如果key存在,返回失败)
IPC_NOWAIT( 如果需要等待,返回错误)
或者三者的或结果。
还有注意的是:当创建一个新队列时,系统自动初始化struct msqid_ds结构的下列成员。
ipc_perm结构按我们以前说的进行初始化。该结构中mode成员按flag中的相应权限位设置。
msg_qnum,msg_lspid,msg_lrpid,msg_stime,msg_rtime都设置为0。
msg_ctime设置为当前时间。
msg_qbytes设置为系统限制值。
(b)、消息队列属性控制
名称:: | msgctl |
功能: | 对消息队列进行多种操作 |
头文件: | #include <sys/msg.h> |
函数原形: | int msgctl(int msqid, int cmd,struct msqid_ds *buf); |
参数: | msqid 消息队列描述字 cmd 要执行的操作 buf 此队列的struct msqid_ds结构 |
返回值: | 若成功返回0,若出错返回-1。 |
该系统调用对由msqid标识的消息队列执行cmd操作,共有三种cmd操作:
IPC_STAT、IPC_SET、IPC_RMID、IPC_INFO。
IPC_STAT:该命令用来获取消息队列信息,返回的信息存贮在buf指向的msqid_ds结构中;
IPC_SET:该命令用来设置消息队列的属性,要设置的属性存储在buf指向的msqid_ds结构中;可设置属性包括:msg_perm.uid、msg_perm.gid、msg_perm.mode以及msg_qbytes,同时,也影响msg_ctime成员。
IPC_RMID:删除msqid_ds标识的消息队列.
IPC_INFO:如ipcs,读取消息队列的基本情况
(c)、
名称:: | msgsnd |
功能: | 将数据放到消息队列上 |
头文件: | #include <sys/types.h> #include <sys/msg.h> #inlcude <sys/ipc.h> |
函数原形: | int msgsnd(int msqid, struct msgbuf *msgp, int msgsz, int msgflg); |
参数: | msqid 消息队列描述字 msgp 指向消息数据的指针 msgsz 发送消息的大小 msgflg 标志位 |
返回值: | 若成功则为0,若出错则为-1。 |
向msgid代表的消息队列发送一个消息,即将发送的消息存储在msgp指向的msgbuf结构中,消息的大小由msgze指定。
struct msgbuf{
long mtype; /*消息类型*/
char mtext[1]; /*消息数据*/
};
我们可以把msgbuf结构看成是一个模版,程序员可以根据自己的需要来设计直接的消息结构。举例来说,如果某个应用需要交换由一个整数后跟一个8字节字符数组构成的消息,那它可以如下定义自己的结构:
typedef struct my_msgbuf{
long mtypel
int mshort;
char mchar[MY_DATA];
}Message;
参数mtype是一个正整数,又产生消息的进程生成,由于表示消息的类型,因此接收消息的进程可以用来进行消息的选择(消息队列在存储信息时是按照发送的先后顺序存放的);
参数msgsz为发送消息的大小,其大小为0到系统对消息队列的限制值;
参数msgflg用来指定在达到系统为消息队列所定的界限时应采取的操作
IPC_NOWAIT:如果需要等待,则不发送消息并且调用进程立即返回错误信息EAGAIN;
0:则调用进程执行挂起,直到达到系统所规定的最大值为止;
对发送消息来说,有意义的msgflg标志为IPC_NOWAIT,指明在消息队列没有足够空间容纳要发送的消息时,msgsnd是否等待。造成msgsnd()等待的条件有两种:
1.当前消息的大小与当前消息队列中的字节数之和超过了消息队列的总容量;
2.当前消息队列的消息数(单位"个")不小于消息队列的总容量(单位"字节数"),此时,虽然消息队列中的消息数目很多,但基本上都只有一个字节。
msgsnd()解除阻塞的条件有三个:
1.不满足上述两个条件,即消息队列中有容纳该消息的空间;
2.msqid代表的消息队列被删除;
3.调用msgsnd()的进程被信号中断;
当msgsnd成功返回,与消息队列相关的msqid_ds结构得到更新,以标明发出该调用的进程ID(msg_lspid),进行该调用的时间(msg_stime),并指示队列中增加了一条消息。
(d)、接收消息
名称:: | msgrcv |
功能: | 从队列中接收消息 |
头文件: | #include <sys/types.h> #include <sys/msg.h> #inlcude <sys/ipc.h> |
函数原形: | Int msgrcv(int msqid, void *msgp, size_t msgsz, long int msgtyp, int msgflg); |
参数: | msqid 消息队列描述字 msgp 指向消息数据的指针 msgsz 发送消息的大小 msgtyp 指定请求消息的类型 msgflg 标志位 |
返回值: | 若成功则为0,若出错则为-1。 |
其他参数跟发送函数一样,
第二个参数中元素mtype是接收消息的类型(由发送方指定)
第三个参数msgsz用于指定mtext的大小,如果收到的消息大于msgsz,并且msgflg&MSG_NOERROR为真,则将该消息截止msgsz字节,消息的截断部分将丢失;
第四个参数msgtyp用于指定请求的消息类型:
msgtyp=0:收到队列中的第一条消息,任意类型
msgtyp>0:收到第一条msgtyp类型的消息
msgtyp<0:收到第一条最低类型(小于或等于msgtyp)的消息
接收消息成功以后,该消息将自动从消息队列中删除,并返回接收到的消息大小。