linux IPC之消息队列

来源:互联网 发布:transmit mac 破解 编辑:程序博客网 时间:2024/04/30 12:26

消息队列相关概念

消息队列就是一个消息的链表。可以把消息看作一个记录,具有特定的格式以及特定的优先级。对消息队列有写权限的进程可以向其中按照一定的规则添加新消息;对消息队列有读权限的进程则可以从消息队列中读走消息。在linux系统中消息队列与键值一一对应。消息队列是通过链表管理的,内核提供一个struct msqid_ds *msgque[MSGMNI]向量维护内核的一个消息队列列表,因此linux系统支持的最大消息队列数由msgque数组大小来决定,每一个msqid_ds表示一个消息队列,并通过msqid_ds.msg_first、msg_last维护一个先进先出的msg链表队列,当发送一个消息到该消息队列时,把发送的消息构造成一个msg结构对象,并添加到msqid_ds.msg_first、msg_last维护的链表队列,同样,接收消息的时候也是从msg链表队列尾部查找到一个msg_type匹配的msg节点,从链表队列中删除该msg节点,并修改msqid_ds结构对象的数据。键值即是数组msgque的索引。
msqid_ds 与msg相关的关系如下图所示:
msqid_ds-msg_

消息队列的使用步骤

  1. 获取键值
    key_t ftok(char *pathname, char proj)
    头文件为”sys/ipc.h”。返回文件名对应的键值,失败返回 -1。proj是消息队列名字,不能为空。fname是指定的文件名(已经存在的文件名)。需要有-t 的权限,或用root权限执行,通常设为/tmp。
  2. 创建/打开消息队列
    int msgget(key_t key, int msgflg)
    头文件为sys/msg.h。key由ftok获得。
    msgflg有:
    IPC_CREAT 创建新的消息队列,应配有文件权限0666。
    IPC_EXCL 与IPC_CREAT一同使用,表示如果要创建的消息队列已经存在,则返回错误。
    IPC_NOWAIT 读写消息不阻塞。
    当没有与key相对应的消息队列并且msgflg中包含了IPC_CREAT标志 或 key的参数为IPC_PRIVATE 时,创建一个新的消息队列。
  3. 发送消息
    int msgsnd(int msqid, struct msgbuf *msgp, int msgsz, int msgflg)
    向消息队列发送一条消息。参数msqid为消息队列的id,msgp为存放消息的结构体。msgsz是消息的长度,和数组的大小不一样哦。msgflg为消息标志,通常为0,也可以为IPC_NOWAIT。出错返回 -1。
    消息格式
    struct msgbuf {
    long mtype;
    char mtext[100];
    };
  4. 接收消息
    int msgrcv(int msqid, struct msgbuf *msgp, int msgsz, long msgtyp, int msgflg)
    参数msqid代表的消息队列中读取一个msgtyp类型的消息,并把消息存储在msgp指定的msgbuf结构中。读取后队列中的消息将会删除。size为结构体中数据的大小,不要计算msgtyp。出错返回 -1。
  5. 设置消息队列属性
    int msgctl ( int msgqid, int cmd, struct msqid_ds *buf );
    参数:msgctl 系统调用对 msgqid 标识的消息队列执行 cmd 操作,系统定义了 3 种 cmd 操作: IPC_STAT , IPC_SET , IPC_RMID
    IPC_STAT : 该命令用来获取消息队列对应的 msqid_ds 数据结构,并将其保存到 buf 指定的地址空间。
    IPC_SET : 该命令用来设置消息队列的属性,要设置的属性存储在buf中。
    IPC_RMID : 从内核中删除 msqid 标识的消息队列。

示列

消息发送端:send.c

/*send.c*/  #include <stdio.h>   #include <sys/types.h>   #include <sys/ipc.h>   #include <sys/msg.h>   #include <errno.h>   #define MSGKEY 1024   struct msgstru  {     long msgtype;     char msgtext[2048];   };  main()  {    struct msgstru msgs;    int msg_type;    char str[256];    int ret_value;    int msqid;    msqid=msgget(MSGKEY,IPC_EXCL);  /*检查消息队列是否存在*/    if(msqid < 0){      msqid = msgget(MSGKEY,IPC_CREAT|0666);/*创建消息队列*/      if(msqid <0){      printf("failed to create msq | errno=%d [%s]\n",errno,strerror(errno));      exit(-1);      }    }     while (1){      printf("input message type(end:0):");      scanf("%d",&msg_type);      if (msg_type == 0)         break;      printf("input message to be sent:");      scanf ("%s",str);      msgs.msgtype = msg_type;      strcpy(msgs.msgtext, str);      /* 发送消息队列 */      ret_value = msgsnd(msqid,&msgs,sizeof(struct msgstru),IPC_NOWAIT);      if ( ret_value < 0 ) {         printf("msgsnd() write msg failed,errno=%d[%s]\n",errno,strerror(errno));         exit(-1);      }    }    msgctl(msqid,IPC_RMID,0); //删除消息队列   }

消息接收端 receive.c

/*receive.c */  #include <stdio.h>   #include <sys/types.h>   #include <sys/ipc.h>   #include <sys/msg.h>   #include <errno.h>   #define MSGKEY 1024   struct msgstru  {     long msgtype;     char msgtext[2048];  };  /*子进程,监听消息队列*/  void childproc(){    struct msgstru msgs;    int msgid,ret_value;    char str[512];    while(1){       msgid = msgget(MSGKEY,IPC_EXCL );/*检查消息队列是否存在 */       if(msgid < 0){          printf("msq not existed! errno=%d [%s]\n",errno,strerror(errno));          sleep(2);          continue;       }       /*接收消息队列*/       ret_value = msgrcv(msgid,&msgs,sizeof(struct msgstru),0,0);       printf("text=[%s] pid=[%d]\n",msgs.msgtext,getpid());    }    return;  }  void main()  {    int i,cpid;    /* create 5 child process */    for (i=0;i<5;i++){       cpid = fork();       if (cpid < 0)          printf("fork failed\n");       else if (cpid ==0) /*child process*/          childproc();    }  }
0 0
原创粉丝点击