Linux17 --- 消息队列

一、IPC进程间通信:消息队列

消息队列是在两个进程之间传递二进制块数据的一种简单有效的方式。每个数据块都有一个特定的类型,接收方可以根据类型来有选择地接收数据,而不一定像管道和命名管道那样必须以先进先出的方式接收数据。

Linux消息队列的API都定义在sys/msg.h头文件中,包括4个系统调用:msgget、msgsnd、msgrcv 和 msgctl。

在这里插入图片描述

1、相关系统调用:

1)msgget系统调用 – 创建或获取系统调用

msgget系统调用创建一个消息队列,或者获取一个已有的消息队列
其定义如下:
在这里插入图片描述
和semget系统调用一样,key参数是一个键值,用来标识一个全局唯一的消息队列。msgflg参数的使用和含义与semget系统调用的sem_flags参数相同。
msgget成功时返回一个正整数值,它是消息队列的标识符。msgget失败时返回-1,并设置errno。
如果msgget用于创建消息队列,则与之关联的内核数据结构msqid_ds将被创建并初始化。msqid_ds结构体的定义如下:
在这里插入图片描述

2)msgsnd 系统调用 – 添加到队列

msgsnd系统调用把一条消息添加到消息队列中。
其定义如下:
在这里插入图片描述
msqid 参数是由 msgget 调用返回的消息队列标识符。
msg_ptr 参数指向一个准备发送的消息,消息必须被定义为如下类型:
在这里插入图片描述
其中,mtype成员指定消息的类型,它必须是一个正整数。mtext是消息数据。msg_sz参数是消息的数据部分(mtext)的长度。这个长度可以为0,表示没有消息数据。
msgflg参数控制msgsnd的行为。它通常仅支持IPC_NOWAIT标志,即以非阻塞的方式发送消息。默认情况下,发送消息时如果消息队列满了,则msgsnd将阻塞。若IPCNOWAIT标志被指定,则msgsnd将立即返回并设置errno为EAGAIN。

处于阻塞状态的msgsnd调用可能被如下两种异常情况所中断:
1)消息队列被移除。此时msgsnd调用将立即返回并设置errno为EIDRM。
2)程序接收到信号。此时msgsnd调用将立即返回并设置errno为EINTR。
msgsnd成功时返回0,失败则返回-1并设置errno。msgsnd成功时将修改内核数据结构msqid_ds的部分字段,如下所示:
将msg_qnum 加1。
将msg_lspid 设置为调用进程的PID。
将msg_stime设置为当前的时间。

3)msgrcv 系统调用 – 获取消息

msgrcv系统调用从消息队列中获取消息。
其定义如下:
在这里插入图片描述
msqid参数是由msgget调用返回的消息队列标识符。
msg_ptr参数用于存储接收的消息,msg_sz参数指的是消息数据部分的长度。
msgtype参数指定接收何种类型的消息。我们可以使用如下几种方式来指定消息类型:

msgtype等于0。读取消息队列中的第一个消息。
msgtype大于0。读取消息队列中第一个类型为msgtype的	消息(除非指定了标志MSG_EXCEPT,见后文)。
msgtype小于0。读取消息队列中第一个类型值比msgtype		的绝对值小的消息。参数msgflg 控制msgrcv函数的行为。它	可以是如下一些标志的按位或:
IPC_NOWAIT。如果消息队列中没有消息,则msgrcv调用立即返回并设置errno为ENOMSG。
MSG_ EXCEPT。如果msgtype大于0,则接收消息队列中第一个非 msgtype类型的消息。
MSG NOERROR。如果消息数据部分的长度超过了msg_sz,就将它截断。

处于阻塞状态的msgrcv调用还可能被如下两种异常情况所中断:

消息队列被移除。此时msgrcv调用将立即返回并设置errno为EIDRM。
程序接收到信号。此时msgrcv调用将立即返回并设置errno为 EINTR。

msgrcv成功时返回0,失败则返回-1并设置errno。msgrcv成功时将修改内核数据结构msqid_ds的部分字段,如下所示:

将msg_qnum减1。
将msg_lrpid设置为调用进程的PID。
将msg_rtime设置为当前的时间。

4)msgctl – 控制某些属性

msgctl 系统调用控制消息队列的某些属性。
其定义如下:
在这里插入图片描述
msqid参数是由msgget调用返回的共享内存标识符。command参数指定要执行的命令。msgctl支持的所有命令如表13-4所示。
在这里插入图片描述
在这里插入图片描述
msgctl成功时的返回值取决于command参数,如表13-4所示。msgctl函数失败时返回-1并设置errno。

2、代码使用示例:

1)a.c --创建消息队列,并添加一个消息

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

//定义消息体
struct mess//由用户自己定义,只需要的一个成员为long type,长整形;代表消息类型。结构体名等都可自定义
{
    long type;
    char date[128];
};

int main()//创建消息队列,并添加一个消息
{
    int msgid = msgget((key_t)1235,IPC_CREAT|0600);//创建消息队列,IPC..为标志位,0600为权限
    
    if(msgid == -1)//判断是否创建成功
    {
        exit(0);
    }

    //添加消息
    //先定义一个结构体变量,用来表示消息
    struct mess dt;
    //给消息赋消息类型值
    dt.type = 1;//这里赋值为一号类型消息
    strcpy(dt.date,"hello1");//这里直接写入一个消息
    //将结构体添加到消息队列中
    msgsnd(msgid,(void *)&dt,128,0);//这里的128是数据部分的大小
}

其中4d3为1235,进程已经结束,但消息队列依旧存在。多次运行不会多次创建消息队列,但会多次写入数据。
在这里插入图片描述

2)b.c – 获取消息队列,并读取一个消息

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

//定义消息体
struct mess//由用户自己定义,只需要的一个成员为long type,长整形;代表消息类型。结构体名等都可自定义
{
    long type;
    char date[128];
};

int main()//获取消息队列,并读取一个消息
{
    int msgid = msgget((key_t)1235,IPC_CREAT|0600);//创建消息队列,IPC..为标志位,0600为权限
    
    if(msgid == -1)//判断是否创建成功
    {
        exit(0);
    }

    //读取消息
    //先定义一个结构体变量,用来表示消息
    struct mess dt;
    //获取消息
    msgrcv(msgid,(void*)&dt,128,1,0);//这里的128是消息部分的大小,不是结构体的大小
    printf("read:%s\n",dt.date);
    

}

每运行一次b,其就会从其所获取的消息队列中读取一个相对应信号类型的消息,如果没有,就会阻塞,等待另外进程写入,再读取。

msgrcv(msgid,(void*)&dt,128,1,0);

如果将第四个参数改为0,就回不区分消息类型,就会读取任意类型的消息。发送端不能发送0号类型消息,但接收端可以。

在这里插入图片描述

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值