消息队列是消息的连接表,存放在内核中并由消息队列标识符标识。在本节中,咱们把消息队列简称为队列(queue),其标识符为队列ID(queue ID)。html
msgget用于建立一个新队列或打开一个现存的队列。msgsnd将新消息添加到队列尾端。每一个消息包含一个正长整型类型字段,一个非负长度以及实际数据字节(对应于长度),全部这些都在将消息添加到队列时,传送给msgsnd。msgrcv用于从队列中取消息。咱们并不必定要以先进先出次序取消息,也能够按消息的类型字段取消息。编程
每一个队列都有一个msgqid_ds结构与其相关联:服务器
struct msqid_ds { struct ipc_perm msg_perm; /* http://www.cnblogs.com/nufangrensheng/p/3561681.html */ msgqnum_t msg_qnum; /* # of messages on queue */ msglen_t msg_qbytes; /* max # of bytes no queue */ pid_t msg_lspid; /* pid of last msgsnd() */ pid_t msg_lrpid; /* pid of last msgrcv() */ time_t msg_stime; /* last-msgsnd() time */ time_t msg_rtime; /* last-msgrcv() time */ time_t msg_ctime; /* last-change time */ ... };
此结构规定了队列的当前状态。结构中所示的各成员是由Single UNIX Specification定义的。具体实现可能包括标准中没有定义的另外一些字段。函数
调用的第一个函数一般是msgget,其功能是打开一个现存队列或建立一个新队列。学习
#include <sys/msg.h> int msgget(key_t key, int flag); 返回值:若成功则返回消息队列ID,若出错则返回-1
http://www.cnblogs.com/nufangrensheng/p/3561681.html中标识符和键部分,说明了将key变换成一个标识符的规则,而且讨论是否建立一个新队列或访问一个现存队列。ui
当建立一个新队列时,初始化msqid_ds结构的下列成员:spa
若执行成功,msgget返回非负队列ID。此后,该值就可被用于其余三个消息队列函数(msgsnd、msgrcv和msgctl)。指针
msgctl函数对队列执行多种操做。它和另外两个与信号量和共享存储有关的函数(semctl和shmctl)是XSI IPC的相似于ioctl的函数(亦即垃圾桶函数)。code
#include <sys/msg.h> int msgctl(int msqid, int cmd, struct msqid_ds *buf); 返回值:若成功则返回0,若出错则返回-1
cmd参数说明对由msqid指定的队列要执行的命令:htm
IPC_STAT 取此队列的msqid_ds结构,并将它存放在buf指向的结构中。
IPC_SET 按由buf指定结构中的值,设置与此队列相关结构中的下列四个字段:msg_perm.uid、msg_perm.gid、msg_perm.mode和msg_qbytes。此命令只能由下列两种进程执行:一种是其有效用户ID等于msg_perm.cuid或msg_perm.uid;另外一种是具备超级用户特权的进程。只有超级用户才能增长msg_qbytes的值。
IPC_RMID 从系统中删除该消息队列以及仍在该队列中的全部数据。这种删除当即生效。仍在使用这一消息队列的其余进程在它们下一次试图对此队列进行操做时,将出错返回EIDRM。此命令只能由下列两种进程执行:一种是其有效用户ID等于msg_perm.cuid或msg_perm.uid;另外一种是具备超级用户特权的进程。
这三条命令(IPC_STAT、IPC_STAT和IPC_RMID)也可用于信号量和共享存储。
调用msgsnd将数据放到消息队列中。
#include <sys/msg.h> int msgsnd(int msqid, const void *ptr, size_t nbytes, int flag); 返回值:若成功则返回0,若出错则返回-1
每一个消息都由三部分组成,它们是:正长整型类型字段、非负长度(nbytes)以及实际数据字节(对应于长度)。消息老是放在队列尾端。
ptr参数指向一个长整型数,它包含了正的整型消息类型,在其后紧跟着消息数据。(若nbytes是0,则无消息数据。)若发送的最长消息是512字节,则可定义下列结构:
struct mymesg { long mtype; /* positive message type */ char mtext[512]; /* message data, of length nbytes */ };
因而,ptr就是一个指向mymesg结构的指针。接收者可使用消息类型以非先进先出的次序取消息。
参数flag的值能够指定为IPC_NOWAIT。这相似于文件I/O的非阻塞I/O标志(见http://www.cnblogs.com/nufangrensheng/p/3544997.html)。若消息队列已满(或者是队列中的消息总数等于系统限制值,或队列中的字节总数等于系统限制值),则指定IPC_NOWAIT使得msgsnd当即出错返回EAGAIN。若是没有指定IPC_NOWAIT,则进程阻塞直到下述状况出现为止:有空间能够容纳要发送的消息;从系统中删除了此队列;或捕捉到一个信号,并从信号处理程序返回。在第二种状况下,返回EIDRM(“标识符被删除”)。最后一种状况则返回EINTR。
注意,对删除消息队列的处理不是很完善。由于对每一个消息队列并无设置一个引用计数器(对打开文件则有这种计数器),因此删除一个队列会形成仍在使用这一队列的进程在下次对队列进行操做时出错返回。信号量机制也以一样的方式处理其删除。相反,删除一个文件时,要等到使用该文件的最后一个进程关闭了它的文件描述符后,才能删除文件中的内容。
当msgsnd成功返回,与消息队列相关的msqid_ds结构获得更新,以代表发出该调用的进程ID(msg_lspid)、进行该调用的时间(msg_stime),并指示队列中增长了一条消息(msg_qnum)。
msgrcv从队列中取用消息:
#include <sys/msg.h> ssize_t msgrcv(int msqid, void *ptr, size_t nbytes, long type, int flag); 返回值:若成功则返回消息的数据部分的长度,若出错则返回-1
如同msgsnd中同样,ptr参数指向一个长整型数(返回的消息类型存放在其中),跟随其后的是存放实际消息数据的缓冲区。nbytes说明数据缓冲区的长度。若返回的消息大于nbytes,并且在flag中设置了MSG_NOERROR,则该消息被截短。(在这种状况下,不通知咱们消息截短了,消息的截去部分被丢弃。)若是没有设置这一标志,而消息又太长,则出错返回E2BIG(消息仍留在队列中)。
参数type使咱们能够指定想要哪种消息:
type == 0 返回队列中的第一个消息。
type > 0 返回队列中消息类型为type的第一个消息。
type < 0 返回队列中消息类型值小于或等于type绝对值的消息,若是这种消息有若干个,则取类型值最小的消息。
type值非0用于以非先进先出次序读消息。例如,若应用程序对消息赋优先权,那么type就能够是优先权值。若是一个消息队列由多个客户进程和一个服务器进程使用,那么type字段能够用来包含客户进程的进程ID(只要进程ID能够存放在长整型中)。
能够指定flag值为IPC_NOWAIT,使操做不阻塞。这使得若是没有所指定类型的消息,则msgrcv返回-1,errno设置为ENOMSG。若是没有指定IPC_NOWAIT,则进程阻塞直至以下状况出现才终止:有了指定类型的消息;从系统中删除了此队列(出错则返回-1且errno设置为EIDRM);或捕捉到一个信号并从信号处理程序返回(msgrcv返回-1,errno设置为EINTR)。
msgrcv成功执行时,内核更新与该消息队列相关联的msqid_ds结构,以指示调用者的进程ID(msg_lrpid)和调用时间(msg_rtime),并将队列中的消息数(msg_qnum)减1。
消息队列原来的实施目的是提供比通常IPC更高速度的进程通讯方法,但如今与其余形式的IPC相比,在速度方面已经没有什么差异了。考虑到使用消息队列具备的问题(见http://www.cnblogs.com/nufangrensheng/p/3561681.html中优势和缺点部分),咱们得出的结论是,在新的应用程序中不该当再使用它们。
本篇博文内容摘自《UNIX环境高级编程》(第二版),仅做我的学习记录所用。关于本书可参考:http://www.apuebook.com/。