消息队列就是一个消息的链表。可以把消息看作一个记录,具有特定的格式以及特定的优先级。对消息队列有写权限的进程可以向消息队列中按照一定的规则添加新消息;对消息队列有读权限的进程则可以从消息队列中读走消息。消息队列是随内核持续的。
使用函数(msgget、msgctl、msgsnd、msgrcv)
拓展函数(ftok)
1.ftok
系统建立IPC通讯 (消息队列、信号量和共享内存) 时必须指定一个ID值。通常情况下,该id值通过ftok函数得到。 |
||
所需头文件 |
#include <sys/types.h>
#include <sys/ipc.h>
|
|
函数说明 |
得到消息队列标识符或创建一个消息队列对象并返回消息队列标识符 |
|
函数原型 |
key_t ftok( const char * fname, int id ) |
|
函数传入值 |
fname |
指定的文件名(已经存在的文件名) |
id |
子序号。虽然是int类型,但是只使用8bits(1-255)。 |
|
|
||
函数返回值 |
成功:返回IPC键值 |
|
出错:-1,错误原因存于error中 |
||
附加说明 |
在一般的UNIX实现中,是将文件的索引节点号取出,前面加上子序号得到key_t的返回值。 如指定文件的索引节点号为65538,换算成16进制为0x010002,而你指定的ID值为38,换算成16进制为0x26,则最后的key_t返回值为0x26010002。 ls -i 查看索引节点号 |
|
|
2.struct msqid_ds
消息队列状态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 */
__kernel_ipc_pid_t msg_lrpid; /* last receive pid */
};
3. msgget
msgget(得到消息队列标识符或创建一个消息队列对象) |
||
所需头文件 |
#include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> |
|
函数说明 |
得到消息队列标识符或创建一个消息队列对象并返回消息队列标识符 |
|
函数原型 |
int msgget(key_t key, int msgflg) |
|
函数传入值 |
key |
0(IPC_PRIVATE):会建立新的消息队列 |
大于0的32位整数:视参数msgflg来确定操作。通常要求此值来源于ftok返回的IPC键值 |
||
msgflg |
0:取消息队列标识符,若不存在则函数会报错 |
|
IPC_CREAT:当msgflg&IPC_CREAT为真时,如果内核中不存在键值与key相等的消息队列,则新建一个消息队列;如果存在这样的消息队列,返回此消息队列的标识符 |
||
IPC_CREAT|IPC_EXCL:如果内核中不存在键值与key相等的消息队列,则新建一个消息队列;如果存在这样的消息队列则报错 |
||
函数返回值 |
成功:返回消息队列的标识符 |
|
出错:-1,错误原因存于error中 |
||
附加说明 |
上述msgflg参数为模式标志参数,使用时需要与IPC对象存取权限(如0600)进行|运算来确定消息队列的存取权限 |
|
错误代码 |
EACCES:指定的消息队列已存在,但调用进程没有权限访问它 EEXIST:key指定的消息队列已存在,而msgflg中同时指定IPC_CREAT和IPC_EXCL标志 ENOENT:key指定的消息队列不存在同时msgflg中没有指定IPC_CREAT标志 ENOMEM:需要建立消息队列,但内存不足 ENOSPC:需要建立消息队列,但已达到系统的限制 |
如果用msgget创建了一个新的消息队列对象时,则msqid_ds结构成员变量的值设置如下:
msg_qnum、msg_lspid、msg_lrpid、 msg_stime、msg_rtime设置为0。
msg_ctime设置为当前时间。
msg_qbytes设成系统的限制值。
msgflg的读写权限写入msg_perm.mode中。
msg_perm结构的uid和cuid成员被设置成当前进程的有效用户ID,gid和cuid成员被设置成当前进程的有效组ID。
4.msgctl
msgctl (获取和设置消息队列的属性) |
||
所需头文件 |
#include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> |
|
函数说明 |
获取和设置消息队列的属性 |
|
函数原型 |
int msgctl(int msqid, int cmd, struct msqid_ds *buf) |
|
函数传入值 |
msqid |
消息队列标识符 |
cmd
|
IPC_STAT:获得msgid的消息队列头数据到buf中 |
|
IPC_SET:设置消息队列的属性,要设置的属性需先存储在buf中,可设置的属性包括:msg_perm.uid、msg_perm.gid、msg_perm.mode以及msg_qbytes |
||
buf:消息队列管理结构体,请参见消息队列内核结构说明部分 |
||
函数返回值 |
成功:0 |
|
出错:-1,错误原因存于error中 |
||
错误代码 |
EACCESS:参数cmd为IPC_STAT,确无权限读取该消息队列 EFAULT:参数buf指向无效的内存地址 EIDRM:标识符为msqid的消息队列已被删除 EINVAL:无效的参数cmd或msqid EPERM:参数cmd为IPC_SET或IPC_RMID,却无足够的权限执行 |
5.msgsnd
msgsnd (将消息写入到消息队列) |
||
所需头文件 |
#include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> |
|
函数说明 |
将msgp消息写入到标识符为msqid的消息队列 |
|
函数原型 |
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg) |
|
函数传入值 |
msqid |
消息队列标识符 |
msgp |
发送给队列的消息。msgp可以是任何类型的结构体,但第一个字段必须为long类型,即表明此发送消息的类型,msgrcv根据此接收消息。msgp定义的参照格式如下: struct s_msg{ /*msgp定义的参照格式*/ |
|
msgsz |
要发送消息的大小,不含消息类型占用的4个字节,即mtext的长度 |
|
msgflg |
0:当消息队列满时,msgsnd将会阻塞,直到消息能写进消息队列 |
|
IPC_NOWAIT:当消息队列已满的时候,msgsnd函数不等待立即返回 |
||
IPC_NOERROR:若发送的消息大于size字节,则把该消息截断,截断部分将被丢弃,且不通知发送进程。 |
||
函数返回值 |
成功:0 |
|
出错:-1,错误原因存于error中 |
||
错误代码 |
EAGAIN:参数msgflg设为IPC_NOWAIT,而消息队列已满 EIDRM:标识符为msqid的消息队列已被删除 EACCESS:无权限写入消息队列 EFAULT:参数msgp指向无效的内存地址 EINTR:队列已满而处于等待情况下被信号中断 EINVAL:无效的参数msqid、msgsz或参数消息类型type小于0 |
msgsnd()为阻塞函数,当消息队列容量满或消息个数满会阻塞。消息队列已被删除,则返回EIDRM错误;被信号中断返回E_INTR错误。
如果设置IPC_NOWAIT消息队列满或个数满时会返回-1,并且置EAGAIN错误。
msgsnd()解除阻塞的条件有以下三个条件:
① 不满足消息队列满或个数满两个条件,即消息队列中有容纳该消息的空间。
② msqid代表的消息队列被删除。
③ 调用msgsnd函数的进程被信号中断。
6.msgrcv
msgrcv (从消息队列读取消息) |
||
所需头文件 |
#include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> |
|
函数说明 |
从标识符为msqid的消息队列读取消息并存于msgp中,读取后把此消息从消息队列中删除 |
|
函数原型 |
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg); |
|
函数传入值 |
msqid |
消息队列标识符 |
msgp |
存放消息的结构体,结构体类型要与msgsnd函数发送的类型相同 |
|
msgsz |
要接收消息的大小,不含消息类型占用的4个字节 |
|
msgtyp |
0:接收第一个消息 |
|
>0:接收类型等于msgtyp的第一个消息 |
||
<0:接收类型等于或者小于msgtyp绝对值的第一个消息 |
||
msgflg |
0: 阻塞式接收消息,没有该类型的消息msgrcv函数一直阻塞等待 |
|
IPC_NOWAIT:如果没有返回条件的消息调用立即返回,此时错误码为ENOMSG |
||
IPC_EXCEPT:与msgtype配合使用返回队列中第一个类型不为msgtype的消息 |
||
IPC_NOERROR:如果队列中满足条件的消息内容大于所请求的size字节,则把该消息截断,截断部分将被丢弃 |
||
函数返回值 |
成功:实际读取到的消息数据长度 |
|
出错:-1,错误原因存于error中 |
||
错误代码 |
E2BIG:消息数据长度大于msgsz而msgflag没有设置IPC_NOERROR EIDRM:标识符为msqid的消息队列已被删除 EACCESS:无权限读取该消息队列 EFAULT:参数msgp指向无效的内存地址 ENOMSG:参数msgflg设为IPC_NOWAIT,而消息队列中无消息可读 EINTR:等待读取队列内的消息情况下被信号中断 |
msgrcv()解除阻塞的条件有以下三个:
① 消息队列中有了满足条件的消息。
② msqid代表的消息队列被删除。
③ 调用msgrcv()的进程被信号中断。
msgsend.c
#include <stdio.h> #include <string.h> #include <unistd.h> #include <sys/ipc.h> #include <sys/msg.h> #include <time.h> #define TEXT_SIZE 512 struct msgbuf { long mtype ; int status ; char mtext[TEXT_SIZE] ; }; int main(int argc, char **argv) { int msqid ; struct msqid_ds info ; //消息队列状态 struct msgbuf buf ; int flag ; int sendlength; int key ; key = ftok("msg.tmp", 0x01 ) ; if (key < 0) { perror("ftok key error") ; return -1 ; } msqid = msgget( key, 0600|IPC_CREAT ) ; if ( msqid < 0 ) { perror("create message queue error") ; return -1 ; } flag = msgctl(msqid, IPC_STAT, &info); if ( flag < 0) { perror("msgctl message error") ; return -1 ; } buf.mtype = 1 ; buf.status = 9 ; strcpy(buf.mtext, "hello!") ; sendlength = sizeof(struct msgbuf) - sizeof(long) ; flag = msgsnd( msqid, &buf, sendlength , 0 ) ; if ( flag < 0 ) { perror("send message error") ; return -1 ; } buf.mtype = 3 ; buf.status = 9 ; strcpy(buf.mtext, "bye!") ; sendlength = sizeof(struct msgbuf) - sizeof(long) ; flag = msgsnd( msqid, &buf, sendlength , 0 ) ; if ( flag < 0 ) { perror("send message error") ; return -1 ; } system("ipcs -q") ; return 0 ; }
msgrecv.c
#include <stdio.h> #include <string.h> #include <unistd.h> #include <sys/ipc.h> #include <sys/msg.h> #define TEXT_SIZE 512 struct msgbuf { long mtype ; int status ; char mtext[TEXT_SIZE] ; }; int main(int argc, char **argv) { int msqid ; struct msgbuf buf ; int flag ; int recvlength ; int key ; key = ftok("msg.tmp", 0x01 ) ; if ( key < 0 ) { perror("ftok key error") ; return -1 ; } msqid = msgget( key, 0 ) ; if ( msqid < 0 ) { perror("get ipc_id error") ; return -1 ; } recvlength = sizeof(struct msgbuf) - sizeof(long) ; memset(&buf, 0x00, sizeof(struct msgbuf)) ; flag = msgrcv( msqid, &buf, recvlength , 0, 0) ; if ( flag < 0 ) { perror("recv message error ") ; return -1 ; } printf("type=%d, message=%s ", buf.mtype, buf.mtext) ; system("ipcs -q") ; return 0 ; }