消息队列可以认为是一个消息链表,System V 消息队列使用消息队列标识符标识。具有足
够特权的任何进程都可以往一个队列放置一个消息,具有足够特权的任何进程都可以从一个给定队列读出一个消息。在某个进程往一个队列写入消息之前,并不需要另外某个进程在该队列上等待消息的到达。System V 消息队列是随内核持续的,只有在内核重起或者显示删除一个消息队列时,该消息队列才会真正被删除。可以将内核中的某个特定的消息队列画为一个消息链表,如下图所示:
对于系统中每个消息队列,内核维护一个msqid_ds的信息结构:
struct 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 */
__kernel_ipc_pid_t msg_lrpid; /* last receive pid */
};
System V 消息队列操作函数
系统V消息队列API共有四个,使用时需要包括几个头文件:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
)int msgget(key_t key, int msgflg)
参数key是一个键值,由ftok获得;msgflg参数是一些标志位。该调用返回与健值key相对应的消息队列描述字。
在以下两种情况下,该调用将创建一个新的消息队列:
如果没有消息队列与健值key相对应,并且msgflg中包含了IPC_CREAT标志位;
key参数为IPC_PRIVATE;
参数msgflg可以为以下:IPC_CREAT、IPC_EXCL、IPC_NOWAIT或三者的或结果。
调用返回:成功返回消息队列描述字,否则返回-1。
注:参数key设置成常数IPC_PRIVATE并不意味着其他进程不能访问该消息队列,只意味着即将创建新的消息队列。
2)int msgrcv(int msqid, struct msgbuf *msgp, int msgsz, long msgtyp, int msgflg);
该系统调用从msgid代表的消息队列中读取一个消息,并把消息存储在msgp指向的msgbuf结构中。
msqid为消息队列描述字;消息返回后存储在msgp指向的地址,msgsz指定msgbuf的mtext成员的长度(即消息内容的长度),msgtyp为请求读取的消息类型;读消息标志msgflg可以为以下几个常值的或:
IPC_NOWAIT
如果没有满足条件的消息,调用立即返回,此时,errno=ENOMSGIPC_EXCEPT
与msgtyp>0配合使用,返回队列中第一个类型不为msgtyp的消息IPC_NOERROR
如果队列中满足条件的消息内容大于所请求的msgsz字节,则把该消息截断,截断部分将丢失。
msgrcv手册中详细给出了消息类型取不同值时(>0;
<0; =0),调用将返回消息队列中的哪个消息。
msgrcv()解除阻塞的条件有三个:
消息队列中有了满足条件的消息;
msqid代表的消息队列被删除;
调用msgrcv()的进程被信号中断;
调用返回:成功返回读出消息的实际字节数,否则返回-1。
3)int
msgsnd(int msqid, struct msgbuf *msgp, int msgsz, int msgflg);
向msgid代表的消息队列发送一个消息,即将发送的消息存储在msgp指向的msgbuf结构中,消息的大小由msgze指定。
对发送消息来说,有意义的msgflg标志为IPC_NOWAIT,指明在消息队列没有足够空间容纳要发送的消息时,msgsnd是否等待。造成msgsnd()等待的条件有两种:
当前消息的大小与当前消息队列中的字节数之和超过了消息队列的总容量;
当前消息队列的消息数(单位"个")不小于消息队列的总容量(单位"字节数"),此时,虽然消息队列中的消息数目很多,但基本上都只有一个字节。
msgsnd()解除阻塞的条件有三个:
不满足上述两个条件,即消息队列中有容纳该消息的空间;
msqid代表的消息队列被删除;
调用msgsnd()的进程被信号中断;
调用返回:成功返回0,否则返回-1。
4)int
msgctl(int msqid, int cmd, struct msqid_ds *buf);
该系统调用对由msqid标识的消息队列执行cmd操作,共有三种cmd操作:IPC_STAT、IPC_SET
、IPC_RMID。
IPC_STAT:该命令用来获取消息队列信息,返回的信息存贮在buf指向的msqid结构中;
IPC_SET:该命令用来设置消息队列的属性,要设置的属性存储在buf指向的msqid结构中;可设置属性包括:msg_perm.uid、msg_perm.gid、msg_perm.mode以及msg_qbytes,同时,也影响msg_ctime成员。
IPC_RMID:删除msqid标识的消息队列;
调用返回:成功返回0,否则返回-1。
代码如下:
#include
<stdio.h>
#include
<stdlib.h>
#include
<unistd.h>
#include
<sys/ipc.h>
#include
<sys/msg.h>
#include
<sys/types.h>
#define
MSG_R 0400
#define
MSG_W 0200
#define
SVMSG_MODE (MSG_R | MSG_W | MSG_R >>3 | MSG_R >>6)
struct
msgbuf
{
long
mtype;
char
mtext[1];
};
void
system_v_test()
{
int
msqid;
struct
msqid_ds info;
struct
msgbuf buf;
msqid=msgget(IPC_PRIVATE,SVMSG_MODE
| IPC_CREAT);
buf.mtype=1;
buf.mtext[0]=1;
msgsnd(msqid,&buf,1,0);
msgctl(msqid,IPC_STAT,&info);
printf("read-write:%3o,cbytes=%lu,qnum=%lu,qbytes=%lu\n",info.msg_perm.mode
&
0777,(ulong)info.msg_cbytes,(ulong)info.msg_qnum,(ulong)info.msg_qbytes);
msgctl(msqid,IPC_RMID,NULL);
}
运行结果: