Linux消息队列

来源:互联网 发布:单片机控制电机转速 编辑:程序博客网 时间:2024/05/20 01:10

 

 

 

 

消息队列是linux IPC 机制的一种,和共享内存很相似。

消息队列的内核持续性要求每个消息队列都在系统范围内对应唯一的键值,所以,要获得一个消息队列的描述字,只需提供该消息队列的键值即可。

消息队列描述字是由在系统范围内唯一的键值生成的,而键值可以看作对应系统内的一条路经。


1、读写操作的结构体

    struct msgbuf

 {

    long mtype;

   char mtext[1];

   }

mtype成员代表消息类型,从消息队列中读取消息的一个重要依据就是消息的类型;mtext是消息内容,当然长度不一定为1。因此,对于发送消息来说,首先预置一个msgbuf缓冲区并写入消息类型和内容,调用相应的发送函数即可;对读取消息来说,首先分配这样一个msgbuf缓冲区,然后把消息读入该缓冲区即可。

 

1)int msgget(key_t key, int msgflg)

参数key是一个键值,由ftok获得;msgflg参数是一些标志位。该调用返回与健值key相对应的消息队列描述字。

在以下两种情况下,该调用将创建一个新的消息队列:

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

       参数msgflg可以为以下:IPC_CREAT、IPC_EXCL、IPC_NOWAIT或三者的或结果。msgget函数取得的是标识符而不是文件描述符,所以不可用select活poll。

调用返回:成功返回消息队列描述字,否则返回-1。

注:参数key设置成常数IPC_PRIVATE并不意味着其他进程不能访问该消息队列,只意味着即将创建新的消息队列。

2)int msgrcv(int msqid, struct msgbuf *msgp, int msgsz, long msgtyp, int msgflg);
该系统调用从msgid代表的消息队列中读取一个消息,并把消息存储在msgp指向的msgbuf结构中。

msqid为消息队列描述字;消息返回后存储在msgp指向的地址,msgsz指定msgbuf的mtext成员的长度(即消息内容的长度),msgtyp为请求读取的消息类型;读消息标志msgflg可以为以下几个常值的或:

  • IPC_NOWAIT 如果没有满足条件的消息,调用立即返回,此时,errno=ENOMSG
  • IPC_EXCEPT 与msgtyp>0配合使用,返回队列中第一个类型不为msgtyp的消息
  • IPC_NOERROR 如果队列中满足条件的消息内容大于所请求的msgsz字节,则把该消息截断,截断部分将丢失。

 

msgrcv手册中详细给出了消息类型取不同值时(>0; <0; =0),调用将返回消息队列中的哪个消息。

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

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

 

调用返回:成功返回读出消息的实际字节数,否则返回-1。

3)int msgsnd(int msqid, struct msgbuf *msgp, int msgsz, int msgflg);
向msgid代表的消息队列发送一个消息,即将发送的消息存储在msgp指向的msgbuf结构中,消息的大小由msgze指定。

对发送消息来说,有意义的msgflg标志为IPC_NOWAIT,指明在消息队列没有足够空间容纳要发送的消息时,msgsnd是否等待。造成msgsnd()等待的条件有两种:

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


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

  1. 不满足上述两个条件,即消息队列中有容纳该消息的空间;
  2. msqid代表的消息队列被删除;
  3. 调用msgsnd()的进程被信号中断;

 

调用返回:成功返回0,否则返回-1。

4)int msgctl(int msqid, int cmd, struct msqid_ds *buf);
该系统调用对由msqid标识的消息队列执行cmd操作,共有三种cmd操作:IPC_STAT、IPC_SET 、IPC_RMID。

  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标识的消息队列;

例程一:

C代码
  1. /*msgserver.c*/  
  2.   
  3. #include <stdlib.h>  
  4. #include <string.h>  
  5. #include <errno.h>  
  6. #include <sys/types.h>  
  7. #include <sys/ipc.h>  
  8. #include <sys/msg.h>  
  9. #include <sys/stat.h>  
  10.   
  11. #define   MSG_FILE "msgserver.c"   
  12. #define   BUFFER 255   
  13. #define   PERM S_IRUSR|S_IWUSR   
  14. /* 服务端创建的消息队列最后没有删除,我们要使用ipcrm命令来删除的 */  
  15. /* ipcrm -q <msqid> */  
  16.   
  17. struct msgtype   
  18. {   
  19.     long mtype;   
  20.     char buffer[BUFFER+1];   
  21. };   
  22.   
  23. int main()   
  24. {   
  25.     struct msgtype msg;   
  26.     key_t key;   
  27.     int msgid;   
  28.       
  29.     if((key=ftok(MSG_FILE,'a'))==-1)   
  30.     {   
  31.         fprintf(stderr,"Creat Key Error:%s/n", strerror(errno));   
  32.         exit(1);   
  33.     }   
  34.   
  35.     if((msgid=msgget(key, PERM|IPC_CREAT|IPC_EXCL))==-1)   
  36.     {  
  37.         fprintf(stderr, "Creat Message Error:%s/n", strerror(errno));   
  38.         exit(1);  
  39.     }   
  40.     printf("msqid = %d/n", msgid);  
  41.     while(1)  
  42.     {   
  43.         msgrcv(msgid, &msg, sizeof(struct msgtype), 1, 0);   
  44.         fprintf(stderr,"Server Receive:%s/n", msg.buffer);   
  45.         msg.mtype = 2;   
  46.         msgsnd(msgid, &msg, sizeof(struct msgtype), 0);   
  47.     }   
  48.     exit(0);   
  49. }   

 

C代码
  1. /* msgclient.c */  
  2.   
  3. #include <stdio.h>  
  4. #include <stdlib.h>  
  5. #include <string.h>  
  6. #include <errno.h>  
  7. #include <sys/types.h>  
  8. #include <sys/ipc.h>  
  9. #include <sys/msg.h>  
  10. #include <sys/stat.h>  
  11.   
  12. #define   MSG_FILE "msgserver.c"   
  13. #define   BUFFER 255   
  14. #define   PERM S_IRUSR|S_IWUSR   
  15.   
  16. struct msgtype {   
  17.     long mtype;   
  18.     char buffer[BUFFER+1];   
  19. };   
  20.   
  21. int main(int argc, char **argv)   
  22. {   
  23.     struct msgtype msg;   
  24.     key_t key;   
  25.     int msgid;   
  26.       
  27.     if(argc != 2)   
  28.     {   
  29.         fprintf(stderr,"Usage:%s string/n", argv[0]);   
  30.         exit(1);   
  31.     }   
  32.       
  33.     if((key=ftok(MSG_FILE,'a'))==-1)   
  34.     {   
  35.         fprintf(stderr,"Creat Key Error:%s/n", strerror(errno));   
  36.         exit(1);   
  37.     }   
  38.       
  39.     if((msgid=msgget(key, PERM))==-1)   
  40.     {   
  41.         fprintf(stderr,"Creat Message  Error:%s/n", strerror(errno));   
  42.         exit(1);   
  43.     }   
  44.       
  45.     msg.mtype = 1;   
  46.     strncpy(msg.buffer, argv[1], BUFFER);   
  47.     msgsnd(msgid, &msg, sizeof(struct msgtype), 0);    
  48.     memset(&msg, '/0'sizeof(struct msgtype));   
  49.     msgrcv(msgid, &msg, sizeof(struct msgtype), 2, 0);   
  50.     fprintf(stderr, "Client receive:%s/n", msg.buffer);   
  51.     exit(0);  

例程二:

#include <unistd.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
static struct msgbuf1
{
     long mtype;
     char mtext[100];
} sndbuf, rcvbuf, *msgp ;

extern int errno;

int   main(int argc, char **argv)
{
     int rtrn, msqid ;
     char name[10];
     double balance;
   
     if (argc!=2)
     {
      fprintf(stderr,"msgreq [01-99]/n"); exit(-1);
     }
     if ((msqid = msgget(0x888, IPC_CREAT|0660)) == -1 )
     {
        fprintf(stderr, "msgget 888 failed !/n");
        //exit(-1);
     }
     msgp=&sndbuf;

     sprintf(sndbuf.mtext,"%2.2s",argv[1]);
     printf("输入4位帐号:");
     scanf("%s",&sndbuf.mtext[2]);

     sndbuf.mtext[6]=0;
     msgp->mtype=666;
     rtrn=msgsnd(msqid,msgp, strlen(sndbuf.mtext), 0);
     if (rtrn==-1)
     {
         perror("msgsnd"); exit(-1);
      }
     msgp=&rcvbuf;
   
     fprintf(stderr,"等待后台数据处理进程的回答....");

     rtrn=msgrcv(msqid,msgp, 100, atoi(argv[1]), 0);
     if(rtrn==-1)
     {
      perror("msgrcv"); exit(-1);
     }

     sscanf(rcvbuf.mtext,"%[^|]|%lf",name,&balance);
     printf("/n姓名=%s/n",name);
     printf("余额=%lf/n",balance);
}

/*服务方程序msgcenter.c*/
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

static struct msgbuf1
{
     long mtype;
     char mtext[100];
} sndbuf, rcvbuf , *msgp;

extern int errno;

int main()
{
     int rtrn, msqid ;
     char strbuf[100];
   
     if ( (msqid = msgget(0x888, IPC_CREAT|0600)) == -1 )
     {
         fprintf(stderr, "msgget 888 failed !/n"); exit(-1);
     }

     while(1)
     {
         msgp=&rcvbuf;
         fprintf(stderr,"等待前台进程的请求....");
       
         rtrn=msgrcv(msqid, msgp, 100, 666 ,MSG_NOERROR);  
         if(rtrn==-1)
         {
             perror("msgrcv");exit(-1);
         }
         msgp=&sndbuf;
         sprintf(strbuf,"%2.2s/0",rcvbuf.mtext);
         msgp->mtype=atoi(strbuf);
         printf("/n输入帐号=%4.4s的帐户姓名:",&rcvbuf.mtext[2]);
       
         scanf("%s",sndbuf.mtext);
         strcat(sndbuf.mtext,"|");
         printf("输入该帐户余额:");
         scanf("%s",strbuf);
         strcat(sndbuf.mtext,strbuf);
         rtrn=msgsnd(msqid,msgp, strlen(sndbuf.mtext), 0);
         if (rtrn==-1)
         {
             perror("msgsnd");
             exit(-1);
         }
     }
}