免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 877 | 回复: 0
打印 上一主题 下一主题

学习笔记08-学习《精通UNIX下C语言编程及项目实践》 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2009-04-23 13:19 |只看该作者 |倒序浏览

                欢迎转载,请保留作者信息bill@华中科技大学
[/url]
[url=http://billstone.cublog.cn/]http://billstone.cublog.cn

  
  十二 消息队列
  消息队列是UNIX内核中的一个先进先出的链表结构. 相对于管道, 消息队列有明显的优势, 原因在于:
  (1) 消息队列是一种先进先出的队列型数据结构, 可以保证先送的货物先到达, 后送的货物后到达, 避免了插队现象.
  (2) 信息队列将输出的信息进行了打包处理, 这样就可以保证以每个消息为单位进行接收了.
  (3) 消息队列还可以对信息进行分类处理, 标记各种类别的信息, 这样就可以根据信息类别分别出列.
  IPC就是进程间通信, 侠义上讲, IPC指消息队列, 信号量和共享内存三种对象. 通过shell命令ipcs可以查询当前系统的IPC对象信息:

  
  
  [bill@billstone Unix_study]$ ipcs
   
  ------ Shared Memory Segments --------
  key        shmid      owner      perms      bytes      nattch     status
  0x00000000 196609     bill      777        393216     2          dest
  0x00000000 491522     root      644        106496     2          dest
  0x00000000 524291     root      644        110592     2          dest
  0x00000000 557060     root      644        110592     2          dest
  0x00000000 589829     root      644        110592     2          dest
   
  ------ Semaphore Arrays --------
  key        semid      owner      perms      nsems
   
  ------ Message Queues --------
  key        msqid      owner      perms      used-bytes   messages
   
  [bill@billstone Unix_study]$
  
  

  消息队列简介
  UNIX内核使用结构msqid_ds来管理消息队列:

  
  
  struct msqid_ds {
          struct ipc_perm msg_perm;
          struct msg
  *msg_first;          /* first message
  on queue, unused  */
          struct msg
  *msg_last;           /* last message in
  queue, unused */
          __kernel_time_t
  msg_stime;      /* last msgsnd time */
          __kernel_time_t
  msg_rtime;      /* last msgrcv time */
          __kernel_time_t
  msg_ctime;      /* last change time */
          unsigned long  msg_lcbytes;     /* Reuse junk fields for 32 bit */
          unsigned long  msg_lqbytes;     /* ditto */
          unsigned short
  msg_cbytes;      /* current number of
  bytes on queue */
          unsigned short msg_qnum;        /* number of messages in queue */
          unsigned short
  msg_qbytes;      /* max number of bytes
  on queue */
          __kernel_ipc_pid_t
  msg_lspid;   /* pid of last msgsnd */
          __kernel_ipc_pid_t
  msg_lrpid;   /* last receive pid */
  };
  
  

  其中msg结构的定义如下, 我们在实际项目中几乎很少使用如下的结构, 一般都需要我们根据实际的需求自行定义.

  
  
  Struct msg{
      struct msg* msg_next;
      long msg_type;
      long msg_ts;
      short msg_spot;
  };
  
  

  理论上可以通过结构msqid_ds的成员msg_first, msg_last和结构msg的成员msg_next遍历全部消息队列并完成管理和维护消息队列的功能, 但实际上这三个成员是UNIX内核的直辖数据, 用户无法引用.
  消息队列中消息本身是由消息类型和消息数据组成, 我们常常使用如下结构作为消息模板:

  
  
  struct msgbuf {
          long mtype;         /* type of message */
          char mtext[1];      /* message text */
  };
  
  

  根据消息类型的不同, 我们可以在同一个信息队列中定义不同功能的消息.
  使用消息队列
  (1) 消息队列的创建
  在UNIX中, 采用函数msgget创建消息队列

  
  
  #include
  #include
  #include
  extern int msgget (key_t __key, int __msgflg)
  __THROW;
  
  

  函数msgget创建一个新的消息队列, 或者访问一个已经存在的消息队列. 调用成功时返回消息队列的标志符, 否则返回-1.
  (2) 消息队列的发送和接收
  在UNIX中函数msgsnd向消息队列发送消息, 原型如下:

  
  
  #include
  #include
  #include
  extern int msgsnd (int __msqid, __const void
  *__msgp, size_t __msgsz, int __msgflg) __THROW;
  
  

  发送消息一般分五个步骤:
 a) 根据自己的需要定义消息结构

  
  
  struct msgbuf {
          long mtype;         /* type of message */
          char ctext[100];      /* message data */
  };
  
  

 b) 打开或创建消息队列

  
  
  msgid = msgget(Key, 0666 | IPC_CREAT);
  if(msgid 打开(或创建)队列失败
  
  

 c) 组装消息
 d) 发送消息

  
  
  int ret;
  ret = msgsnd(msgid, (void *)&buf,
  strlen(buf.ctext), IPC_NOWAIT);
  
  

 e) 发送判断

  
  
  If (ret == -1){
      if (errno == EINTR) 信号中断, 重新发送;
      else 系统错误
  }
  
  

  下面是一个发送消息的实例: 它循环读取键盘输入, 将输入的字符串信息写入到消息队列(关键字为0x1234)中.

  
  
  [bill@billstone Unix_study]$ cat msg1.c
  #include
  #include
  #include
  #include
  #include
   
  extern int errno;
   
  struct mymsgbuf{
          long mtype;
          char ctext[100];
  };
   
  int main(void)
  {
          struct mymsgbuf buf;
          int msgid;
   
          if((msgid =
  msgget(0x1234, 0666 | IPC_CREAT))
                  fprintf(stderr,
  "open msg %X failed\n", 0x1234);
                  exit(1);
          }
          while(1){
                 
  printf("Please input: ");
                  memset(&buf,
  0, sizeof(buf));
                  fgets(buf.ctext,
  sizeof(buf.ctext), stdin);
                  buf.mtype =
  getpid();
                  while((msgsnd(msgid, (void *)&buf,
  strlen(buf.ctext), IPC_NOWAIT))
                          if(errno
  == EINTR)
                                 
  continue;
                          exit(2);
                  }
                 
  if(!strncmp(buf.ctext, "exit", 4))
                          break;
          }
   
          return 0;
  }
  [bill@billstone Unix_study]$
  
  

  运行结果如下:

  
  
  [bill@billstone Unix_study]$ make msg1
  cc     msg1.c   -o msg1
  [bill@billstone Unix_study]$ ipcs -q
   
  ------ Message Queues --------
  key        msqid      owner      perms      used-bytes   messages
   
  [bill@billstone Unix_study]$ ./msg1
  Please input: Hello world!
  Please input: Nice to meet you!
  Please input: bye!
  Please input: exit
  [bill@billstone Unix_study]$ ipcs -q
   
  ------ Message Queues --------
  key        msqid      owner      perms      used-bytes   messages
  0x00001234 98304      bill       666        41           4
   
  [bill@billstone Unix_study]$
  
  

  在UNIX中函数msgrcv从消息队列中接收消息, 原型如下:

  
  
  #include
  #include
  #include
  extern int msgrcv (int __msqid, void *__msgp, size_t
  __msgsz, long int __msgtyp, int __msgflg) __THROW;
  
  

  与函数msgsnd不同,这里参数msgsz指的是缓冲区的最大容量,包括消息类型占用的部分.
  这里配合上面的例子设计接收消息的实例: 以阻塞方式不断地从消息队列(关键字为0x1234)中读取消息, 并打印接收到的消息类型, 长度和数据等信息, 当接收到数据内容为'exit'时程序结束.

  
  
  [bill@billstone Unix_study]$ cat msg2.c
  #include
  #include
  #include
  #include
  #include
   
  extern int errno;
   
  struct mymsgbuf{
          long mtype;
          char ctext[100];
  };
   
  int main(void)
  {
          struct mymsgbuf buf;
          int msgid, ret;
   
          if((msgid =
  msgget(0x1234, 0666 | IPC_CREAT))
                  fprintf(stderr,
  "open msg %X failed\n", 0x1234);
                  exit(1);
          }
          while(1){
                  memset(&buf,
  0, sizeof(buf));
                  while((ret =
  msgrcv(msgid, (void *)&buf, sizeof(buf), 0, 0))
                          if(errno
  == EINTR)
                                 
  continue;
                         
  fprintf(stderr, "Error no: %d", errno);
                          exit(2);
                  }
                  fprintf(stderr,
  "Msg: Type=%d, Len=%d, Text:%s", buf.mtype, ret, buf.ctext);
                 
  if(!strncmp(buf.ctext, "exit", 4))
                          break;
          }
   
          return 0;
  }
  [bill@billstone Unix_study]$
  
  

  运行结果如下:

  
  
  [bill@billstone Unix_study]$ make msg2
  cc     msg2.c   -o msg2
  [bill@billstone Unix_study]$ ipcs -q
   
  ------ Message Queues --------
  key        msqid      owner      perms      used-bytes   messages
  0x00001234 98304      bill      
  666        41           4
   
  [bill@billstone Unix_study]$ ./msg2
  Msg: Type=15666, Len=13, Text:Hello world!
  Msg: Type=15666, Len=18, Text:Nice to meet you!
  Msg: Type=15666, Len=5, Text:bye!
  Msg: Type=15666, Len=5, Text:exit
  [bill@billstone Unix_study]$ ipcs -q
   
  ------ Message Queues --------
  key        msqid      owner      perms      used-bytes   messages
  0x00001234 98304      bill       666        0            0
   
  [bill@billstone Unix_study]$
  
  

  从上面可以看到, 采用消息队列通信比采用管道通信具有更多的灵活性.
  系统调用msgctl对消息队列进行各种控制, 包括查询消息队列数据结构, 改变消息队列访问权限, 改变消息队列属主信息和删除消息队列等, 原型如下:

  
  
  #include
  #include
  #include
  int msgctl(int msqid, int cmd, struct msqid_ds
  *buf);
  
  

  根据cmd参数对msqid消息队列操作:
  a) IPC_RMID:
删除消息队列
  b) IPC_STAT:
读取消息队列
  c) IPC_SET: 重置消息队列结构msqid_ds中的成员uid, gid, mode及msg_qbytes.
               
               
               
               
               
               
               

本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u1/47687/showart_1907973.html
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP