前言

  • 知识点

    • 消息队列、信号量共享内存 被统称为 system-V IPC

      • 以上都是“持续性”资源,即它们被创建之后, 不会因为进程的退出而消失

4. 消息队列

4.1 概念

  • 消息队列

    • 消息队列提供了一种从一个进程向另一个进程发送一个数据块的方法

4.2 对比

  • 消息队列与信号之间的对比

    • 信号承载的信息量少,而消息队列可以承载大量自定义的数据
  • 消息队列与管道之间的对比
    • 相同

      • 进程间通信都可以是不相关的进程
      • 都可以独立于发送和接收进程而存在
      • 在进程终止时,其内容并不会被删除
    • 不同
      • 在命名管道中,发送数据用 write(),接收数据用 read(), 则在消息队列中,发送数据用 msgsnd(),接收数据用 msgrcv() ,消息队列对每个数据都有一个最大长度的限制
      • 管道只能承载无格式字节流,消息队列提供有格式的字节流
      • 消息队列是面向记录的,其中的消息具有特定的格式以及特定的优先级,接收程序可以通过消息类型有选择地接收数据, 而不是像命名管道中那样,只能默认地接收
      • 消息队列可以实现消息的随机查询,消息不一定要以先进先出的顺序接收,也可以按消息的类型接收

4.3 函数及使用流程

  • 使用流程:

    1. 使用 msgget() 来创建或打开消息队列
    2. 使用 msgsnd() 来发送消息到文末
    3. 使用 msgrcv() 来接收消息,可指定某一条消息
    4. 使用 msgctl() 来控制消息(具体往下看

4.3.1 msgget()

  • 使用 msgget() 来创建或打开消息队列
  • 通过命令 man 了解更多
  • 函数原型:int msgget(key_t key, int msgflg);
    • key:消息队列的关键字值,多个进程可以通过它访问同一个消息队列
    • msgflg:表示创建的消息队列的模式标志参数,主要有IPC_CREAT,IPC_EXCL和权限mode,如:
      • IPC_CREAT:没有关键字 key 的消息队列就新建一个,有就直接打开
      • IPC_CREAT | IPC_EXCL:消息队列不存在,则新建一个,如果消息队列存在,则报错
      • IPC_CREAT | 0666:(注:消息队列不在意执行权限)
    • 返回
      • 成功:返回消息队列标识值
      • 失败:返回-1
        • 返回的错误码,即是在变量 error

          • EACCES:消息队列存在,但进程没有访问权限
          • EEXIST:msgflg 同时指定了 IPC_CREAT和IPC_EXCL,但是消息队列已经存在
          • ENOENT:消息队列不存在,且没有指定 IPC_CREAT 标志
          • ENOMEM:内存不足
          • ENOSPC:消息队列个数达到系统的限制

4.3.2 msgsng()

  • 使用 msgsnd() 来发送消息到文末
  • 通过命令 man 了解更多
  • 函数原型:int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
    • msqid:消息队列标识符
    • msgp:发送给队列的消息。msgp可以是任何类型的结构体,但第一个字段必须为long类型, 即表明此发送消息的类型,msgrcv()函数则根据此接收消息
      /*msgp定义的参照格式*/
      struct s_msg{
      long type; /* 必须大于0,消息类型 */
      char mtext[1]; /* 消息正文,可以是其他任何类型 */
      } msgp;
    • msgsz:要发送消息的大小,不包含消息类型占用的4个字节
    • msgflg:
      • 0:当消息队列满时,msgsnd() 函数将会阻塞,直到消息能写进消息队列
      • IPC_NOWAIT:当消息队列已满的时候,msgsnd() 函数不等待立即返回
      • IPC_NOERROR:若发送的消息大于size字节,则把该消息截断,截断部分将被丢弃,且不通知发送进程
    • 返回
      • 成功:返回 0
      • 失败:返回 -1,错误原因存在于变量 error
        • EAGAIN:参数 msgflg 设为 IPC_NOWAIT,而消息队列已满
        • EIDRM:标识符为 msqid 的消息队列已被删除
        • EACCESS:无权限写入消息队列
        • EFAULT:参数 msgp 指向无效的内存地址
        • EINTR:队列已满而处于等待情况下被信号中断
        • EINVAL:无效的参数msqid、msgsz或参数消息类型type小于0
  • 解除阻塞的三个条件:
    1. 消息队列变为未满
    2. 消息队列被删除
    3. 调用 msgsnd() 的进程被信号中断

4.3.3 msgrcv()

  • 使用 msgrcv() 来发送消息到文末

  • 通过命令 man 了解更多

  • 函数原型:ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);

    • msqid:消息队列标识符
    • msgp:存放消息的结构体,结构体类型要与msgsnd()函数发送的类型相同
    • msgsz:要接收消息的大小,不包含消息类型占用的4个字节
    • msgtyp:
      • > 0:表示接收类型等于msgtyp的第一个消息
      • = 0:表示接收第一个消息
      • < 0:表示接收类型等于或者小于msgtyp绝对值的第一个消息
    • msgflg:
      • 0:阻塞式接收消息,没有该类型的消息 msgrcv 函数一直阻塞等待
      • IPC_NOWAIT:若在消息队列中并没有相应类型的消息可以接收,则函数立即返回,此时错误码为ENOMSG
      • IPC_EXCEPT:与 msgtype 配合使用返回队列中第一个类型不为 msgtype 的消息
      • IPC_NOERROR:如果队列中满足条件的消息内容大于所请求的 size 字节,则把该消息截断,截断部分将被丢弃
    • 返回:
      • 成功:返回接收到的消息长度
      • 失败:返回 -1,错误原因存在于变量 error 中:
        • E2BIG:消息数据长度大于msgsz而msgflag没有设置IPC_NOERROR
        • EIDRM:消息队列已被删除
        • EACCESS:无权限读取该消息队列
        • EFAULT:参数msgp指向无效的内存地址
        • ENOMSG:参数msgflg设为IPC_NOWAIT,而消息队列中无消息可读
        • EINTR:等待读取队列内的消息情况下被信号中断
  • 解除阻塞的三个条件:

    1. 消息队列中已有符合条件的消息
    2. 消息队列被删除
    3. 调用 msgrcv() 的进程被信号中断

4.3.4 msgctl()

  • 使用 msgctl() 来设置或者获取消息队列的相关属性等等
  • 通过命令 man 了解更多
  • 函数原型:int msgctl(int msqid, int cmd, struct msqid_ds *buf);
    • msqid:消息队列标识符
    • cmd:操作命令
      • IPC_STAT:获取该 MSG 信息并存到结构体 msqid_ds 类型的 buf 中
      • IPC_SET:设置消息队列的属性,属性为 msqid_ds(msg_perm.uid、msg_perm.gid、msg_perm.mode以及msg_qbytes)
      • IPC_RMID:立即删除该 MSG,并且唤醒所有阻塞在该 MSG上的进程,同时忽略第三个参数
      • IPC_INFO:获得关于当前系统中 MSG 的限制值信息
      • MSG_INFO:获得关于当前系统中 MSG 的相关资源消耗信息
      • MSG_STAT:同 IPC_STAT,但 msgid 为该消息队列在内核中记录所有消息队列信息的数组的下标, 因此通过迭代所有的下标可以获得系统中所有消息队列的相关信息
    • buf:相关信息结构体缓冲区
    • 返回值
      • 成功:返回 0
      • 失败:返回 -1,错误原因存在 error 中:
        • EACCESS:参数cmd为IPC_STAT,却无权限读取该消息队列
        • EFAULT:参数buf指向无效的内存地址
        • EIDRM:标识符为msqid的消息队列已被删除
        • EINVAL:无效的参数cmd或msqid
        • EPERM:参数cmd为IPC_SET或IPC_RMID,却无执行权限

4.4 例程

  • 发送进程
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h> #define BUFFER_SIZE 512
/*定义消息结构体*/
struct message{
long msg_type;
char msg_text[BUFFER_SIZE];
};
int main()
{
int qid;
struct message msg; /*1. 创建消息队列*/
if ((qid = msgget((key_t)1234, IPC_CREAT|0666)) == -1)
{
perror("msgget\n");
exit(1);
} /*打印标识符*/
printf("Open queue %d\n",qid); while(1)
{
printf("Enter some message to the queue:");
if ((fgets(msg.msg_text, BUFFER_SIZE, stdin)) == NULL)
{
printf("\nGet message end.\n");
exit(1);
}
/*赋值消息类型*/
msg.msg_type = getpid();
/*2. 添加消息到消息队列*/
if ((msgsnd(qid, &msg, strlen(msg.msg_text), 0)) < 0)
{
perror("\nSend message error.\n");
exit(1);
}
else
{
printf("Send message.\n");
}
if (strncmp(msg.msg_text, "quit", 4) == 0)
{
printf("\nQuit get message.\n");
break;
}
}
exit(0);
}
  • 接收进程
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h> #define BUFFER_SIZE 512
/*定义消息结构体,与发送进程中的一样*/
struct message{
long msg_type;
char msg_text[BUFFER_SIZE];
};
int main()
{
int qid;
struct message msg; /*创建消息队列,键值和发送进程的一样*/
if ((qid = msgget((key_t)1234, IPC_CREAT|0666)) == -1)
{
perror("msgget");
exit(1);
}
/*打印标识符*/
printf("Open queue %d\n", qid);
do
{
/*读取消息队列*/
memset(msg.msg_text, 0, BUFFER_SIZE);
if (msgrcv(qid, (void*)&msg, BUFFER_SIZE, 0, 0) < 0)
{
perror("msgrcv");
exit(1);
}
printf("The message from process %ld : %s", msg.msg_type, msg.msg_text);
} while(strncmp(msg.msg_text, "quit", 4)); /*从系统内核中删除消息队列*/
if ((msgctl(qid, IPC_RMID, NULL)) < 0)
{
perror("msgctl");
exit(1);
}
else
{
printf("Delete msg qid: %d.\n", qid);
}
exit(0);
}

参考:

* 野火

【linux】系统编程-2-消息队列的更多相关文章

  1. Linux系统之《消息队列》入手应用

    目录 简述 代码 编译 运行 简述 消息队列是Linux进程间通信方式之一,消息队列一般是用于简单的通信,数据量不大,通信不频繁的情况.如果交互频繁或者数据量大就不适合了. 代码 下面直接上代码,发送 ...

  2. Linux系统编程@进程通信(一)

    进程间通信概述 需要进程通信的原因: 数据传输 资源共享 通知事件 进程控制 Linux进程间通信(IPC)发展由来 Unix进程间通信 基于System V进程间通信(System V:UNIX系统 ...

  3. linux系统编程之框架

    linux系统编程之框架: 1. 进程 1.1 进程概念 1.1.1 PCB 1.1.2 环境变量 1.2 进程控制 1.3 进程间通信 1.3.1 管道 1.3.2 有名管道 1.3.3 共享内存 ...

  4. linux系统编程(一)概述

    glibc库封装了linux系统调用,并提供c语言接口 所以学习linux系统编程,主要参考glibc库系统调用相关api 一.进程控制: fork 创建一个新进程 clone 按指定条件创建子进程 ...

  5. Linux 系统编程 学习 总结

    背景 整理了Liunx 关于 进程间通信的 很常见的知识. 目录 与 说明 Linux 系统编程 学习:000-有关概念 介绍了有关的基础概念,为以后的学习打下基础. Linux 系统编程 学习:00 ...

  6. Linux 系统编程 学习:02-进程间通信1:Unix IPC(1)管道

    Linux 系统编程 学习:02-进程间通信1:Unix IPC(1)管道 背景 上一讲我们介绍了创建子进程的方式.我们都知道,创建子进程是为了与父进程协作(或者是为了执行新的程序,参考 Linux ...

  7. Linux 系统编程 学习:03-进程间通信1:Unix IPC(2)信号

    Linux 系统编程 学习:03-进程间通信1:Unix IPC(2)信号 背景 上一讲我们介绍了Unix IPC中的2种管道. 回顾一下上一讲的介绍,IPC的方式通常有: Unix IPC包括:管道 ...

  8. Linux 系统编程 学习:04-进程间通信2:System V IPC(1)

    Linux 系统编程 学习:04-进程间通信2:System V IPC(1) 背景 上一讲 进程间通信:Unix IPC-信号中,我们介绍了Unix IPC中有关信号的概念,以及如何使用. IPC的 ...

  9. Linux 系统编程 学习:05-进程间通信2:System V IPC(2)

    Linux 系统编程 学习:05-进程间通信2:System V IPC(2) 背景 上一讲 进程间通信:System V IPC(1)中,我们介绍了System IPC中有关消息队列.共享内存的概念 ...

随机推荐

  1. Arcgis基于高程(DEM)计算地形湿度指数(TWI),以及坡度(Slope)度单位转换为弧度

    以30m*30m分辨率的图层为例 一.基于表面工具箱Surface计算Slope 1.如下图输入图层DEM,输出Slope 2.单位转换: Scale_slope=Slope*pi/180 二.基于水 ...

  2. Lsi卡和IB卡在CentOS中升级

    LSI 9271 步骤1:准备升级工具和固件包 rpm -ivh MegaCli-8.07.14-1.noarch.rpm [root@phegdata01 ~]# unzip 23-34-0-000 ...

  3. VMware Workstation Pro 16 官方正式版下载(含密钥)

    VMware官方网站 https://www.vmware.com VMware Workstation Pro已于近日更新.毫无疑问,这可能是Windows系统上最强大最好用的虚拟机! VMware ...

  4. Java lambda 分组后多列求和

    主要思路是reducing,可以像sql一样分组后多列求和处理成新对象等: select code,max(name)as name,sum(chengJi)as chengJi,sum(age)as ...

  5. 基于FPGA的VGA显示实验设计

    基于FPGA的VGA显示实验设计 成果展示(优酷视频): 视频: 基于FPGA的VGA显示技术(手机控制) http://v.youku.com/v_show/id_XNjk4ODE3ODUy.htm ...

  6. kali修改配置文件后并保存

    vim是进入配置文件并修改 修改完按Esc进入控制模式,再:w保存 :wq是保存并退出

  7. Beta冲刺随笔——Day_Nine

    这个作业属于哪个课程 软件工程 (福州大学至诚学院 - 计算机工程系) 这个作业要求在哪里 Beta 冲刺 这个作业的目标 团队进行Beta冲刺 作业正文 正文 其他参考文献 无 今日事今日毕 林涛: ...

  8. Python_爬虫养殖专业户_00

    为什么学习爬虫? 当你在夜深人静,睡不着觉,想看一些更加睡不着觉的图片/视频时... 这是一句疑似玩笑话, 现实情况是, 每一天, 整个社会都积累了大量的数据, 在数据化的社会中,没有大批量的收集和探 ...

  9. PHP代码审计分段讲解(9)

    22 弱类型整数大小比较绕过 <?php error_reporting(0); $flag = "flag{test}"; $temp = $_GET['password' ...

  10. linux替换项目jar包

    查看服务是否启动,如果启动,则需要根据 pid 停止服务: # ps -ef | grep xxx.jar 杀掉该项目进程 # kill -9 pid号 再次查看进程 # ps -ef | grep ...