LINUX下的消息队列实例解析(转)

LINUX下的消息队列实例解析(转)

转载自:http://blog.csdn.net/primeprime/article/details/43323891

    一、消息队列的基本概念

    消息队列 (也叫做报文队列)是Unix系统V版本中进程间通信机制之一。消息队列就是一个消息的链表。就是把消息看作一个记录,并且这个记录具有特定的格式以及特定的优先级。对消息队列有写权限的进程可以按照一定的规则添加新消息;对消息队列有读权限的进程则可以从消息队列中读出消息。Linux采用消息队列的方式来实现消息传递。这种消息的发送方式是:发送方不必等待接收方检查它所收到的消息就可以继续工作下去,而接收方如果没有收到消息也不需等待。新的消息总是放在队列的末尾,接收的时候并不总是从头来接收,可以从中间来接收。消息队列是随内核持续的并和进程相关,只有在内核重起或者显示删除一个消息队列时,该消息队列才会真正被删除。因此系统中记录消息队列的数据结构 (struct ipc_ids msg_ids)位于内核中系统中的所有消息队列都可以在结构msg_ids中中找到访问入口。IPC标识符:每一个I P C目标都有一个唯一的I P C标识符。这里所指的I P C目标是指一个单独的消息队列、一个信号量集或者一个共享的内存段。系统内核使用此标识符在系统内核中指明 I P C目标。IPC关键字:想要获得唯一的标识符,则必须使用一个 I P C关键字。客户端进程和服务器端进程必须双方都同意此关键字。这是建立一个客户机/服务器框架的第一步。一般情况下,可以使用ftok ( )函数为客户端和服务器端产生关键字值。

    #include <sys/types.h>  

    #include<sys/ipc.h>   

    key_t ftok( const char* fname, int id )   

    fname就是你指定的文件名(已经存在的文件名),一般使用当前目录,如:  key_t key;   

    key =ftok(".", 1);

    这样就是将fname设为当前目录。id是子序号在一般的UNIX实现中,是将文件的索引节点号取出,前面加上子序号得到key_t的返回值。如指定文件的索引节点号为65538,换算成16进制为0x010002,而你指定的ID值为38,换算成16进制为0x26,则最后的key_t返回值为0x26010002。查询文件索引节点号的方法是: ls -i当删除重建文件后,索引节点号由操作系统根据当时文件系统的使用情况分配,因此与原来不同,所以得到的索引节点号也不同。  如果要确保key_t值不变,要么确保ftok的文件不被删除,要么不用ftok,指定一个固定的key_t值,比如:   

    #define IPCKEY 0x111   

    char path[256];   

    sprintf(path, "%s/etc/config.ini", (char*)getenv("HOME") );   msgid=ftok(path, IPCKEY );

    也就是说其实ftok的作用就是根据你所指定的文件的索引点号生成一个独一无二的KEY,并保持。不过在使用的过程中,一般都是直接指定一个固定的值,这样使用起来简单,但一定要确保和其它程序不冲突。

    二、使用消息队列

    查看一个系统中的消息可以用ipcs –q来查看,这可以看到msgid和权限以及当前有几条消息。当你在调试的过程中,由于程序没有正常结束,而你又想删除此消息时,请使用ipcrm –q msgid命令。

    下面结合具体的实例代码详细说明一下使用的过程。

    Snd.c文件

    #include <stdlib.h>

    #include <stdio.h>

    #include <string.h>

    #include <errno.h>

    #include <unistd.h>

    #include <sys/msg.h>

    #include <sys/stat.h>

    #define MAX_TEXT 512

    #define MSG_KEY 335

     

    struct my_msg_st

    {

    long my_msg_type;//这个就是消息的类型,在接收的时候一定要指定这个

                           //类型才会接收到相应的消息。

    char some_text[MAX_TEXT];

    };

     

    int main()

    {

    int i = 10;

    int running = 1;

    struct my_msg_st some_data;

    int msgid;

    char buffer[BUFSIZ];

    msgid = msgget((key_t)MSG_KEY, IPC_CREAT|IPC_EXCL|S_IRUSR|S_IWUSR);//首先创建,如果不成                功,就取得

    if(msgid==-1)

    {

         printf("create faile get imsgid\n");

         msgid=msgget((key_t)MSG_KEY,IPC_EXCL|S_IRUSR|S_IWUSR);

    }

    if (msgid == -1)

    {

         fprintf(stderr, "msgget failed with error: %d\n",errno);

         exit(EXIT_FAILURE);

    }

     

    while(running)

     {

         printf("[%d]Enter some text: less than%d\n",msgid,MAX_TEXT);

         fgets(buffer, BUFSIZ, stdin);

         some_data.my_msg_type = 10;

         strcpy(some_data.some_text, buffer);

          if (msgsnd(msgid, (void*)&some_data, sizeof(some_data), 0) == -1)

         {

             fprintf(stderr, "msgsnd failed\n");

             exit(EXIT_FAILURE);

          }

          if(strncmp(buffer,"end", 3) == 0)

         {

          running = 0;

         }

     }

     

    exit(EXIT_SUCCESS);

    }

    Rcv.c文件

    #include <stdlib.h>

    #include <unistd.h>

    #include <stdio.h>

    #include <string.h>

    #include <sys/types.h>

    #include <sys/stat.h>

    #include <sys/msg.h>

    #include <errno.h>

    #define MAX_TEXT 512

    #define MSG_KEY  335

    struct my_msg_st

    {

     long my_msg_type;

     char some_text[MAX_TEXT];

    };

     

    int main()

    {

     int running = 1;

     int msgid;

     struct my_msg_st some_data;

     long int msg_to_receive = 10;

     msgid =msgget((key_t)MSG_KEY, IPC_CREAT|IPC_EXCL|S_IRUSR|S_IWUSR);

    if(msgid==-1)

         msgid=msgget((key_t)MSG_KEY,IPC_EXCL|S_IRUSR|S_IWUSR);

     if (msgid == -1)

    {

          fprintf(stderr,"msggetfailed with error: %d\n",errno);

          exit(EXIT_FAILURE);

     }

     printf("prepare receivemsg:%d\n",msgid);

    while(running)

    {

          //这里的msg_to_receive指定了接收消息的类型,如果类型不对,将取不到消息.IPC_NOWAIT           果消息队列里没有消息,则马上返回并设置errno=ENOMSG。如果指定为0则将等待消息的到来。

          if (msgrcv(msgid,(void*)&some_data,sizeof(some_data),msg_to_receive,IPC_NOWAIT) == -1)

         {

              if(ENOMSG==errno)

              {

                   printf("no Msg receive\n");

                   continue;

              }

          fprintf(stderr,"msgrcv failed with error: %d\n", errno);

          exit(EXIT_FAILURE);

          }

          printf("You wrote:%s", some_data.some_text);

         if (strncmp(some_data.some_text, "end", 3) == 0)

         {

             running = 0;

         }

     }

     

    if (msgctl(msgid, IPC_RMID, 0) == -1) //使用这个库函数来移除消息队列。

    {

     fprintf(stderr,"msgctl(IPC_RMID) failed\n");

     exit(EXIT_FAILURE);

     }

     exit(EXIT_SUCCESS);.

    }

     

    三、消息队列的相关参数

    1.查看系统中默认的消息队列的相关参数

    Ipcs –l可以查看当前消息队列的上限限制。

    ------Messages: Limits --------

    max queuessystem wide = 16                     2G

    max size ofmessage (bytes) = 65536               2G

    default maxsize of queue (bytes) = 65536          2G

    msgmni最大消息队列数

    msgmax最大消息长度(字节数)

    msgmnb消息队列中的最大字节数

    其实我们要关心的参数主要有三个,说得通俗点就是最多可以有多少个队列使用,每个队列里可以放多少消息,每个消息最多可以带多少内容。

    现在解释如下:系统中默认的消息列数是16,最大可以达到2G,系统中消息的个数和消息长度的总数,受队列最大尺寸限制。Msgmnb= msgmax*最大个数.

    其中消息队列的长度受结构使用。

    structmy_msg_st

    {

          long my_msg_type;

          char some_text[MAX_TEXT];

    };

    大家如果有兴趣,可以自己写个程序测试试下,简单的很就是无限制分配就可以了。

    修改消息队列的参数

    1.永久修改

    root用户下修改/etc/sysctl.conf文件。

    2.临时修改

    root用户下sysctl-w kernel.msgmnb= 1048576

     

    /proc/sys/kernel/msgmax    单个消息的最大值

    /proc/sys/kernel/msgmnb     单个消息体的容量的最大值

    /proc/sys/kernel/msgmni    消息体的数量缺省值为 16

    cat/proc/sys/kernel/shmmax

    可通过下面的方式进行设置

    echo819200 > /proc/sys/kernel/msgmax

    echo1638400 > /proc/sys/kernel/msgmnb       

    echo1600 > /proc/sys/kernel/msgmni

     cd /proc/sys/kernel ; cat msgmax;cat msgmnb;cat msgmni;

     

    在程序中可以通过

    msgctl(msgid,IPC_STAT,&mymsq_ds);

    printf("%d,%d,%d\n",mymsq_ds.msg_qbytes,mymsq_ds.msg_qnum,mymsq_ds.msg_cbytes);来获得队列消息的总大小,已经有的消息数和所占空间数。

    1.IPC_STAT:该命令用来获取消息队列信息,返回的信息存贮在buf指向的msqid结构中;

    2.IPC_SET:该命令用来设置消息队列的属性,要设置的属性存储在buf指向的msqid结构中;可设置属性包括:msg_perm.uid、msg_perm.gid、msg_perm.mode以及msg_qbytes,同时,也影响msg_ctime成员。

    3.IPC_RMID:删除msqid标识的消息队列。

     

    0
    0
     
     
    • 0
      点赞
    • 0
      收藏
      觉得还不错? 一键收藏
    • 0
      评论
    评论
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

    当前余额3.43前往充值 >
    需支付:10.00
    成就一亿技术人!
    领取后你会自动成为博主和红包主的粉丝 规则
    hope_wisdom
    发出的红包
    实付
    使用余额支付
    点击重新获取
    扫码支付
    钱包余额 0

    抵扣说明:

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

    余额充值