消息队列函数(msggetmsgctlmsgsndmsgrcv)

Posted z_fishLong

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了消息队列函数(msggetmsgctlmsgsndmsgrcv)相关的知识,希望对你有一定的参考价值。

消息队列函数由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()的进程被信号中断。


示例


#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    //结构体 -->msgsend/msgrcv

    long mtype ;
    char mtext[TEXT_SIZE] ;
;


//主函数
int main(int argc, char **argv)

    int msqid ;
    struct msqid_ds info ;   //msqid_ds 头文件中
    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 ); //获取消息队列状态 获得的内容保存在info里面
    if ( flag < 0 )
    
        perror("get message status error") ;
        return -1 ;
    
    
    //打印 info里面保存消息队列的信息
    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 ) ;
    
    //该命令是显示Linux系统下所有的共享内存
    system("ipcs -q") ;
    
    
    //演示接受信息
    recvlength = sizeof(struct msgbuf) - sizeof(long) ;
    
    //memset-->内存空间初始化-->这里初始化buf1的内存空间
    memset(&buf1, 0x00, sizeof(struct msgbuf)) ;
 
    flag = msgrcv( msqid, &buf1, recvlength ,3,0 ) ; //开始接受信息  接受的信息保存在buf1中
    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); //IPC_RMID:删除这片共享内存 
    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.  两进程通过消息队列收发消息

#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 ;
 
 
 		//系统建立IPC通讯(如消息队列、共享内存时)必须指定一个ID值。
 		//通常情况下,该id值通过ftok函数得到。
    key = ftok("msg.tmp", 0x01 );
    
    if ( key < 0 )
    
        perror("ftok key error") ;
        return -1 ;0
    
     
    msqid = msgget( key, 0600|IPC_CREAT ) ; //获取编号为0600的消息队列,没有就创建
    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 ;

接受端

#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);    //根据key 拿到消息队列id
    if ( msqid < 0 )
    
        perror("get ipc_id error") ;
        return -1 ;
    
 
 
    recvlength = sizeof(struct msgbuf) - sizeof(long) ;
    memset(&buf1, 0x00, sizeof(struct msgbuf)) ;   //初始化buf1 的内存空间
    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) ;
    
    //输出 Linux系统的共享内存信息
    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命令显式地删除,该消息队列就一直存在于系统中。另外信号量和共享内存也是随内核持续存在的。


以上是关于消息队列函数(msggetmsgctlmsgsndmsgrcv)的主要内容,如果未能解决你的问题,请参考以下文章

SystemV消息队列

rt_thread的消息队列

进程通信之消息队列

Azure 函数队列触发器:如何设置出列消息的时间延迟

Azure 函数中绑定队列存储消息文本失败

2014025658《嵌入式系统程序设计》第七周学习总结