进程间通信-消息队列

概述

        消息队列是消息的链接表 , 存放在内核中并由消息队列标识符标识。我们将称消息队列为 “ 队列 ,其标识符为 队列 I D” m s g g e t 用于创建一个新队列或打开一个现存的队列。 msgsnd 用于将新消息添加到队列尾端。每个消息包含一个正长整型类型字段,一个非负长度以及实际数据字节(对应于长度),所有这些都在将消息添加到队列 时,传送给 msgsnd msgrcv 用于从队列中取消息。我们并不一定要以先进先出次序取消息,也可以按消息的类型字段取消息.
        linux 消息队列内部实现为一个带头节点的链表 ( 内核中的链表 ), 提供了很多函数可以去操作这个链表, 链表结构如下

struct msg //消息结点
{
    struct msg *next; //下一个消息
    long type;//消息的类型
    long length;//该消息的长度
    char data[];//消息的内容
}
struct msqid_ds // "头结点" ---->表示一个消息队列
{
    struct ipc_perm msg_perm; //该数据结构的读写权限
    struct msg *msg_first; //指向消息队列上的第一个消息
    struct msg *msg_last; //指向消息队列上的最后一个消息
    __kernel_time_t msg_stime; //最后发送消息到消息队列的时间
    __kernel_time_t msg_rtime; //最后从消息队列读取消息的时间
    __kernel_time_t msg_ctime; //最后效果结构体的时候
    unsigned long msg_lcbytes; /* Reuse junk fields for 32 bit */
    unsigned long msg_lqbytes; /* ditto */
    unsigned short msg_cbytes; //当前消息队列上面总字节数
    unsigned short msg_qnum; //消息个数
    unsigned short msg_qbytes; //消息队列的最大的总字节数
    __kernel_ipc_pid_t msg_lspid; //最后发送消息的进程的 pid
    __kernel_ipc_pid_t msg_lrpid; //最后接收消息的进程的 pid
}

 

        msqid_ds 消息队列数据结构:描述整个消息队列的属性,主要包括整个消息队列
的权限,拥有者、两个重要的指针分别指向消息队列的第一个消息和最后一个消息。

消息队列使用步骤

1.使用 ftok 先向内核申请一个 IPC key(钥匙,"许可证")
2.使用 msgget 打开或者创建一个 IPC 的“对象(msg)”
3.使用 msgsnd 和 msgrcv 读写信息

打开或创建一个消息队列的 IPC 对象

头文件:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
函数原型:
int msgget(key_t key, int msgflg);
作用:
        创建或打开一个用于消息队列的 IPC 对象
参数含义:
        key: system V IPC 的 key,一般由 ftok 生成
        msgflg: 标志位,分两种情况,创建的话为 IPC_CREAT|权限位,打开的话为 0
返回值:
        成功返回一个 system V 消息队列的 id
        失败返回-1,同时 errno 被设置
示例:
int mid = msgget(key,IPC_CREAT | 0664);
if(-1 == mid)
{
    perror("msgget error");
    exit(-1);
}

写入信息到消息队列上(msgsnd)

头文件:
​​​​​​​#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
函数原型:
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
作用:
        发送 msgp 消息到 msqid 指定的消息队列上
参数含义:
        msqid: 指定的消息队列的 id,表示你要把消息发送到哪一个消息队列,一般由msgget 函数生成
        msgp: 指向要发送的消息的结构体指针,该结构体具有定义如下(一般需要自己定义)
struct msgbuf //定义一个发送消息的结构体
{
    long mtype; //消息的类型,具体的含义是由程序设计者指定 >0
    char mtext[0]; //消息的内容,可长可短//“柔性数组”“0 长数组”
};
        msgsz:消息内容的长度,上面结构体中数组 mtext 的实际大小
        msgflg:阻塞标志位,为 0 表示阻塞模式,为 IPC_NOWAIT 表示非阻塞模式
返回值:
        成功返回 0,
        失败返回-1,同时 errno 被设置
示例:
struct msgbuf {
    long mtype; //消息的类型,具体的含义是由程序设计者指定 >0
    char mtext[0]; //消息的内容,可长可短//“柔性数组”“0 长数组”
};

//把信息发送到消息队列
char buf[256] = {0}; //用于保存发送的信息
printf("请输入发送的消息\n");
fgets(buf,256,stdin); //从标准输入中输入字符串到 buf 中
//分配消息结构体的空间(结构体本身和消息大小)
struct msgbuf *p = (struct msgbuf *)malloc(sizeof(struct msgbuf)+strlen(buf)+1);
if(p == NULL)
{
    perror("malloc error");
    exit(-1);
}
printf("请输入发送消息的类型\n");
scanf("%ld",&(p->mtype));
//p->mtype = 200;
memcpy(p->mtext,buf,strlen(buf)+1); //把 buf 空间上
strlen(buf)+1 大小的内容 copy 到 p->mtext 上去
int r = msgsnd(mid,(void *)p, strlen(buf)+1,0); //发送结构体上的消息到 mid 的消息队列上,以阻塞模式
if(r == -1)
{
    perror("msgsnd error");
    exit(-1);
}

读取信息到消息队列上(msgrcv)

头文件:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
函数原型:
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
作用:
        读取 msqid 指定消息队列的信息保存到 msgp 内存上
参数含义:
        msqid: 指定的消息队列的 id,表示你要把消息发送到哪一个消息队列,一般由msgget 函数生成
        msgp: 指向消息结构体 msgbuf,用来保存读取到的消息
struct msgbuf //定义一个发送消息的结构体
{
long mtype; //消息的类型,具体的含义是由程序设计者指定 >0
char mtext[0]; //消息的内容,可长可短//“柔性数组”“0 长数组”
};
        msgsz: 用来指定 msgp 指向的消息结构体,最多能够保存多少个字节的消息
        msgtyp:指定要读取消息的类型,为 0 的话表示接收全部类型
        msgflg:阻塞标志位,为 0 表示阻塞模式,为 IPC_NOWAIT 表示非阻塞模式
返回值:
        成功返回 0,
        失败返回-1,同时 errno 被设置
示例:
struct msgbuf {
long mtype; //消息的类型,具体的含义是由程序设计者指定 >0
char mtext[0]; //消息的内容,可长可短//“柔性数组”“0 长数组”
};

struct msgbuf *p = (struct msgbuf *)malloc(sizeof(struct msgbuf)+100);
if(p == NULL)
{
    perror("malloc error");
    exit(-1);
}
memset(p,0,sizeof(struct msgbuf)+100); //把 p 所指内存全部赋值为 0
int r = msgrcv(mid, (void *)p, 100,100,0); //以阻塞模式读取 100 字节 mid 消息队列上类型为 100 的消息到 p 所指内存上阻塞
if(r == -1)
{
    perror("msgrcv error");
    exit(-1);
}

消息队列的控制操作(msgctl)

头文件:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
函数原型:
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
作用:
        对 msqid 指定的消息队列根据 cmd 命令号执行指定的操作
参数含义:
        msqid:消息队列的 id
        cmd:命令号,常用命令如下:
                IPC_RMID 删除指定的消息队列
                IPC_SET 设置消息队列的属性
                IPC_STAT 获取消息队列的属性
        buf:msqid_ds 结构体(7.1 节有定义),用来保存要设置或获取的消息队列属性,当 cmd 为 IPC_RMID 时则忽略改参数
返回值:
        成功返回 0,
        失败返回-1,同时 errno 被设置

消息队列使用实例

进程 1-发送消息
#include<stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include<string.h>
#include<stdlib.h>

#define PATHNAME "/home/china" //一个存在的路径名,ftok 获取这个文件的 inode 编号
#define PROJ_ID 20207 //一个整数值(非 0)用于申请一个“钥匙”

//自定义的消息结构体
//sizeof(struct msgbuf) == sizeof(long) == 8
struct msgbuf {
    long mtype; //消息的类型,具体的含义是由程序设计者指定 >0
    char mtext[0]; //消息的内容,可长可短//“柔性数组”“0 长数组”
};


int main()
{

    //产生一个 System V IPC key
    key_t key = ftok(PATHNAME,PROJ_ID);
    if(-1 == key)
    {
        perror("ftok error");
        exit(-1);
    }

    //利用 key 创建或者打开一个 IPC 对象
    int mid = msgget(key,IPC_CREAT | 0664);
    if(-1 == mid)
    {
        perror("msgget error");
        exit(-1);
    }

    //把信息发送到消息队列
    char buf[256] = {0}; //用于保存发送的信息
    printf("请输入发送的消息\n");
    fgets(buf,256,stdin); //从标准输入中输入字符串到 buf 中
    //分配消息结构体的空间(结构体本身和消息大小)
    struct msgbuf *p = (struct msgbuf *)malloc(sizeof(struct msgbuf)+strlen(buf)+1);
    if(p == NULL)
    {
        perror("malloc error");
        exit(-1);
    }
    printf("请输入发送消息的类型\n");
    scanf("%ld",&(p->mtype));
    //p->mtype = 200;
    memcpy(p->mtext,buf,strlen(buf)+1); //把 buf 空间上
    strlen(buf)+1 大小的内容 copy 到 p->mtext 上去
    int r = msgsnd(mid,(void *)p, strlen(buf)+1,0); //发送结构体上的消息到 mid 的消息队列上,以阻塞模式
    if(r == -1)
    {
        perror("msgsnd error");
        exit(-1);
    }
    free(p);
    return 0;
}
进程 2-接收进程
#include<stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include<string.h>
#include<stdlib.h>

#define PATHNAME "/home/china"
#define PROJ_ID 20207

//自定义的消息结构体
//sizeof(struct msgbuf) == sizeof(long) == 8
struct msgbuf {
    long mtype; //消息的类型,具体的含义是由程序设计者指定 >0
    char mtext[0]; //消息的内容,可长可短//“柔性数组”“0 长数组”
};

int main()
{
    //产生一个 System V IPC key
    key_t key = ftok(PATHNAME,PROJ_ID);
    if(-1 == key)
    {
        perror("ftok error");
        exit(-1);
    }
    //利用 key 创建或者打开一个 IPC 对象
    int mid = msgget(key,IPC_CREAT | 0664); 
    if(-1 == mid)
    {
        perror("msgget error");
        exit(-1);
    }
    //把从消息队列中读取消息
    //分配消息结构体的空间(结构体本身和消息大小)
    struct msgbuf *p = (struct msgbuf *)malloc(sizeof(struct msgbuf)+100);
    if(p == NULL)
    {
        perror("malloc error");
        exit(-1);
    }
    memset(p,0,sizeof(struct msgbuf)+100); //把 p 所指内存全部赋值为 0
    int r = msgrcv(mid, (void *)p, 100,100,0); //以阻塞模式读取 100字节 mid 消息队列上类型为 100 的消息到 p 所指内存上阻塞
    if(r == -1)
    {
        perror("msgrcv error");
        exit(-1);
    }else
    {
        printf("消息类型:%ld\n",p->mtype);
        printf("接收的消息为:%s\n",p->mtext);
    }
    free(p);
    //移除系统中的消息队列
    msgctl(mid,IPC_RMID,NULL);
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

九月丫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值