消息队列五

1 消息队列基本概念

 消息队列是系统内核地址空间中的一个内部的链表。消息可以按照顺序发送到队列中,也可以以几种不同的方式从队列中读取。每一个消息队列用一个唯一的IPC标识符表示。

  了解在系统内核中的数据结构是了解IPC机制如何工作的最好的方法。

  首先我们看一下数据结构msgbuf。此数据结构可以说是消息数据的模板。虽然此数据结构需要用户自己定义,但了解系统中有这样一个数据结构是十分重要的。在<sys/msg.h>中,此数据结构是这样定义的:

[cpp]  view plain  copy
 print ?
  1. struct msgbuf{  
  2.      long mtype;         /*type of message, must>0*/  
  3.     char mtext[1];      /*message text*/  
  4. };  

 在数据结构msgbuf中共有两个元素:

     mtype指消息的类型,它由一个整数来代表,并且它只能是大于0的整数。

     mtext是消息数据本身。

 mtext字段不但可以存储字符,还可以存储任何其他的数据类型。此字段可以说是完全任意的,因为程序员自己可以重新定义此数据结构。请看下面重新定义的例子:

[cpp]  view plain  copy
 print ?
  1. struct my_msgbuf{  
  2.     long mtype;           /*Message type*/  
  3.     char request_id;      /*Request identifier*/  
  4.     struct client info;   /*Client information structure*/  
  5. };  

 这里的消息类型字段和前面的一样,但数据结构的其余部分则由其他的两个字段所代替,而其中的一个还是另外一个结构。这就体现了消息队列的灵活之处。内核本身并不对消息结构中的数据做任何翻译。你可以在其中发送任何信息,但存在一个内部给定的消息大小的限制。在Linux系统中,消息的最大的长度是4056个字节,其中包括mtype,它占用4个字节的长度。


创建消息队列

 系统调用msgget()用于创建一个新的消息队列,或者存取一个已经存在的消息队列,其函数原型是:

[cpp]  view plain  copy
 print ?
  1. #include<sys/types.h>  
  2. #include<sys/ipc.h>  
  3. #include<sys/msg.h>  
  4. int msgget(key_t key, int msgflg);  

 

 系统调用msgget()中的第一个参数是消息队列关键字值,可以由ftok()获得。第二个参数msgflg是一些标志,包括:

   IPC_CREAT:如果内核中没有此队列,则创建它。

   IPC_EXCL:当和IPC_CREAT一起使用时,如果队列已经存在,则返回错误。

 当msgget()执行成功时,返回消息队列的标识符,否则返回-1,通过errno和perror()函数查看错误信息.

下面是一个打开和创建一个消息队列的例子,函数返回消息队列的标识符

 

[cpp]  view plain  copy
 print ?
  1. int open_ queue(key_ t keyval)  
  2. {  
  3.   int qid;  
  4.   if((qid = msgget(keyval,IPC_CREAT|0660)) == -1){  
  5.     perror(”msgget”);  
  6.     return(-1);  
  7.   }  
  8.   return(qid);  
  9. }  

发送和接收消息

当得到了消息队列标识符,就可以在队列上执行发送或者接收消息了。msgsnd()系统调用用于向队列发送一条消息,其函数原型是:   

[cpp]  view plain  copy
 print ?
  1. int msgsnd(int msqid, struct msgbuf *msgp, sizet msgsz, int msgflg);   


 

第一个参数是消息队列标识符。第二个参数msgp,是指向消息缓冲区的指针。参数msgsz指定了消息的字节大小,但不包括消息类型的长度(4个字节)。参数msgflg可以设置为:

   0:此时为忽略此参数,如果消息队列已满,调用进程将会挂起,直到消息可以写入到队列中。

  IPC_NOWAIT:如果消息队列己满,那么此消息则不会写入到消息队列中,控制将返回到调用进程中。消息队列写入成功时,函数返回0,否则返回-1。

下面是一个发送消息的例子:

[cpp]  view plain  copy
 print ?
  1. int send_message(int qid, struct mymsgbuf *qbuf)  
  2. {  
  3.   int result, length;  
  4.   /*The length is essentially the size of the structure minus sizeof(mtype)*/  
  5.   length = sizeof(struct mymsgbuf) - sizeof(long);  
  6.   if((result = msgsnd(qid, qbuf, length, O)) == -1){  
  7.      perror(”msgsnd”);  
  8.      return(-1);  
  9.   }  
  10.   return(result);  
  11. }  


 

msgrcv()系统调用用于从消息队列读取一条消息,其函数原型是:

[cpp]  view plain  copy
 print ?
  1. ssize_t msgrcv(int msqid, struct msgbuf *msgp, size_t msgsz, long msgtype, int msgflg);  


 

第一个参数是消息队列的标识符。第二个参数代表要存储消息的缓冲区的地址。第三参数是消息缓冲区的长度,不包括mtype的长度,它可以按照如下的方法计算:

msgsz=sizeof(struct mymsgbuf)-sizeof(long);

第四个参数是要从消息队列中读取的消息的类型。

如果msgtype=0,接收消息队列的第一个消息。大于0接收队列中消息类型等于这个值的第一个消息。小于0接收消息队列中小于或者等于msgtype绝对值的所有消息中的最小一个消息。一般为0。

第五个参数msgflg取值为:

  0:从队列中取出最长时间的一条消息。

 IPC_NOWAIT:当队列没有消息时,调用会立即返回ENOMSG错误。否则,调用进程将会挂起,直到队列中的一条消息满足msgrcv()的参数要求。

   当函数成功时,返回写入缓冲区的数据大小,否则返回-1。

  下面是一个接收消息的例子:

[cpp]  view plain  copy
 print ?
  1. int read_message(int qid, long type, struct mymsgbuf *qbuf)  
  2. {      
  3. int result, length;  
  4. /* The length is essentially the size of the structure minus sizeof(mtype)*/  
  5. length = sizeof(struct mymsgbuf) - sizeof(long);  
  6.   if((result = msgrcv(qid, qbuf, length, type, O))==-l){  
  7.   perror(¨msgrcv¨);  
  8.   return(-1);  
  9. }  
  10. return(result);  
  11. }  

消息队列的控制

消息队列标识符的属性被记录在一个msgid_ds结构体:

 

[cpp]  view plain  copy
 print ?
  1. struct msqid_ds{  
  2.   struct ipc_perm msg_perm;      /*所有者和权限*/  
  3.   time_t          msg_stime;     /*最后一次向队列发送消息的时间*/  
  4.   time_t          msg_rtime;     /*最后一次从队列接收消息的时间*/  
  5.   time_t          msg_ctime;     /*队列最后一次改动的时问*/  
  6.   unsigned long   __msg_cbytes;  /*当前队列所有消息的总长度*/  
  7.   msgqnum_t       msg_qnum;      /*当前队列中的消息数量*/  
  8.   msglen_t        msg_qbytes;    /*消息队列的最大消息总长度*/  
  9.   pid_t           msg_lspid;     /*最一次给队列发送消息的进程PID*/  
  10.   pid_t           msg_lrpid;     /*最后一次从队列接收消息的进程PID*/  
  11. }  


通过msgctl()可以对消息队列进行控制或者一些属性的修改,其函数原型为:

[cpp]  view plain  copy
 print ?
  1. int msgctl(int msqid, int cmd, struct msqid ds *buf;    

第一个参数是消息队列的标识符,第二个参数cmd指定了操作,下面是几个常用的操作:

  IPC_STAT:读取消息队列的数据结构msqid_ds,并将其存储在buf指定的地址中。

  IPC_SET:设置消息队列的数据结构msqid_ds中的ipc_perm、msg_qbytes、msg_ctime元素的值。这个值取自buf参数。   

  IPC_RMID:从系统内核中移走消息队列。

比如下面是一个删除消息队列的例子。

[cpp]  view plain  copy
 print ?
  1. int remove_queue(int qid){  
  2.   if(msgctl(qid, IPC_RMID, 0)==-1){  
  3.     perror(”msgctl”);  
  4.     return(-1);  
  5. }  
  6.   return(O);  
  7. }  

综合示例msgtool

我们来实现一个简单的消息队列工具,用于创建消息队列、发送、读取消息、改变权限以及删除消息队列。

它的用法如下:

(1)  发送消息

msgtool s (type)”text”

(2)  读取消息

msgtool r (type)

(3)  改变权限

msgtool l m (mode)

(4)  删除队列

msgtool d

源代码msgtoo1.c

[cpp]  view plain  copy
 print ?
  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3. #include <string.h>  
  4. #include <ctype.h>  
  5. #include <sys/types.h>  
  6. #include <sys/ipc.h>  
  7. #include <sys/msg.h>  
  8.   
  9. #define MAX_SEND_SIZE 80  
  10. struct mymsgbuf {  
  11.     long mtype;  
  12.     char mtext[MAX_SEND_SIZE];  
  13. };  
  14.   
  15. void send_message(int qid, struct mymsgbuf *qbuf, long type, char *text);  
  16. void read_message(int qid, struct mymsgbuf *qbuf, long type);  
  17. void remove_queue(int qid);  
  18. void change_queue_mode(int qid, char *mode);  
  19. void usage(void);  
  20.   
  21. int main(int argc, char *argv[])  
  22. {  
  23.     key_t key;  
  24.     int msgqueue_id;  
  25.     struct mymsgbuf qbuf;  
  26.     if(argc == 1)  
  27.         usage();  
  28.     /* Create unique key via call to ftok() */  
  29.     key = ftok("."'m');  
  30.     /* Open the queue - create if necessary */  
  31.     if((msgqueue_id = msgget(key, IPC_CREAT|0660)) == -1) {  
  32.         perror("msgget");  
  33.         exit(1);  
  34.     }  
  35.   
  36.     printf("message queue id = [%d]\n", msgqueue_id );  
  37.   
  38.     switch(tolower(argv[1][0]))  
  39.     {  
  40.         case 's':   
  41.             if( argc < 4 ){  
  42.                 usage();  
  43.                 break;  
  44.             }  
  45.             send_message(msgqueue_id, (struct mymsgbuf *)&qbuf, atol(argv[2]), argv[3]);  
  46.             break;  
  47.         case 'r':   
  48.             if( argc < 3 ){  
  49.                 usage();  
  50.                 break;  
  51.             }  
  52.             read_message(msgqueue_id, &qbuf, atol(argv[2]));  
  53.             break;  
  54.         case 'd':   
  55.             remove_queue(msgqueue_id);  
  56.             break;  
  57.         case 'm':   
  58.             if( argc < 3 ){  
  59.                 usage();  
  60.                 break;  
  61.             }  
  62.             change_queue_mode(msgqueue_id, argv[2]);  
  63.             break;  
  64.         default: usage();  
  65.     }  
  66.     return(0);  
  67. }  
  68.   
  69. void send_message(int qid, struct mymsgbuf *qbuf, long type, char *text)  
  70. {  
  71.     /* Send a message to the queue */  
  72.     printf("Sending a message ...\n");  
  73.     qbuf->mtype = type;  
  74.     strcpy(qbuf->mtext, text);  
  75.     if((msgsnd(qid, (struct msgbuf *)qbuf, strlen(qbuf->mtext)+1, 0)) ==-1)  
  76.     {  
  77.         perror("msgsnd");  
  78.         exit(1);  
  79.     }  
  80. }  
  81.   
  82. void read_message(int qid, struct mymsgbuf *qbuf, long type)  
  83. {  
  84.     /* Read a message from the queue */  
  85.     printf("Reading a message ...\n");  
  86.     qbuf->mtype = type;  
  87.     msgrcv(qid, (struct msgbuf *)qbuf, MAX_SEND_SIZE, type, 0);  
  88.     printf("Type: %ld Text: %s\n", qbuf->mtype, qbuf->mtext);  
  89. }  
  90.   
  91. void remove_queue(int qid)  
  92. {  
  93.     /* Remove the queue */  
  94.     msgctl(qid, IPC_RMID, 0);  
  95. }  
  96.   
  97. void change_queue_mode(int qid, char *mode)  
  98. {  
  99.     struct msqid_ds myqueue_ds;  
  100.     /* Get current info */  
  101.     msgctl(qid, IPC_STAT, &myqueue_ds);  
  102.     /* Convert and load the mode */  
  103.     sscanf(mode, "%ho", &myqueue_ds.msg_perm.mode);  
  104.     /* Update the mode */  
  105.     msgctl(qid, IPC_SET, &myqueue_ds);  
  106. }  
  107.   
  108. void usage(void)  
  109. {  
  110.     fprintf(stderr, "msgtool - A utility for tinkering with msg queues\n");  
  111.     fprintf(stderr, "USAGE: msgtool (s)end <type> <messagetext>\n");  
  112.     fprintf(stderr, " (r)ecv <type>\n");  
  113.     fprintf(stderr, " (d)elete\n");  
  114.     fprintf(stderr, " (m)ode <octal mode>\n");  
  115.     exit(1);  
  116. }   


 

我们先来发送一条消息:

[cpp]  view plain  copy
 print ?
  1. ./msgtool s 1 ”Hello”  
  2. message queue id = [32768]  
  3. Sending a message...  


 

用ipcs命令可以看到创建的消息队列:

[cpp]  view plain  copy
 print ?
  1. ipcs -q  
  2.   
  3. ------ Message Queues --------  
  4. key        msqid      owner      perms      used-bytes   messages      
  5. 0x6d01c86f 32768      user       660        6            1       


 

可以看到现在队列里有1条消息。

再用msgtool读出先读出一条消息,然后再次读取消息:

[cpp]  view plain  copy
 print ?
  1. ./msgtool r 0  
  2. message queue id = [32768]  
  3. Reading a message ...  
  4. Type: 1 Text: Hello  
  5. [alex@alex-/work/tutorai l]$./msgtool r 0  
  6. message queue id 2[32768]  
  7. Reading a message...  


 

此时消息队列已经为空,进程被阻塞等待消息。我们到另外一个终端发送一条消息:

[cpp]  view plain  copy
 print ?
  1. ./msgtool s 2 “Another message”  
  2. message queue id=[32768]  
  3. Sending a message…  


这是第一个终端的msgtool收到消息:

Type:2 Text:Another message

下面我们修改消息队列的访问权限
 
[cpp]  view plain  copy
 print ?
  1. ./msgtool m 600  
  2. message queue id=[32768]  
  3. [alex@alex一/work/tutorail]$ ipcs –q  
  4.   
  5. ------ Message Queues --------  
  6. key        msqid      owner      perms      used-bytes   messages      
  7. 0x6d01c86f 32768      user       660        6            1     


从ipcs命令看到的消息队列中,perms列已经被改为600。最后删除消息队列

[cpp]  view plain  copy
 print ?
  1. /msgtool d  
  2. message queue id=[32768]  
  3. [alex@alex一/work/tutorail]$ ipcs –q  
  4.   
  5. ------ Message Queues --------  
  6. key        msqid      owner      perms      used-bytes   messages   
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值