linux进程间通信:消息队列

一、消息队列的概念

  消息队列:内核地址空间中的内部链表,通过linux内核在各个进程之间传递内容。
  消息顺序地发送到消息队列中,并以几种不同的方式从队列中获取;消息队列间通过IPC的标识符进行区别;不同的消息队列相互独立;每个消息队列中的消息构成一个独立的链表。

二、相关数据结构

定义位置:<linux/msg.h>

stuct msgbuf{
    long mtype;     //用正数表示的消息类型
    char mtext[1];  //消息数据
};

消息大小限制:<Linux/msg.h>    #define MSGMAX 8192
MSGMAX:mtype + mtext


  msgid_ds结构的IPC对象分为3类,消息队列对应的类型结构如下:
定义位置:<Linux/msg.h>

struct msqid_ds
{
    struct ipc_perm msg_perm;
    time_t msg_stime;       //发送到队列的最后一个消息的时间戳
    time_t msg_rtime;       //从队列中获取的最后一个消息的时间戳
    time_t msgg_ctime;      //对队列进行最后一次变动的时间戳
    unsigned long __msg_cbytes;     //在队列上所驻留的字节总数
    msgqnum_t msg_qnum;     //当前处于队列中的消息数目
    msglen_t msg_qbytes;    //队列中能容纳的字节的最大数目
    pid_t msg_lspid;        //发送最后一个消息进程的PID
    pid_t msg_lrpid;        //接收最后一个消息进程的PID
};

  ipc_perm:内核IPC对象的许可权限的存储结构。
定义位置:<linux/ipc.h>

struct ipc_perm
{
    key_t key;      //函数msgget()使用的键值
    uid_t uid;      //用户的UID
    gid_t gid;      //用户的GID
    uid_t cuid;     //创建者的UID
    gid_t cgid;     //创建者的GID
    unsigned short mode;    //权限
    unsigned short seq;     //序列号
};

三、相关函数

函数原型key_t ftok(const char *pathname, int proj_id)
头文件<sys/types.h><sys/ipc.h>
参数说明
pathname生成key所依据的路径
proj_id生成key所依据的id值
返回值成功:key值,失败:-1

描述:
  ftok()接口使用文件(必须为已存在且可访问的文件)标识与proj_id(不能为0)的低八位生成系统IPC键值,适用于msgget(),semget()或shmget()。当pathname与proj_id相同时,返回的结果总是相同的。当pathname或proj_id任一参数不同时,返回值应该是不同的。(不能确保不同)
  在libc4和libc5下该函数的原型为:
    key_t ftok(char *pathname, char proj_id)。现在,虽然proj_id是int型的,但是仍然仅有8位被使用,典型的使用方法会用一个ASCII的字符来为proj_id赋值,这也就是为何proj_id值为0被视为未定义的行为。
  此接口并不保证结果唯一,最好的一种解决方案为:将proj_id的字节、inode的低16位与设备编号的低8位结合起来放入一个32位的结果当中。


函数原型int msgget(key_t key, int msgflg)
头文件<sys/types.h><sys/ipc.h><sys/msg.h>
参数说明
key消息队列的键值
msgflg操作key对应的动作
返回值成功:消息队列标识符,失败:-1

描述:
  msgget()系统调用返回与key相关联的消息队列标识符。用于创建或访问一个消息队列。若key有值IPC_PRIVATE。若消息队列不存在,则IPC_CREAT在msgflg中被指定。
  若IPC_CREAT和IPC_EXCL同时在msgflg中被指定,且对应的消息队列已存在,则该调用出错。
  在创建的时候,msgflg的低位定义了消息队列的权限。该权限的格式与open()接口的mode中权限格式相同。
  当新的消息队列被创建时,msqid_ds结构被如下初始化:
    msg_perm.cunid 和 msg_perm.uid:调用进程的用户ID
    msg_perm.cgid 和 msg_perm.gid:调用进程的用户组ID
    msg_perm.mode的低9位:msgflg的低九位
    msg_qnum,msg_lspid,msg_lrpid,msg_stime 和 msg_rtime被置为0
    msg_ctime被置为当前时间
    msg_qbytes被置为系统限制值:MSGMNB


函数原型int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg)
头文件<sys/types.h><sys/ipc.h><sys/msg.h>
参数说明
msqid消息队列id
msgp消息结构内存首地址
msgsz消息的大小
msgflg调用标志
返回值成功:0,失败:-1

msgflg:
  IPC_NOWAIT:若队列空间充足,则随即执行并返回。否则,执行失败并返回EAGAIN。若不指定该选项,则在队列空间不足时,执行阻塞至空间充足后继续执行。


函数原型ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg)
头文件<sys/types.h><sys/ipc.h><sys/msg.h>
参数说明
msqid消息队列id
msgp存储消息的缓存首地址
msgsz指定缓冲区msgp最大字节数
msgtyp指定消息类型
msgflg指定附加动作
返回值成功:拷贝到队列中mtext中的字节数,失败:-1

msgsz:
  指定存储消息的缓冲区最大字节数,当取出的消息大小大于此值时,若在msgflg中指定MSG_NOERROR,则消息内容将被截断,截断的数据将被丢弃。若未指定MSG_NOERROR,则消息将不会被移除,调用失败并返回-1,同时错误码errno被置为E2BIG。
msgtyp:
  0:读取队列中第一条消息;
  大于0:读取队列中第一条类型为该值得消息。若msgflg中指定MSG_EXCEPT,则读取队列中第一条类型号不为该值得消息;
  小于0:读取队列中第一条类型值小于或等于该值得绝对值的最小类型消息。
msgflg:
  位掩码,由0或以下标志进行或(|)运算构成:
    IPC_NOWAIT:若无消息则立即返回,并设错误码为ENOMSG。
    MSG_EXCEPT:在msgtyp参数大于0下使用,读取队列中第一条类型与msgtyp不同的消息。
    MSG_NOERROR:在消息内容长度大于msgsz时对消息进行截断。
  当队列中没有请求的类型的消息时,且msgflg中未指定IPC_NOWAIT,调用进程将阻塞至下列任一情况发生:
    一条所需类型的消息被放入队列;
    消息队列被移除,此情况下,系统调用失败,并置错误码为EIDRM.
    调用进程捕获到信号。此情况下,系统调用失败并置错误码为EINTR.


|函数原型|int msgctl(int msqid, int cmd, struct msqid_ds *buf)|

头文件<sys/types.h><sys/ipc.h><sys/msg.h>
参数说明
msqid消息队列id
cmd对队列的操作
buf结果存储缓冲区
返回值成功:IPC_STAT,IPC_SET,IPC_RMID返回0,失败:-1

  msgctl()通过cmd中指定的操作对消息队列进行控制。
cmd:
  IPC_STAT:在调用者对消息队列有读取权限的前提下,将内核中与指定队列id相关的数据结构拷贝至buf中。
  IPC_SET:将buf指向的msqid_ds结构找中的一些成员拷贝到内核与消息队列对应的数据结构中。结构中被更新的成员:msg_ctime,msg_qbytes,msg_perm.uid,msg_perm.gid,msg_perm.mode。调用进程的有效UID必须与msg_perm.uid或msg_perm.cuid相匹配,或者该用户拥有对应的特权。在将msg_qbytes提升至超过MSGMNB的值得时候,需要对应的特殊权限。
  IPC_RMID:移除消息队列,并唤醒所有等待中的读/写进程。主调进程需要对应的特权,或者是队列的拥有者或者创建者。

四、代码简例

#include<iostream>
#include<unistd.h>
#include<stdio.h>
#include<string.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>

struct my_msg
{
    long mtype;
    char mtext[512];
    int num;
};


int main()
{
    key_t msg_key = ftok(".", 'a');
    //generate message queue key
    if(-1 == msg_key)
    {
        std::cout << __LINE__;
        perror("Create msg_key:");
        return -1;
    }
    //create message queue
    int msg_qid = msgget(msg_key, IPC_CREAT);
    if(-1 == msg_qid)
    {
        std::cout << __LINE__;
        perror("Create message queue:");
        return -1;
    }
    pid_t pid = fork();
    if(-1 == pid)
    {
        std::cout << __LINE__;
        perror("Fork:");
        return -1;
    }
    if(0 < pid)
    {
        //父进程进行读取操作
        ssize_t nbytes = 0;
        char buf[1024];
        while(true)
        {
            nbytes = msgrcv(msg_qid, buf, sizeof(buf), 0, 0);
            if(-1 == nbytes)
            {
                std::cout << __LINE__;
                perror("Msgrcv:");
            }
            else
            {
                struct my_msg *msg = (struct my_msg*)buf;
                std::cout<< "msg type: " << msg->mtype << " metxt: " << msg->mtext << std::endl;
                if(5 == msg->mtype)
                {
                    msgctl(msg_qid, IPC_RMID, NULL);
                    std::cout << "Rove the message queue & exit" << std::endl;
                    return 0;
                }
            }
        }
    }
    else 
    {
        //子进程进行写入操作
        struct my_msg msg;
        int cnt = 1;
        for(int i = 1; i < 6; i ++)
        {
            msg.mtype = i;
            msg.num = cnt++;
            strcpy(msg.mtext, "My self defined msg queue.");
            int res = msgsnd(msg_qid, (void*)(&msg), sizeof(msg), 0);
            if(-1 == res)
            {
                std::cout << __LINE__;
                perror("Msg send:");
            }
            else 
            {
                std::cout << "-----------send: " << msg.mtext << std::endl;
            }
        }
    }
}

执行结果:

[pig@localhost code]$ g++ msgque.cpp 
[pig@localhost code]$ sudo ./a.out 
-----------send: My self defined msg queue.
-----------send: My self defined msg queue.
-----------send: My self defined msg queue.
-----------send: My self defined msg queue.
-----------send: My self defined msg queue.
msg type: 1 metxt: My self defined msg queue.
msg type: 2 metxt: My self defined msg queue.
msg type: 3 metxt: My self defined msg queue.
msg type: 4 metxt: My self defined msg queue.
msg type: 5 metxt: My self defined msg queue.
Rove the message queue & exit
[pig@localhost code]$ 
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值