【转】IPC-消息队列
一、 概念
消息队列就是一个消息的链表。对消息队列有写权限的进程可以向其中按照一定的规则添加新消息;对消息队列有读权限的进程可以从消息队列中读出消息。消息队列是随内核持续的。下面介绍三个概念:
1;随进程持续:IPC一直存在,直至打开IPC对象的最后一个进程关闭该对象为止,如管道和有名管道
2;随内核持续:IPC一直持续到内核重新自举或者显示删除对象为止。如:消息队列,信号量,共享内存
3;随文件系统持续:IPC一直持续的显示删除该对象为止
System V消息队列目前被大量使用。
二、 消息队列的信息
结构msqid_ds用来设置或返回消息队列的信息,存在于用户空间;
struct msqid_ds
{
struct ipc_perm msg_perm; /* structure describing operation permission */
__time_t msg_stime; /* time of last msgsnd command */
__time_t msg_rtime; /* time of last msgrcv command */
__time_t msg_ctime; /* time of last change */
unsigned long int __msg_cbytes; /* current number of bytes on queue */
msgqnum_t msg_qnum; /* number of messages currently on queue */
msglen_t msg_qbytes; /* max number of bytes allowed on queue */
__pid_t msg_lspid; /* pid of last msgsnd() */
__pid_t msg_lrpid; /* pid of last msgrcv() */
unsigned long int __unused4;
unsigned long int __unused5;
};
三、 打开创建消息队列
消息队列的内核持续性要求每个消息队列都在系统范围内对应唯一的键值,所以,要获得一个消息队列的描述字,只需提供该消息队列的键值即可。msgget用于创建一个消息队列或打开一个现存的队列。
int msgget(key_t key,int msgflag);
创建一个新的消息队列或打开一个存在的消息队列。
若成功则为消息队列描述字若出错则为-1。
key: 是一个键值,由ftok获得,或直接给其常量也可;
msgflg: 是一些标志位,数值有:IPC_CREAT(创建消息队列)、IPC_EXCL( )、IPC_NOWAIT( )或三者的或结果。
比如说:msgflg =IPC_CREAT| IPC_EXCL|0666,意思是若不存在key值的队列则创建,否则如果存在则打开队列,0666意思与一般文件权限一样。3位八进制数代表本用户,同组用户,其它用户的读写执行权限。
在以下两种情况下,该调用将创建一个新的消息队列:
1.如果没有消息队列与健值key相对应,并且msgflg中包含了IPC_CREAT标志位;
2.key参数为IPC_PRIVATE;
还有注意的是:当创建一个新队列时,系统自动初始化struct msqid_ds结构的下列成员。
ipc_perm结构按我们以前说的进行初始化。该结构中mode成员按flag中的相应权限位设置。
msg_qnum,msg_lspid,msg_lrpid,msg_stime,msg_rtime都设置为0。
msg_ctime设置为当前时间。msg_qbytes设置为系统限制值。
四、获得和修改消息队列属性,删除消息队列
int msgctl(int msqid, int cmd,struct msqid_ds *buf);
对消息队列进行多种操作
若成功返回0,若出错返回-1
msqid: 消息队列ID,
cmd: 要执行的操作,等同于信号量和共享存储的操作。
buf: 此队列的struct msqid_ds结构,临时的 msqid_ds 结构体类型的变量。用于存储读取的消息队列属性或需要修改的消息队列属性
该系统调用对由msqid标识的消息队列执行cmd操作,共有三种cmd操作:IPC_STAT(取得队列状态)、IPC_SET(设置队列属性)、IPC_RMID(删除消息队列)。
IPC_STAT:该命令用来获取消息队列信息,返回的信息存贮在buf指向的msqid_da结构中;
IPC_SET:该命令用来设置消息队列的属性,要设置的属性存储在buf指向的msqid_ds结构中;可设置属性包括:msg_perm.uid、msg_perm.gid、msg_perm.mode以及msg_qbytes,同时,也影响msg_ctime成员。
IPC_RMID:删除msqid_ds标识的消息队列.
五、用消息队列发送和接收消息
int msgrcv(int msqid, struct msgbuf *msgp, size_t msgsz, long msgtyp, int msgflg);
从消息队列msqid中读取一个msgtyp类型的消息,并把消息存储在msgp指向的msgbuf结构体中。(在成功读取后,队列中的这条消息将被删除)
成功返回0,失败返回-1.
msqid: 消息队列ID
msgp:接收存放的消息队列缓存结构,
msgsz:消息数据长度
msgtype:消息类型,=0读取队列中第一个数据
msgflg:读取标志,
IPC_NOWAIT(如果碰到没有满足条件的消息,立即返回,此时错误代码为ENOMSG),
IPC_EXCEPT(与msgtype>0)配合使用,返回队列中第一个类型不为msgtype的消息,
MSG_NOERROR:截断看超长数据。
附:
msgrcv()解除阻塞的条件三个条件:
1. 消息队列中有了满足条件的消息(或使用了);
2. msqid代表的消息队列被删除;
3. 调用msgrcv()的进程被信号中断;
int msgsnd(int msqid, struct msgbuf *msgp, int msgsz, int msgflg);
向消息队列发送一条消息。即将发送的消息存储在msgp指向的msgbuf结构中,消息的大小由msgze指定。
成功返回0,否则返回-1。
msqid:消息队列ID
msgp:指向消息数据的指针
msgsz:发送消息的大小
msgflag:标志位
附:msgp格式为:
struct msgbuf{
long mtype; /*消息类型 must be > 0*/
char mtext[1]; /*消息数据 这是只是一个数组首地址,并非只有一个字符*/
};
我们可以把msgbuf结构看成是一个模版,程序员可以根据自己的需要来设计直接的消息结构。举例来说,如果某个应用需要交换由一个整数后跟一个8字节字符数组构成的消息,
那它可以如下定义自己的结构:
typedef struct my_msgbuf{
long mtypel
int mshort;
char mchar[MY_DATA];
}Message;
对发送消息来说,有意义的msgflg标志为IPC_NOWAIT,指明在消息队列没有足够空间容纳要发送的消息时,msgsnd是否等待,0表示等待。
造成msgsnd()等待的条件有两种:
1.当前消息的大小与当前消息队列中的字节数之和超过了消息队列的总容量;
2.当前消息队列的消息数(单位"个")不小于消息队列的总容量(单位"字节数"),此时,虽然消息队列中的消息数目很多,但基本上都只有一个字节。
msgsnd()解除阻塞的条件有三个:
1.不满足上述两个条件,即消息队列中有容纳该消息的空间;
2.msqid代表的消息队列被删除;
3.调用msgsnd()的进程被信号中断;
当msgsnd成功返回,与消息队列相关的msqid_ds结构得到更新,以标明发出该调用的进程ID(msg_lspid),进行该调用的时间(msg_stime),并指示队列中增加了一条消息。
六、消息队列的使用