Linux进程间通信(IPC)编程实践(四) 详解System V消息队列(2)(msgsnd & msgrcv)

Linux基础及编程 专栏收录该内容
71 篇文章 3 订阅

本文主要介绍消息队列发送/接收消息的API:

msgsnd函数

[cpp]  view plain copy
  1. int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);   

参数

   msgid: 由msgget函数返回的消息队列标识码, 也可以是通过ipcs命令查询出来的一个已经存在的消息队列的ID号

   msgp:是一个指针,指针指向准备发送的消息,

   msgsz:是msgp指向的消息长度, 注意: 这个长度不含保存消息类型的那个long int长整型

   msgflg:控制着当前消息队列满到达系统上限时将要发生的事情,如果msgflg = IPC_NOWAIT表示队列满不等待,返回EAGAIN错误。

   另外:消息结构在两方面受到制约: 

(1)它必须小于系统规定的上限值(MSGMAX); 

(2)必须以一个long int长整数开始,接收者函数将利用这个长整数确定消息的类型;

[cpp]  view plain copy
  1. /消息结构参考形式如下:    
  2. struct msgbuf    
  3. {    
  4.     long mtype;       /* message type, must be > 0 */    
  5.     char mtext[1];    /* message data, 可以设定为更多的字节数 */    
  6. };    
[cpp]  view plain copy
  1. /**示例1:   
  2. 测试1: 发送消息的最大长度为8192字节, 一旦超过这个值, 则msgsnd出错, 提示 Invalid argument错误;  
  3. 测试2: 消息队列所能够接收的最大字节数16384字节, 一旦超过这个长度, 如果msgflg为0(阻塞模式), 则进程会一直阻塞下去, 直到有进程来将消息取走; 而如果msgflg为IPC_NOWAIT模式, 则一个字节也不会写入消息队列, 直接出错返回;  
  4. **/    
  5. int main(int argc, char *argv[])    
  6. {    
  7.     //argv[0]=./main ,argv[1]=type,argv[2]=len  
  8.     if (argc != 3)    
  9.         err_quit("Usage: ./main <type> <length>");    
  10.     
  11.     int type = atoi(argv[1]);    
  12.     int len = atoi(argv[2]);    
  13.     
  14.     int msgid = msgget(0x255, 0666|IPC_CREAT);    
  15.     if (msgid == -1)    
  16.         err_exit("msgget error");    
  17.     
  18.     struct msgbuf *buf;    
  19.     buf = (struct msgbuf *)malloc(len + sizeof(msgbuf::mtype));    
  20.     buf->mtype = type;    
  21.     if (msgsnd(msgid, buf, len, IPC_NOWAIT) == -1)  //非阻塞   
  22.         err_exit("msgsnd error");    
  23. }    
msgrcv函数

[cpp]  view plain copy
  1. ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);  

参数

   msgid: 由msgget函数返回的消息队列标识码

   msgp:是一个指针,指针指向准备接收的消息;

   msgsz:是msgp指向的消息长度,这个长度不含保存消息类型的那个long int长整型

   msgtype:它可以实现接收优先级的简单形式(见下图)

   msgflg:控制着队列中没有相应类型的消息可供接收时将要发生的事(见下图)

返回值:

   成功->返回实际放到接收缓冲区里去的字节数(注意: 此处并不包含msgbuf中的mtype的长度[man-page: msgrcv() returns the number of bytes actually copied into the mtext array.]);

   失败->返回-1;

msgtyp参数

msgtyp=0

返回队列第一条信息

msgtyp>0

返回队列第一条类型等于msgtype的消息

msgtyp<0

返回队列第一条类型小于等于(<=)msgtype绝对值的消息,并且是满足条件的消息类型最小的消息(按照类型进行排序的顺序进行接收消息)

 

msgflg参数

msgflg=IPC_NOWAIT

队列没有可读消息不等待,返回ENOMSG错误。

msgflg=MSG_NOERROR

消息大小超过msgsz(msgrcv 函数的第三个参数)时被截断, 并且不会报错

msgtyp>0且msgflg=MSG_EXCEPT

接收类型不等于msgtype的第一条消息


另外,下面的接受程序中,我们会用到一个用于 用命令行执行程序时的指定不同参数的函数getopt

我们先介绍一下这个函数的基本用法:

./main 1 234 -c -d

程序会根据读取的参数执行相应的操作,在C语言中,这个功能一般是靠getopt()这个函数,结合switch语句来完成的

[cpp]  view plain copy
  1. #include <unistd.h>    
  2. int getopt(int argc, char * const argv[],    
  3.                   const char *optstring);    
  4.     
  5. extern char *optarg;    
  6. extern int optind, opterr, optopt;    
  7.   
  8.   
  9. //示例: 解析 ./main -n -t 3 中的参数选项    
  10. int main(int argc, char *argv[])    
  11. {    
  12.     while (true)    
  13.     {    
  14.         int opt = getopt(argc, argv, "nt:");    
  15.         if (opt == '?')    
  16.             exit(EXIT_FAILURE);    
  17.         else if (opt == -1)    
  18.             break;    
  19.     
  20.         switch (opt)    
  21.         {    
  22.         case 'n':    
  23.             cout << "-n" << endl;    
  24.             break;    
  25.         case 't':    
  26.             int n = atoi(optarg);    
  27.             cout << "-t " << n << endl;    
  28.             break;    
  29.         }    
  30.     }    
  31. }    

例如参数"a:b::cde",表示可以有,-a,-b,-c,-d,-e这几个参数。

:”表示必须该选项带有额外的参数,全域变量optarg会指向此额外参数,“::”标识该额外的参数可选(有些Uinx可能不支持“::”)。

全域变量optind指示下一个要读取的参数在argv中的位置。

如果getopt()找不到符合的参数则会印出错信息,并将全域变量optopt设为“?”字符。

如果不希望getopt()印出错信息,则只要将全域变量opterr设为0即可。

[cpp]  view plain copy
  1. /** 示例2: 消息接收(配合示例1中程序使用)  
  2. 说明:     -t [number], 指定接收消息的类型, 类型为number的值  
  3. -n ,指定以IPC_NOWAIT模式接收消息  
  4. **/    
  5. int main(int argc, char *argv[])    
  6. {    
  7.     /** 解析参数 **/    
  8.     int type = 0;    
  9.     int flag = 0;    
  10.     int opt;    
  11.     while ((opt = getopt(argc, argv, "nt:")) != -1)    
  12.     {    
  13.         switch (opt)    
  14.         {    
  15.         case 'n':   // 指定IPC_NOWAIT选项    
  16.             flag |= IPC_NOWAIT;    
  17.             break;    
  18.         case 't':   // 指定接收的类型, 如果为0的话,说明是按照顺序接收    
  19.             type = atoi(optarg);    
  20.             break;    
  21.         default:    
  22.             exit(EXIT_FAILURE);    
  23.         }    
  24.     }    
  25.     
  26.     int msgid = msgget(0x255, 0);    
  27.     if (msgid == -1)    
  28.         err_exit("msgget error");    
  29.     
  30.     const int MSGMAX = 8192;    //指定一条消息的最大长度    
  31.     struct msgbuf *buf;    
  32.     buf = (struct msgbuf *)malloc(MSGMAX + sizeof(buf->mtype));    
  33.     
  34.     ssize_t nrcv;    
  35.     if ((nrcv = msgrcv(msgid, buf, MSGMAX, type, flag)) == -1)    
  36.         err_exit("msgrcv error");    
  37.     cout << "recv " << nrcv << " bytes, type = " << buf->mtype << endl;    
  38. }    

对于接收时的优先级,我们可以根据截图来理解:


从消息队列中发送/读取 消息:

[cpp]  view plain copy
  1. /** 综合示例: msgsnd/msgrcv, 消息发送/接收实践 **/    
  2. //1. 消息发送    
  3. int main()    
  4. {    
  5.     int msgid = msgget(0x1234,0666|IPC_CREAT);    
  6.     if (msgid == -1)    
  7.         err_exit("msgget error");    
  8.     
  9.     struct msgBuf myBuffer;    
  10.     for (int i = 0; i < 128; ++i)    
  11.     {    
  12.         myBuffer.mtype = i+1;    
  13.         sprintf(myBuffer.mtext,"Hello, My number is %d",i+1);    
  14.         if (msgsnd(msgid,&myBuffer,strlen(myBuffer.mtext),IPC_NOWAIT) == -1)    
  15.             err_exit("msgsnd error");    
  16.     }    
  17. }    

[cpp]  view plain copy
  1. //2. 消息接收:从队首不断的取数据    
  2. int main(int argc, char *argv[])    
  3. {    
  4.     int msgid = msgget(0x1234, 0);    
  5.     if (msgid == -1)    
  6.         err_exit("msgget error");    
  7.     
  8.     struct msgBuf buf;    
  9.     ssize_t nrcv;    
  10.     while ((nrcv = msgrcv(msgid, &buf, sizeof(buf.mtext), 0, IPC_NOWAIT)) > 0)    
  11.     {    
  12.         cout << "recv " << nrcv << " bytes, type: " << buf.mtype    
  13.         << ", message: " << buf.mtext << endl;    
  14.     }    
  15. }    
  • 3
    点赞
  • 0
    评论
  • 1
    收藏
  • 打赏
    打赏
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页

打赏作者

NK_test

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值