Linux下多任务间通信和同步-消息队列

简介

消息队列简称为队列.消息队列就是一些消息的列表.用户可以在消息队列中添加消息和读取消息等.从这点上看,消息队列具有一定的FIFO特性,但是它可以实现消息的随机查询,比FIFO具有更大的优势.同时,这些消息又是存在于内核中的,由”队列ID”来标识.

消息队列的实现包括创建或打开消息队列,添加消息,读取消息和控制消息队列这四种操作:

  1. 创建或打开消息队列使用的函数是msgget,这里创建的消息队列的数量会受到系统消息队列数量的限制;
  2. 添加消息使用的函数是msgsnd函数,它把消息添加到已打开的消息队列末尾;
  3. 读取消息使用的函数是msgrcv,它把消息从消息队列中取走,与FIFO不同的是,这里可以指定取走某一条消息;
  4. 控制消息队列使用的函数是msgctl,它可以完成多项功能.

msgget系统调用

该系统调用创建或打开一个消息队列.msgget函数语法:

其中,msgflag可以为:IPC_CREAT,IPC_EXEC,IPC_NOWAIT或者三者的组合.在一下两种情况下,该调用将创建一个新的消息队列:

 

  • 如果没有消息队列与键值key相对应,并且msgfalg中包含IPC_CREAT标志位;
  • key参数为IPC_PRIVATE.

 

msgsnd系统调用

该系统调用行msgid代表的消息队列发送一个消息.msgsnd函数语法:

对于发送消息来说,有意义的msgflag标志位IPC_NOWAIT,指明在消息队列没有足够容纳要发送的消息时,msgsnd是否等待.造成msgsnd等待的条件有两种:

 

  • 当前消息的大小与当前消息队列中的字节数之和操作了消息队列的总容量;
  • 当前消息队列中的消息数(单位:个)不小于消息队列的总容量(单位:字节数),此时,虽然消息队列中的消息数目很多,但基本上都只有一个字节.

 

msgsnd解除阻塞的条件有三个:

 

  • 不满足上述两个,即消息队列中有容纳该消息的空间
  • msqid代表的消息队列被删除
  • 调用msgsnd的进程被进程打断

 

msgrcv系统调用

该系统调用从ID为msgid的消息队列中读取一个消息,并把消息存储子啊msgq指向的msgbuf结构中.msgrcv函数语法:


msgrcv解除阻塞的条件有三个:

 

  • 消息队列中有了满足条件的消息;
  • msqid代表的消息队列被删除;
  • 调用msgrcv的进程被信号中断.

 

msgctl系统调用

该系统调用对由msqid标示的消息队列执行cmd操作.msgctl函数语法:

消息队列的应用实例

 

该例子分为两个子程序:消息接收子程序和发送子程序.接受者获取一个消息队列,这个消息队列应该是有后面的发送子程序所创建的.然后采用轮询的方式从消息队列中读取数据.

 

[plain][/plain] view plaincopyprint?

  1. /**************************************************************************************/
  2. /*简介:消息队列例程,接收者子程序                           */
  3. /*************************************************************************************/
  4. #include <stdlib.h>
  5. #include <stdio.h>
  6. #include <string.h>
  7. #include <errno.h>
  8. #include <unistd.h>
  9. #include <sys/types.h>
  10. #include <sys/ipc.h>
  11. #include <sys/msg.h>
  12. /*消息的结构*/
  13. struct my_msg_st
  14. {
  15.     long int my_msg_type;
  16.     char some_text[BUFSIZ];
  17. };
  18. int main()
  19. {
  20.     int running = 1;
  21.     int msgid;
  22.     struct my_msg_st some_data;
  23.     long int msg_to_receive = 0;
  24.     /*创建消息队列*/
  25.     msgid = msgget((key_t)1234, 0666 | IPC_CREAT);
  26.     if (msgid == -1)
  27.     {
  28.         fprintf(stderr, “msgget failed with error: %d\n”, errno);
  29.         return 1;
  30.     }
  31.     /*从队列里检索消息,直到遇见”endr“消息为止。最后,删除消息队列。*/
  32.     while(running)
  33.     {
  34.         if (msgrcv(msgid, (void *)&some_data, BUFSIZ,
  35.                    msg_to_receive, 0) == -1)
  36.     {
  37.             fprintf(stderr, “msgrcv failed with error: %d\n”, errno);
  38.             return 1;
  39.         }
  40.         printf(“You wrote: %s”, some_data.some_text);
  41.         if (strncmp(some_data.some_text, “end”, 3) == 0)
  42.     {
  43.             running = 0;
  44.         }
  45.     }
  46.     if (msgctl(msgid, IPC_RMID, 0) == -1)
  47.     {
  48.         fprintf(stderr, “msgctl(IPC_RMID) failed\n”);
  49.         return 1;
  50.     }
  51.     return 0;
  52. }

msgget获得消息队列的标识码,然后开始接收消息,直到接收到的特殊的”end”消息.发送子程序与接收子程序相似,但发送程序需要一个消息队列.与其他通信方式不同,发送子程序把消息送入消息队列后就可以退出了,并不需要等待接收子程序的读操作.发送子程序的源码如下:

 

 

[plain][/plain] view plaincopyprint?

  1. /**************************************************************************************/
  2. /*简介:消息队列例程,发送者子程序                           */
  3. /*************************************************************************************/
  4. #include <stdlib.h>
  5. #include <stdio.h>
  6. #include <string.h>
  7. #include <errno.h>
  8. #include <unistd.h>
  9. #include <sys/types.h>
  10. #include <sys/ipc.h>
  11. #include <sys/msg.h>
  12. #define MAX_TEXT 512
  13. struct my_msg_st
  14. {
  15.     long int my_msg_type;
  16.     char some_text[MAX_TEXT];
  17. };
  18. int main()
  19. {
  20.     int running = 1;
  21.     struct my_msg_st some_data;
  22.     int msgid;
  23.     char buffer[BUFSIZ];
  24.     msgid = msgget((key_t)1234, 0666 | IPC_CREAT);
  25.     if (msgid == -1)
  26.     {
  27.         fprintf(stderr, “msgget failed with error: %d\n”, errno);
  28.         return 1;
  29.     }
  30.     while(running)
  31.     {
  32.         printf(“Enter some text: “);
  33.         fgets(buffer, BUFSIZ, stdin);
  34.         some_data.my_msg_type = 1;
  35.         strcpy(some_data.some_text, buffer);
  36.         if (msgsnd(msgid, (void *)&some_data, MAX_TEXT, 0) == -1)
  37.     {
  38.             fprintf(stderr, “msgsnd failed\n”);
  39.             return 1;
  40.         }
  41.         if (strncmp(buffer, “end”, 3) == 0)
  42.     {
  43.             running = 0;
  44.         }
  45.     }
  46.     return 0;
  47. }

发送子程序调用msgget函数创建了一个消息队列后,然后调用msgsnd往队列里放上一些消息.下图是运行结果:

 

标签