本系列文章主要是学习记录Linux下进程间通信的方式。

常用的进程间通信方式:管道、FIFO、消息队列、信号量以及共享存储。

参考文档:《UNIX环境高级编程(第三版)》

参考视频:Linux进程通信  推荐看看,老师讲得很不错

Linux核心版本:2.6.32-431.el6.x86_64

注:本文档只是简单介绍IPC,更详细的内容请查看参考文档和相应视频。

本文介绍利用消息队列进行进程间的通信。

1  IPC对象

IPC对象:消息队列、共享内存和信号量。

存在于内核中而不是文件系统中,由用户控制释放(用户管理ipc对象的生命周期),不像管道那样由内核控制。

IPC对象通过标识符来引用和访问,所有IPC在内核空间中有唯一标识ID,在用户空间中的唯一标识称为key。

可通过[root@192 ~]# ipcs  命令查看。

每个IPC对象都由get函数创建:msgget、shmget、semget,调用get函数必须指定关键字key。

2  介绍

  • 消息队列是内核中的一个链表。
  • 消息队列存储在内核中,由消息队列标识符标识。
  • 用户进程将数据(二进制、文本等)传输到内核后,内核重新添加一些如用户ID、组ID、读写进程的ID和优先级等相关信息后并打成一个数据包称为消息。
  • 允许一个或多个进程往消息队列中写消息和读消息,但一个消息只能被一个进程读取,读取完毕后就自动删除。
  • 消息队列具有一定的FIFO的特性,消息可以按照顺序发送到队列中,也可以几种不同的方式从队列中读取。每一个消息队列在内核中用一个唯一的IPC标识ID表示。
  • 消息队列的实现包括创建和打开队列、读取消息和控制消息队列等四种操作。

3  消息队列属性结构体

 1 struct msqid_ds {
2 struct ipc_perm msg_perm; /* Ownership and permissions */
3 time_t msg_stime; /* Time of last msgsnd(2) */
4 time_t msg_rtime; /* Time of last msgrcv(2) */
5 time_t msg_ctime; /* Time of last change */
6 unsigned long __msg_cbytes; /* Current number of bytes in
7 queue (non-standard) */
8 msgqnum_t msg_qnum; /* Current number of messages
9 in queue */
10 msglen_t msg_qbytes; /* Maximum number of bytes
11 allowed in queue */
12 pid_t msg_lspid; /* PID of last msgsnd(2) */
13 pid_t msg_lrpid; /* PID of last msgrcv(2) */
14 };

4  函数原型

1 #include <sys/types.h>
2 #include <sys/ipc.h>
3 #include <sys/msg.h>
4 int msgget(key_t key, int msgflg);
5 说明:创建一个队列或打开一个现有队列。
6 返回:成功返回内核中消息队列的标识ID,出错返回-1。
7 参数key:用户指定的消息队列键值;
8 参数flag:IPC_CTREAT、IPC_EXCL等权限组合。
9 注:若创建消息队列,key可指定键值,也可将之设置为IPC_PRIVATE。若打开进行查询,则key不能为0,必须是一个非零的值,否则查询不到。
1 #include <sys/types.h>
2 #include <sys/ipc.h>
3 #include <sys/msg.h>
4 int msgctl(int msqid, int cmd, struct msqid_ds *buf);
5 说明:消息队列控制函数;
6 返回:成功返回0,出错返回-1;
7 参数msgid:内核中的消息队列ID;
8 参数buf:消息队列属性指针;
9 参数cmd:IPC_STAT:获取消息队列的属性,取此队列的msqid_ds结构,并将其存放在buf指向的结构中;
IPC_SET:设置属性,按由buf指向的结构中的值,设置与此队列相关的结构中的字段;
IPC_RMID:删除队列,从系统中删除该消息队列以及仍在该队列上的所有数据。
 1 int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
2 说明:将消息添加到消息队列尾端。
3 返回:成功返回0,出错返回-1;
4 参数msqid:内核中的消息队列ID;
5 参数msgp:通用指针,指向需要发送的消息,参数传递的格式:
6 struct msgbuf {
7 long mtype; /* message type, must be > 0 */
8 char mtext[1]; /* message data */
9 };
10 mtype:指定消息的类型,它由一个整数来代表,并且它只能是大于0的整数;
11 mtext:消息数据本身。大小由msgsz指定。
12 Linux中,消息的最大长度是4056个字节,其中包括mtype,它占4个字节;
13 结构体msgbuf用户可自定义,但第一个成员必须是mtype。
14 参数msgsz:指定消息的大小,不包括mtype的大小。
15 参数flag:0:阻塞,阻塞直到有空间可以容纳要发送的消息或从系统中删除了此队列或捕捉到一个信号,并从信号处理程序返回。
IPC_NOWAIT:类似于文件I/O的非阻塞标志。若消息队列已满(或者是队列中的消息总数等于系统限制值,或队列中的字节总数等于系统限制值),在指定IPC_NOWAIT使得msgsnd立即出错返回EAGAIN。
1 ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
2 说明:从队列中取消息。
3 返回:成功返回消息的数据部分长度,出错返回-1;
4 参数msgid:消息队列ID;
5 参数msgp:指向消息队列的缓存;
6 参数msgsz:消息缓存的大小,不包括mtype的大小,计算方式:msgsz=sizeof(struct msgbuf)-sizeef(long);
7 参数msgtyp:消息类型;msgtyp==0,获得消息队列中第一个消息;msgtyp>0,获取消息队列中类型为msgtyp的第一个消息;msgtyp<0,获得消息队列中小于或等于msgtyp绝对值的消息(类型最小的)。
8 参数msgflg:0或者IPC_NOWAIT。

5  测试实例

单独创建两个进程,发送消息进程和接收消息进程,它们之间没有关系,通过消息队列来交换数据。

发送消息进程:

 1 #include <sys/types.h>
2 #include <sys/ipc.h>
3 #include <sys/msg.h>
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <string.h>
7
8 typedef struct {
9 long type; //消息类型
10 int start; //消息数据本身,包括start和end
11 int end;
12 }MSG;
13
14 //往消息队列中发送消息
15
16 int main(int argc, char *argv[])
17 {
18 if (argc < 2) {
19 printf("usage: %s key\n", argv[0]);
20 exit(1);
21 }
22
23 key_t key = atoi(argv[1]); //key由用户指定
24 // key_t key = ftok(argv[1], 0); //由函数生成key
25 printf("key: %d\n", key);
26
27 // 创建消息队列
28 int msg_id;
29 if ((msg_id = msgget(key, IPC_CREAT|IPC_EXCL|0777)) < 0) {
30 perror("msgget error");
31 }
32 printf("msg id: %d\n", msg_id);
33
34 // 定义要发送的消息
35 MSG m1 = {4, 4, 400};
36 MSG m2 = {2, 2, 200};
37 MSG m3 = {1, 1, 100};
38 MSG m4 = {6, 6, 600};
39 MSG m5 = {6, 40, 6000};
40
41 //发送消息到消息队列
42 if (msgsnd(msg_id, &m1, sizeof(MSG)-sizeof(long), IPC_NOWAIT) < 0) {
43 perror("msgsnd error");
44 }
45 if (msgsnd(msg_id, &m2, sizeof(MSG)-sizeof(long), IPC_NOWAIT) < 0) {
46 perror("msgsnd error");
47 }
48 if (msgsnd(msg_id, &m3, sizeof(MSG)-sizeof(long), IPC_NOWAIT) < 0) {
49 perror("msgsnd error");
50 }
51 if (msgsnd(msg_id, &m4, sizeof(MSG)-sizeof(long), IPC_NOWAIT) < 0) {
52 perror("msgsnd error");
53 }
54 if (msgsnd(msg_id, &m5, sizeof(MSG)-sizeof(long), IPC_NOWAIT) < 0) {
55 perror("msgsnd error");
56 }
57
58 // 发送后去获取消息队列中消息的总数
59 struct msqid_ds ds;
60 if (msgctl(msg_id, IPC_STAT, &ds) < 0) {
61 perror("msgctl errors");
62 }
63 printf("msgctl totol: %ld\n", ds.msg_qnum);
64
65 return 0;
66 }

接收消息进程:

 1 #include <sys/types.h>
2 #include <sys/ipc.h>
3 #include <sys/msg.h>
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <string.h>
7
8 typedef struct {
9 long type; //消息类型
10 int start; //消息数据本身,包括start和end
11 int end;
12 }MSG;
13
14 //往消息队列中发送消息
15 int main(int argc, char *argv[])
16 {
17 if (argc < 3) {
18 printf("usage: %s key type\n", argv[0]);
19 exit(1);
20 }
21
22 key_t key = atoi(argv[1]);
23 long type = atoi(argv[2]);
24
25 //获得指定的消息队列
26 int msg_id;
27 if ((msg_id = msgget(key, 0777)) < 0) {
28 perror("msgget error");
29 }
30 printf("msg id: %d\n", msg_id);
31
32 MSG m;
33 if (msgrcv(msg_id, &m, sizeof(MSG)-sizeof(long), type, IPC_NOWAIT) < 0) {
34 perror("msgrcv error");
35 } else {
36 printf("type: %ld start: %d end: %d\n", m.type, m.start, m.end);
37 }
38
39 return 0;
40 }

测试步骤:

1、先分别编译发送进程和接收进程

[root@192 ipc]# gcc -o bin/msg_send msg_send.c

[root@192 ipc]# gcc -o bin/msg_rcv msg_rcv.c

2、发送进程发送5条消息,key是人为指定的10

3、接收进程获取一条消息

由于消息队列中有两条type都为6的消息,可以看出,首先获得的是先发送到消息队列的消息。获取后的消息将从消息队列中删除。

4、接收进程继续获取消息

Linux间进程通信--消息队列的更多相关文章

  1. Linux:进程通信之消息队列Message实例

    /*send.c*/ /*send.c*/ #include <stdio.h> #include <sys/types.h> #include <sys/ipc.h&g ...

  2. Linux下进程通信的八种方法

    Linux下进程通信的八种方法:管道(pipe),命名管道(FIFO),内存映射(mapped memeory),消息队列(message queue),共享内存(shared memory),信号量 ...

  3. Linux之进程通信20160720

    好久没更新了,今天主要说一下Linux的进程通信,后续Linux方面的更新应该会变缓,因为最近在看Java和安卓方面的知识,后续会根据学习成果不断分享更新Java和安卓的方面的知识~ Linux进程通 ...

  4. Linux编程---进程通信

    Linux的通信方式主要有分类有以下几种: -匿名管道和FIFO有名管道 -消息队列,信号量和共享存储 -套接字 对于套接字的进程通信,我就留在套接字的文章中再写了. 一.管道 管道是最古老的进程通信 ...

  5. Linux进程间通信(System V) --- 消息队列

    消息队列 IPC 原理 消息队列是消息的链式队列,如下图为消息队列的模型.整个消息队列有两种类型的数据结构. 1.msqid_ds 消息队列数据结构:描述整个消息队列的属性,主要包括整个消息队列的权限 ...

  6. Linux 进程间通信(posix消息队列 简单)实例

    Linux 进程间通信(posix消息队列 简单)实例 详情见: http://www.linuxidc.com/Linux/2011-10/44828.htm 编译: gcc -o consumer ...

  7. 2.Python进程间的通信之队列(Queue)和生产者消费者模型

    一.队列 1.1 概念介绍-----multiprocess.Queue 创建共享的进程队列,Queue是多进程安全的队列,可以使用Queue实现多进程之间的数据传递. Queue([maxsize] ...

  8. linux之间进程通信

    进程间通信方式:                    同主机进程间数据交换机制: pipe(无名管道) / fifo(有名管道)/ message queue(消息队列)和共享内存. 必备基础: f ...

  9. [置顶] 简单解析linux下进程通信方法

    linux下的进程通信手段基本上是从Unix平台上的进程通信手段继承而来的.而对Unix发展做出重大贡献的两大主力AT&T的贝尔实验室及BSD(加州大学伯克利分校的伯克利软件发布中心)在进程间 ...

  10. Linux下进程通信之管道

    每个进程各自有不同的用户地址空间,任何一个进程的全局变量在另一个进程中都看不到,所以进程之间要交换数据必须通过内核,在内核中开辟一块缓冲区,进程1把数据从用户空间拷到内核缓冲区,进程2再从内核缓冲区把 ...

随机推荐

  1. 8k中英双语文本嵌入模型效果初探

    一 模型介绍 向量模型用于生成向量表示,被广泛应用于检索.分类.聚类或语义匹配等传统的自然语言处理任务.到了大模型时代,由于上下文长度的限制,需要压缩.存储和查询大量的信息,这就需要用到向量模型对输入 ...

  2. Flink Standalone集群部署

    Flink Standalone模式部署集群是最简单的一种部署方式,不依赖于其他的组件,另外还支持YARN/Mesos/Docker等模式下的部署,这里使用的flink版本为最新的稳定版1.9.1版本 ...

  3. react 框架(antd)的使用方法

    脚手架 安装    npm install -g create-react-app 引入: import React, { Component } from "react"; im ...

  4. 力扣525(java&python)-连续数组(中等)

    题目: 给定一个二进制数组 nums , 找到含有相同数量的 0 和 1 的最长连续子数组,并返回该子数组的长度. 示例 1: 输入: nums = [0,1]输出: 2说明: [0, 1] 是具有相 ...

  5. 力扣445(java&python)-两数相加Ⅱ(中等)

    题目: 给你两个 非空 链表来代表两个非负整数.数字最高位位于链表开始位置.它们的每个节点只存储一位数字.将这两数相加会返回一个新的链表. 你可以假设除了数字 0 之外,这两个数字都不会以零开头. 示 ...

  6. dotnet 解析 TTF 字体文件格式

    在 Windows 下,可以使用 DX 提供的强大能力,调用 DX 读取 TTF 字体文件,获取字体文件的信息以及额外的渲染信息.特别是基于 DX 的 WPF 更是加了一层封装,使用 FontFami ...

  7. OLAP系列之分析型数据库clickhouse单机版部署(一)

    一.概述 官网:https://clickhouse.com/docs/zh ClickHouse是Yandex于2016年开源的列式存储数据库(DBMS),主要用于在线分析处理查询(OLAP),能够 ...

  8. Redis 5集群部署

    1.redis特点 (1)基于内存 (2)可持久化数据 (3)具有丰富的数据结构类型,适应非关系型数据的存储需求 (4)支持绝大多数主流开发语言,如C.C++.Java.Python.R.JavaSc ...

  9. SpringBoot连接redis报错:exception is io.lettuce.core.RedisException: java.io.IOException: 远程主机强迫关闭了一个现有的连接

    一.解决思路 (1).检查redis的配置是否正确 spring redis: host: localhost port: 6379 password: 123456 database: 0 time ...

  10. 每天上一当系列之vue修饰符.number

    今天使用number修饰符去处理el-input的内容为数字做校验原本以为省事不少,没想到,为0开头无法输入第二位以后,并且输入的比较多的时候会出现Infinity 很神奇,网上查了说是element ...