linux IPC之消息队列

消息队列相关概念

消息队列就是一个消息的链表。可以把消息看作一个记录,具有特定的格式以及特定的优先级。对消息队列有写权限的进程可以向其中按照一定的规则添加新消息;对消息队列有读权限的进程则可以从消息队列中读走消息。在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
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值