Linux进程间通信-消息队列

首先上篇文章我们说到了Linux下进行进程间通信的一种方法或机制匿名管道和命名管道,那么这里要说的是另外一种与之不同的通信方法,即消息队列,两者之间有相同也有不同的地方,具体的下面就一一介绍。


一、什么是消息队列?

首先它也是一种进行进程间通信的方式,通过一个进程向另外一个进程发送数据块的方式,每个数据块都被认为是有一个类型的,而接受者进程接收的数据块可以有不同的类型。

二、消息队列结构体

cat /usr/include/linux/msg.h

通过这个命令就可以查询到消息队列的数据结构,如下图:

这里写图片描述

该结构体第一条是一个IPC结构体,是所有IPC机制(消息队列,信号量,共享内存)所共有的,可以看出消息队列的底层是通过单链表来实现的。

三、消息队列相关的函数

在了解了底层的实现机制和它的通信原理后,所要掌握的就是相关的函数的使用了,所以得先搞清楚都有些什么函数,具体来怎么实现:

1、创建新的消息队列或获取已存在的消息队列

int msgget(key_t key,int msgflg);

这里的key是一个键,用来标识某个特定的消息队列。
而msgflg呢,包含两个标识符IPC_CREAT和IPC_EXCL ,IPC_CREAT的意思为,如果IPC不存在,则创建一个IPC资源。而IPC_EXCL则是只有在共享内存不存在的时候,新的共享内存才建立,否则产生错误。一般情况下两者必须同时存在,才能保证所得对象是新建的。

2、消息的读/写

读取消息

ssize_t msgrcv(int msqid,void *msgp,size_t msgsz,long msgtyp,int msgflg);

放入消息

int msgsnd(int msqid,const void *msgp,size_t msgsz,int msgflg);

msqid就是消息队列的标识码,msgp是指向消息缓冲区的指针,这个位置是用来暂时存储发送和接收的消息,是一个用户可自定义的结构:

struct msgstru
{
    long mtype;//必须大于0
    char mtext[用户指定的大小]
}

msgsz是消息的大小,msgtyp是读取消息的形态,如果为0.则会读取队列里所有的消息。

msgflg则是表明在队列里没有数据的情况下程序所采取的行动,如果msgflg和常数IPC_NOWAIT合用,则在执行msgsnd()执行时若消息队列已满,则msgsnd()将不会阻塞,而会立即返回-1,如果执行的是msgrcv(),则在消息队列为空时,直接返回-1,当msgflg为0时,则当队列呈满或呈空时,采取阻塞等待的处理方式。

3、消息队列的属性

int msgctl(int msgqid,int cmd,struct msqid_ds *buf);

cmd是所要执行的操作,一共定义了三种:
IPC_STAT:获取消息队列中对应的msqid_ds结构,将其保存到buf中。
IPC_SET:所要设置的属性,然后保存在buf中。
IPC_RMID:从内核中删除msqid标识的消息队列。

4、ftok函数

key_t ftok(const char *pathname,int proj_id);

通过ftok函数可以将一个已存在路径和一个整数标识转换为一个key_t的值,就是用来标识特定消息的。

四、代码测试,熟悉接口。

接收消息端:msg_receive.c

#include<unistd.h>
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#include<errno.h>
#include<sys/msg.h>
#define FILEPATH "."
#define ID 0

struct msg_info
{
    long int msg_type;
    char mtext[BUFSIZ]; 
};

int main()
{
    int msgid = -1;
    struct msg_info data;
    long int msgtype = 0;   

    key_t _key = ftok(FILEPATH,ID);//创建键值
    if(_key < 0)
    {
        printf("Get key id error!\n");
        return 1;               
    }

    msgid = msgget(_key,0666 | IPC_CREAT);//获取信号队列ID
    if(msgid == -1)
    {
        printf("Msgrcv get key errno!\n");  
        return 1;
    }

    while(1)
    {
        if(msgrcv(msgid,(void*)&data,BUFSIZ,msgtype,0) == -1)   
        {
            printf("Msgrcv get key errno!\n");
            return 1;
        }

        printf("You wrote:%s\n",data.mtext);

        if(strncmp(data.mtext,"end",3) == 0)
            break;
    }

    if(msgctl(msgid,IPC_RMID,0) == -1)
    {
        printf("Msgctl  errno!\n"); 
        return 1;   
    }

    return 0;
}

发送消息端:msg_send.c

#include<unistd.h>
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#include<sys/msg.h>
#include<errno.h>

#define FILEPATH "."将路径定义为当前目录
#define ID 0
#define MAX_MTEXT 1024

struct msg_info
{
    long int msg_type;
    char mtext[MAX_MTEXT];  
};

int main()
{
    struct msg_info data;
    char buffer[BUFSIZ];    
    int msgid = -1;

    key_t _key = ftok(FILEPATH,ID);
    if(_key < 0)
    {
        printf("Get key errno!\n");
        return 1;   
    }

    msgid = msgget(_key,0666 | IPC_CREAT);
    if(msgid == -1)
    {
        printf("Msgget error!\n");
        return 1;   
    }

    while(1)
    {
        printf("Enter some message!\n");
        fflush(stdout);
        fgets(buffer,BUFSIZ,stdin);
        data.msg_type = 1;
        strcpy(data.mtext,buffer);

        if(msgsnd(msgid,(void*)&data,MAX_MTEXT,0) == -1)
        {
            printf("Msgsnd failed!\n");
            return 1;   
        }

        if(strncmp(buffer,"end",3) == 0)
            break;

            sleep(1);
    }

    return 0;
}

程序的结果如下图:
这里写图片描述

这样就实现了两个进程间的通信。

五、和管道之间的异同

相同:每个消息它也是有最大长度的限制的,所以两者在字节限制这块是相同的,并且都可以实现两个不相干进程间的信息传递,在管道中我们也通过小例子来测出了它的大小,下面就看下系统中各自的分配的大小:
可以发现这里的65536和上节管道里测出的相同(没有问题吧!^_^)

不同:消息队列是基于消息的,所以它的读取不一定是先入先出,是带有选择性的。而管道是基于字节流的,这样消息队列就有效避免了命名管道的同步和阻塞问题。

这里写图片描述


最后给出两个程序的Makefile:

.PHONY:all
all:msg_send msg_receive

msg_send:msg_send.c
    gcc -o $@ $^

msg_receive:msg_receive.c
    gcc -o $@ $^
.PHONY:clean
clean:
    rm -f msg_send msg_receive
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值