点击此处阅读原文

另收藏作者ljianhui的专栏初学Linux

下面来说说如何使用消息队列来进行进程间的通信,消息队列与命名管道有很多相似之处。有关命名管道的更多内容可以参阅的另一篇文章:Linux进程间通信——使用命名管道
 
一、什么是消息队列
消息队列提供了一种从一个进程向另一个进程发送一个数据块的方法。  每个数据块都被认为含有一个类型,接收进程可以独立地接收含有不同类型的数据结构。我们可以通过发送消息来避免命名管道的同步和阻塞问题。但是消息队列与命名管道一样,每个数据块都有一个最大长度的限制。
 
Linux用宏MSGMAX和MSGMNB来限制一条消息的最大长度和一个队列的最大长度。
 
二、在Linux中使用消息队列
Linux提供了一系列消息队列的函数接口来让我们方便地使用它来实现进程间的通信。它的用法与其他两个System V PIC机制,即信号量和共享内存相似。
 
1、msgget函数
该函数用来创建和访问一个消息队列。它的原型为:
  1. int msgget(key_t key, int msgflg);
与其他的IPC机制一样,程序必须提供一个键来命名某个特定的消息队列。msgflg是一个权限标志,表示消息队列的访问权限,它与文件的访问权限一样。msgflg可以与IPC_CREAT做或操作,表示当key所命名的消息队列不存在时创建一个消息队列,如果key所命名的消息队列存在时,IPC_CREAT标志会被忽略,而只返回一个标识符。
 
它返回一个以key命名的消息队列的标识符(非零整数),失败时返回-1.
 
2、msgsnd函数
该函数用来把消息添加到消息队列中。它的原型为:
  1. int msgsend(int msgid, const void *msg_ptr, size_t msg_sz, int msgflg);
msgid是由msgget函数返回的消息队列标识符。
 
msg_ptr是一个指向准备发送消息的指针,但是消息的数据结构却有一定的要求,指针msg_ptr所指向的消息结构一定要是以一个长整型成员变量开始的结构体,接收函数将用这个成员来确定消息的类型。所以消息结构要定义成这样:
 
  1. struct my_message{
  2. long int message_type;
  3. /* The data you wish to transfer*/
  4. };
msg_sz是msg_ptr指向的消息的长度,注意是消息的长度,而不是整个结构体的长度,也就是说msg_sz是不包括长整型消息类型成员变量的长度。
 
msgflg用于控制当前消息队列满或队列消息到达系统范围的限制时将要发生的事情。
 
如果调用成功,消息数据的一分副本将被放到消息队列中,并返回0,失败时返回-1.
 
3、msgrcv函数
该函数用来从一个消息队列获取消息,它的原型为
  1. int msgrcv(int msgid, void *msg_ptr, size_t msg_st, long int msgtype, int msgflg);
msgid, msg_ptr, msg_st的作用也函数msgsnd函数的一样。
 
msgtype可以实现一种简单的接收优先级。如果msgtype为0,就获取队列中的第一个消息。如果它的值大于零,将获取具有相同消息类型的第一个信息。如果它小于零,就获取类型等于或小于msgtype的绝对值的第一个消息。
 
msgflg用于控制当队列中没有相应类型的消息可以接收时将发生的事情。
 
调用成功时,该函数返回放到接收缓存区中的字节数,消息被复制到由msg_ptr指向的用户分配的缓存区中,然后删除消息队列中的对应消息。失败时返回-1.
 
4、msgctl函数
该函数用来控制消息队列,它与共享内存的shmctl函数相似,它的原型为:
  1. int msgctl(int msgid, int command, struct msgid_ds *buf);
command是将要采取的动作,它可以取3个值,
    IPC_STAT:把msgid_ds结构中的数据设置为消息队列的当前关联值,即用消息队列的当前关联值覆盖msgid_ds的值。
    IPC_SET:如果进程有足够的权限,就把消息列队的当前关联值设置为msgid_ds结构中给出的值
    IPC_RMID:删除消息队列
 
buf是指向msgid_ds结构的指针,它指向消息队列模式和访问权限的结构。msgid_ds结构至少包括以下成员:
 
  1. struct msgid_ds
  2. {
  3. uid_t shm_perm.uid;
  4. uid_t shm_perm.gid;
  5. mode_t shm_perm.mode;
  6. };
成功时返回0,失败时返回-1.
 
三、使用消息队列进行进程间通信
马不停蹄,介绍完消息队列的定义和可使用的接口之后,我们来看看它是怎么让进程进行通信的。由于可以让不相关的进程进行行通信,所以我们在这里将会编写两个程序,msgreceive和msgsned来表示接收和发送信息。根据正常的情况,我们允许两个程序都可以创建消息,但只有接收者在接收完最后一个消息之后,它才把它删除。
 
接收信息的程序源文件为msgreceive.c的源代码为:
 
  1. #include <unistd.h>
  2. #include <stdlib.h>
  3. #include <stdio.h>
  4. #include <string.h>
  5. #include <errno.h>
  6. #include <sys/msg.h>
  7. struct msg_st
  8. {
  9. long int msg_type;
  10. char text[BUFSIZ];
  11. };
  12. int main()
  13. {
  14. int running = 1;
  15. int msgid = -1;
  16. struct msg_st data;
  17. long int msgtype = 0; //注意1
  18. //建立消息队列
  19. msgid = msgget((key_t)1234, 0666 | IPC_CREAT);
  20. if(msgid == -1)
  21. {
  22. fprintf(stderr, "msgget failed with error: %d\n", errno);
  23. exit(EXIT_FAILURE);
  24. }
  25. //从队列中获取消息,直到遇到end消息为止
  26. while(running)
  27. {
  28. if(msgrcv(msgid, (void*)&data, BUFSIZ, msgtype, 0) == -1)
  29. {
  30. fprintf(stderr, "msgrcv failed with errno: %d\n", errno);
  31. exit(EXIT_FAILURE);
  32. }
  33. printf("You wrote: %s\n",data.text);
  34. //遇到end结束
  35. if(strncmp(data.text, "end", 3) == 0)
  36. running = 0;
  37. }
  38. //删除消息队列
  39. if(msgctl(msgid, IPC_RMID, 0) == -1)
  40. {
  41. fprintf(stderr, "msgctl(IPC_RMID) failed\n");
  42. exit(EXIT_FAILURE);
  43. }
  44. exit(EXIT_SUCCESS);
  45. }
发送信息的程序的源文件msgsend.c的源代码为:
 
  1. #include <unistd.h>
  2. #include <stdlib.h>
  3. #include <stdio.h>
  4. #include <string.h>
  5. #include <sys/msg.h>
  6. #include <errno.h>
  7. #define MAX_TEXT 512
  8. struct msg_st
  9. {
  10. long int msg_type;
  11. char text[MAX_TEXT];
  12. };
  13. int main()
  14. {
  15. int running = 1;
  16. struct msg_st data;
  17. char buffer[BUFSIZ];
  18. int msgid = -1;
  19. //建立消息队列
  20. msgid = msgget((key_t)1234, 0666 | IPC_CREAT);
  21. if(msgid == -1)
  22. {
  23. fprintf(stderr, "msgget failed with error: %d\n", errno);
  24. exit(EXIT_FAILURE);
  25. }
  26. //向消息队列中写消息,直到写入end
  27. while(running)
  28. {
  29. //输入数据
  30. printf("Enter some text: ");
  31. fgets(buffer, BUFSIZ, stdin);
  32. data.msg_type = 1;    //注意2
  33. strcpy(data.text, buffer);
  34. //向队列发送数据
  35. if(msgsnd(msgid, (void*)&data, MAX_TEXT, 0) == -1)
  36. {
  37. fprintf(stderr, "msgsnd failed\n");
  38. exit(EXIT_FAILURE);
  39. }
  40. //输入end结束输入
  41. if(strncmp(buffer, "end", 3) == 0)
  42. running = 0;
  43. sleep(1);
  44. }
  45. exit(EXIT_SUCCESS);
  46. }
运行结果如下:
 
 
四、例子分析——消息类型
 
这里主要说明一下消息类型是怎么一回事,注意msgreceive.c文件main函数中定义的变量msgtype(注释为注意1),它作为msgrcv函数的接收信息类型参数的值,其值为0,表示获取队列中第一个可用的消息。再来看看msgsend.c文件中while循环中的语句data.msg_type = 1(注释为注意2),它用来设置发送的信息的信息类型,即其发送的信息的类型为1。所以程序msgreceive能够接收到程序msgsend发送的信息。
 
如果把注意1,即msgreceive.c文件main函数中的语句由long int msgtype = 0;改变为long int msgtype = 2;会发生什么情况,msgreceive将不能接收到程序msgsend发送的信息。因为在调用msgrcv函数时,如果msgtype(第四个参数)大于零,则将只获取具有相同消息类型的第一个消息,修改后获取的消息类型为2,而msgsend发送的消息类型为1,所以不能被msgreceive程序接收。重新编译msgreceive.c文件并再次执行,其结果如下:
 
 
我们可以看到,msgreceive并没有接收到信息和输出,而且当msgsend输入end结束后,msgreceive也没有结束,通过jobs命令我们可以看到它还在后台运行着。
 
五、消息队列与命名管道的比较
 
消息队列跟命名管道有不少的相同之处,通过与命名管道一样,消息队列进行通信的进程可以是不相关的进程,同时它们都是通过发送和接收的方式来传递数据的。在命名管道中,发送数据用write,接收数据用read,则在消息队列中,发送数据用msgsnd,接收数据用msgrcv。而且它们对每个数据都有一个最大长度的限制。
 
与命名管道相比,消息队列的优势在于,1、消息队列也可以独立于发送和接收进程而存在,从而消除了在同步命名管道的打开和关闭时可能产生的困难。2、同时通过发送消息还可以避免命名管道的同步和阻塞问题,不需要由进程自己来提供同步方法。3、接收程序可以通过消息类型有选择地接收数据,而不是像命名管道中那样,只能默认地接收。
 

[转]Linux进程间通信——使用消息队列的更多相关文章

  1. Linux进程间通信——使用消息队列

    下面来说说如何用不用消息队列来进行进程间的通信,消息队列与命名管道有很多相似之处.有关命名管道的更多内容可以参阅我的另一篇文章:Linux进程间通信——使用命名管道   一.什么是消息队列 消息队列提 ...

  2. 练习--LINUX进程间通信之消息队列MSG

    https://www.ibm.com/developerworks/cn/linux/l-ipc/part3/ 继续坚持,或许不能深刻理解,但至少要保证有印象. ~~~~~~~~~~~~~~ 消息队 ...

  3. Linux进程间通信(消息队列/信号量+共享内存)

    写在前面 不得不说,Deadline果真是第一生产力.不过做出来的东西真的是不堪入目,于是又花了一早上重写代码. 实验内容 进程通信的邮箱方式由操作系统提供形如 send()和 receive()的系 ...

  4. Linux进程间通信之消息队列

    本文依据以下思路展开,首先从宏观上阐述消息队列的机制,然后以具体代码为例进一步阐述该机制,最后试着畅想一下该通信机制潜在的应用. 消息队列是在两个不相关进程间传递数据的一种简单.高效方式,她独立于发送 ...

  5. Linux进程间通信(二) - 消息队列

    消息队列 消息队列是Linux IPC中很常用的一种通信方式,它通常用来在不同进程间发送特定格式的消息数据. 消息队列和之前讨论过的管道和FIFO有很大的区别,主要有以下两点(管道请查阅我的另一篇文章 ...

  6. linux 进程间通信之 消息队列

    消息队列就是一个消息的链表. 能够把消息看作一个记录,具有特定的格式以及特定的优先级.对消息队列有写权限的进程能够向中依照一定的规则加入新消息.有读权限的进程则能够读走消息. 读走就没有了.消息队列是 ...

  7. linux下的进程间通信之消息队列

    概念: 进程彼此之间可以通过IPC消息进行通信.进程产生的每条消息都被发送到一个IPC消息队列中,这条消息一直存放在队列中,直到另一个进程将其读走为止. 优点:可以通过发送消息来几乎完全避免命名管道的 ...

  8. 进程间通信--POSIX消息队列

    相关函数: mqd_t mq_open(const char *name, int oflag); mqd_t mq_send(mqd_t mqdes, const char *msg_ptr, si ...

  9. linux 进程间消息队列通讯

    转自:http://blog.csdn.net/lifan5/article/details/7588529 http://www.cnblogs.com/kunhu/p/3608589.html 前 ...

随机推荐

  1. Request 、Response 与Server的使用

    纯属记录总结,以下图片都是来自 ASP.NET笔记之 Request .Response 与Server的使用 Request Response Server 关于Server.MapPath 方法看 ...

  2. 「iOS造轮子」之UIButton 用Block响应事件

    俗语说 一个不懒的程序员不是好程序员 造轮子,也只是为了以后更好的coding. coding,简易明了的代码更是所有程序员都希望看到的 无论是看自己的代码,还是接手别人的代码 都希望一看都知道这代码 ...

  3. 掌握Thinkphp3.2.0----模型初步

    1.为什么要学习框架?框架是什么? 简单的说就是为了简单,提高开发的效率.至于什么是框架(一种规范),现在的我还不是很理解,容后再议. 学习框架最重要的就是遵循,按照开发者的意图来使用该框架. 2.t ...

  4. Flink – Trigger,Evictor

    org.apache.flink.streaming.api.windowing.triggers;   Trigger public abstract class Trigger<T, W e ...

  5. sql小技巧

    --实际只会更新一条.可有效防止误操作.特别是操作线上正式数据时. UPDATE TOP(1) Table2 SET Culumn1='value'WHERE id IN(269102,269104) ...

  6. C# DataTable分页处理

    有时候我们从数据库获取的数据量太大,而我们不需要一次性显示那么多的时候,我们就要对数据进行分页处理了,让每页显示不同的数据. public DataTable GetPagedTable(DataTa ...

  7. php程序员绝不能违背的安全铁则

    作为PHP程序员,特别是新手,对于互联网的险恶总是知道的太少,对于外部的入侵有很多时候是素手无策的,他们根本不知道黑客是如何入侵的.提交入侵.上传漏洞.sql 注入.跨脚本攻击等等.作为最基本的防范你 ...

  8. WINDOWS遠程常規化後再次切換回到全屏

    其实有更好的方法的,CTRI+Alt+Break,博主试试看

  9. ios移动端部分手机不支持background-attachment: fixed 的解决办法

    ios系统和某些移动端background-attachment:fixed不兼容性,没有任何效果,但可以hack一下就可以了,代码如下: ps:想在哪个标签加背景,可以在它class后:before ...

  10. TIJ——Chapter Seven:Reusing Classes

    Reusing Classes 有两种常用方式实现类的重用,组件(在新类中创建存在类的对象)和继承. Composition syntax Every non-primitive object has ...