当前位置:C++技术网 > 资讯 > Linux编程:12 消息队列函数msgget、msgsnd等函数说明及其代码使用示例

Linux编程:12 消息队列函数msgget、msgsnd等函数说明及其代码使用示例

更新时间:2017-05-23 09:38:44浏览次数:1+次

    消息队列函数由msgget、msgctl、msgsnd、msgrcv四个函数组成。下面的表格列出了这四个函数的函数原型及其具体说明。
1.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。
2.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,却无足够的权限执行
3.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定义的参照格式*/
    long type; /* 必须大于0,消息类型 */
    char mtext[256]; /*消息正文,可以是其他任何类型*/
}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函数的进程被信号中断。

4.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()的进程被信号中断。

消息队列使用程序范例
5.   消息队列控制范例
msgctl.c源代码如下:
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <error.h>

#define TEXT_SIZE 512
struct msgbuf
{
 long mtype;
 char mtext[TEXT_SIZE];
};

int main(int argc, char **argv)
{
 int msqid ;
 struct msqid_ds info ;
 struct msgbuf buf ;
 struct msgbuf buf1 ;
 int flag ;
 int sendlength, recvlength ;
 msqid = msgget( IPC_PRIVATE, 0666 ) ;
 if ( msqid < 0 )
 {
 perror("get ipc_id error") ;
 return -1 ;
 }
 buf.mtype = 1 ;
 strcpy(buf.mtext, "happy new year!") ;
 sendlength = sizeof(struct msgbuf) - sizeof(long) ;
 flag = msgsnd( msqid, &buf, sendlength , 0 ) ;
 if ( flag < 0 )
 {
 perror("send message error") ;
 return -1 ;
 }
 buf.mtype = 3 ;
 strcpy(buf.mtext, "good bye!") ;
 sendlength = sizeof(struct msgbuf) - sizeof(long) ;
 flag = msgsnd( msqid, &buf, sendlength , 0 ) ;
 if ( flag < 0 )
 {
 perror("send message error") ;
 return -1 ;
 }
 flag = msgctl( msqid, IPC_STAT, &info ) ;
 if ( flag < 0 )
 {
 perror("get message status error") ;
 return -1 ;
 }
 printf("uid:%d, gid = %d, cuid = %d, cgid= %d\n" ,
 info.msg_perm.uid, info.msg_perm.gid, info.msg_perm.cuid, info.msg_perm.cgid ) ;
 printf("read-write:%03o, cbytes = %lu, qnum = %lu, qbytes= %lu\n" ,
 info.msg_perm.mode&0777, info.msg_cbytes, info.msg_qnum, info.msg_qbytes ) ;
 system("ipcs -q") ;
 recvlength = sizeof(struct msgbuf) - sizeof(long) ;
 memset(&buf1, 0x00, sizeof(struct msgbuf)) ;
 flag = msgrcv( msqid, &buf1, recvlength ,3,0 ) ;
 if ( flag < 0 )
 {
 perror("recv message error") ;
 return -1 ;
 }
 printf("type=%d, message=%s\n", buf1.mtype, buf1.mtext) ;
 flag = msgctl( msqid, IPC_RMID,NULL) ;
 if ( flag < 0 )
 {
 perror("rm message queue error") ;
 return -1 ;
 }
 system("ipcs -q") ;
 return 0 ;
}

编译 gcc msgctl.c –o msgctl。
执行 ./msg,执行结果如下:
uid:1008, gid = 1003, cuid = 1008, cgid= 1003
read-write:666, cbytes = 1024, qnum = 2, qbytes= 163840
------ Message Queues --------
key        msqid      owner      perms      used-bytes   messages   
0x00000000 65536      zjkf       666        1024         2          

type=3, message=good bye!

------ Message Queues --------
key        msqid      owner      perms      used-bytes   messages
 
6.两进程通过消息队列收发消息
(1)发送消息队列程序
msgsnd.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 time[20];
 char mtext[TEXT_SIZE];
};

char *getxtsj()
{
 time_t tv ;
 struct tm *tmp ;
 static char buf[20] ;
 tv = time( 0 ) ;
 tmp = localtime(&tv) ;
 sprintf(buf,"%02d:%02d:%02d",tmp->tm_hour , tmp->tm_min,tmp->tm_sec);
 return buf ;
}

int main(int argc, char **argv)
{
 int msqid ;
 struct msqid_ds info ;
 struct msgbuf buf ;
 struct msgbuf buf1 ;
 int flag ;
 int sendlength, recvlength ;
 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 ;
 }

 buf.mtype = 1 ;
 buf.status = 9 ;
 strcpy(buf.time, getxtsj()) ;
 strcpy(buf.mtext, "happy new year!") ;
 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.time, getxtsj()) ;
 strcpy(buf.mtext, "good 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 ;
}


(2)接收消息队列程序
msgrcv.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 time[20] ;
 char mtext[TEXT_SIZE] ;
};

int main(int argc, char **argv)
{
 int msqid ;
 struct msqid_ds info ;
 struct msgbuf buf1 ;
 int flag ;
 int recvlength ;
 int key ;
 int mtype ;
 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(&buf1, 0x00, sizeof(struct msgbuf)) ;
 mtype = 1 ;
 flag = msgrcv( msqid, &buf1, recvlength ,mtype,0 ) ;
 if ( flag < 0 )
 {
 perror("recv message error\n") ;
 return -1 ;
 }
 printf("type=%d,time=%s, message=%s\n", buf1.mtype, buf1.time, buf1.mtext) ;
 system("ipcs -q") ;
 return 0 ;
}


(3)编译与执行程序
①在当前目录下利用>msg.tmp建立空文件msg.tmp。
②编译发送消息队列程序 gcc msgsnd.c -o  msgsnd。
③执行./msgsnd,执行结果如下:
----- Message Queues --------
key        msqid      owner      perms      used-bytes   messages   
0x0101436d 294912     zjkf       600        1072         2          

④编译接收消息程序 gcc msgrcv.c -o msgrcv
⑤执行./msgrcv,执行结果如下:
type=1,time=03:23:16, message=happy new year!

------ Message Queues --------
key        msqid      owner      perms      used-bytes   messages   

0x0101436d 294912     zjkf       600        536          1         

⑥利用ipcrm -q 294912删除该消息队列。因为消息队列是随内核持续存在的,在程序中若不利用msgctl函数或在命令行用ipcrm命令显式地删除,该消息队列就一直存在于系统中。另外信号量和共享内存也是随内核持续存在的。

摘录自《深入浅出Linux工具与编程》