进程间通信XSI IPC有3种:消息队列、共享内存、信号量。它们之间有很多相似之处,但也有各自的特殊的地方。消息队列作为其中比较简单的一种,它会有些什么东西呢,来一起探讨探讨。。

消息队列结构


消息队列提供了一种从一个进程向另一个进程发送一个数据块的方法. 每个数据块都被认为是一个类型,接受进程接收的数据块可以有不同的类型值。

我们可以通过发送消息来避免命名管道的同步和阻塞问题。 消息队列与管道不同的是,消息队列是基于消息的,而管道是基于字节流的,且消息队列的读取不一定是先入先出.

命名管道:

      

消息队列:

  

消息队列与命名管道有一个不足,就是每个消息最大长度是有上限的.而且还注意消息队列的生命周期是伴随内核的.

所以如果你没有显示的删除它,那么在关机前它一直在.这里的消息队列我们可以看成一个链表队列,具体为什么? 让我们来看看消息队列的结构.

消息队列结构:

tp@tp:  more  /usr/include/linux/msg.h

此结构定义了队列的当前状态。这里的_first和_last便说明队列的链式结构,它们分别是消息队列的头、尾指针。

初此以外,我们可能还会疑问这个结构是如何表示出这个消息队列的?举个例子,就是我要找出消息队列的"身份证".不妨调出 struct msqid_ds结构体的第一个条目,

也就是IPC的共有结构体,我们再看看它的结构

该结构体中第一个成员key,其实就是消息队列的唯一标识,想找到一个消息队列就是靠它来找,这个我们待会会说到的。

基本命令


系统能创建多少个消息队列?

  使用cat  /proc/sys/kernel/msgmni查看

每个消息队列能装多少个字节?

  用cat  /proc/sys/kernel/msgmnb查看

队列中每一条基类最大是多大?

   用cat  /proc/sys/kernel/msgmax查看

显示消息队列

  ipcs -q      //s:status

  

   ipcs (SystemV IPC系列都可以看)

  

接下来手工删除

  ipcrm -q msqid   (此例对应65536)

 

相关函数


1.创建消息队列

  原型:int msgget(key_t key,     int  msgflg )

  返回值:失败返回-1, 个数限制, 打开别人队列没权限。大于0  调用成功  返回已打开的消息队列ID标识。

参数:

  key_t key    //相当于文件名,也可以由函数ftok生产

   int  msgflg   //指定消息队列创建。一般是IPC_CREAT  0644  或者0 (为0 则代表由操作系统自动选择)     

  

2.往消息队列中写数据

  原型:int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg)

参数:

  void* msgp     //想写入消息队列数据的地址,是指向消息缓冲区的指针,此位置用来暂时存储发送接收消息,是一个用户可定义的通用结构,形态如下:

struct msgbuf
{   long mtype; //k接收类型,可看作消息队列通道号channel ,同时必须> 0   char mtext[] //存放内容
};

  size_t msgsz   //消息大小, 不算上通道号大小

  int msgflg     //一般输入0,由操作系统自行选择

3.从消息队列中读数据

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

参数:

  void *msgp      //接收的消息放在那里

  size_t msgsz    //接收消息的地方大小,不算上通道号

  long msgtyp    //从消息队列内读取的消息形态(选择何种通道)。值为0,表示消息队列中的所有消息都读取

  int msgflg      //0

注意msgflg这个参数,它用来核心程序在队列没有数据的情况的下所采取的行动.

如果msgflg和常数IPC_NOWAIT合用,在msgsnd()执行时若是消息队列已满,则msgsnd()不会阻塞,而会立即返回-1,

如果执行的是msgrcv(),则在消息队列呈空时,不做等待马上返回-1,并设定错误码ENOMSG。

当msgflg为0时,msgsnd()及msgrcv()在队列呈满或呈空的情形时,采取阻塞等待的处理方式.

4.设置消息队列属性

  原型:int msgctl(int msqid,int cmd, struct msqid_ds *buf)

参数:

 int cmd

msgctl系统调用函数对msgqid标识的消息队列执行cmd设置。操作系统定义了常见的3种cmd操作:

  IPC_STAT:该命令用来获取消息队列对应的msqid_ds数据结构,并将其保存到buf指定的地址空间.

  IPC_SET:该命令用来设置消息队列的属性,要设置的属性存储在buf中.

  IPC_RMID:从内核中删除msqid标识的消息队列.函数调用删除消息队列

  

struct msqid_ds *buf);   //cmd不是IPC_STAT 可设置为0,自动选择

消息队列应用


小练习:

创建客户端(client)和服务端(server) ,使得不同的client端可以通过消息队列向server端发送请求,server端分别对client做出回应。

思路:大致如图 (channel 相当于消息类型)

实现代码:

  server.c

   #include<stdio.h>
#include<stdlib.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#include<string.h>
#include<unistd.h>
struct msgbuf{
long channel ;//通道号
char mtext[];
}; int main(void)
{
int id = msgget(, IPC_CREAT|); //创建消息队列
if(id == -) perror("msgget"),exit(); while()
{
struct msgbuf mb;
memset(&mb, 0x00, sizeof(mb));
if(msgrcv(id ,&mb, ,, ) == -) //从消息队列接收client端送来数据
perror("msgrcv"),exit(); printf("Get from Client:%s\n", mb.mtext+ sizeof(long));
// fflush(stdout);
mb.channel = *(long*)(mb.mtext); //将头4个字节强转赋值给队列通道号
//发送回消息队列
msgsnd(id, &mb, strlen(mb.mtext+sizeof(long)) + sizeof(long), );
} }

server.c

  client.c

   #include<stdio.h>
#include<stdlib.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#include<string.h>
#include<unistd.h> struct msgbuf
{
long channel; //消息队列通道号
char mtext[];
}; int main(void)
{
int id = msgget(, );
if(id == -) perror("msgget"),exit();
while()
{
struct msgbuf mb,rcv;
memset(&mb, 0x00, sizeof(mb));
//从stdin读入数据到mb.mtext后移sizeof(long)字节出
while(fgets(mb.mtext+sizeof(long), - sizeof(long), stdin) != NULL)
{
*(long*)mb.mtext = getpid(); //头部赋值为pid
mb.channel = ; //选用通道号 1 //发送到消息队列. 注意对发送大小的处理
msgsnd(id, &mb, strlen(mb.mtext+ sizeof(long))+sizeof(long), );
printf("send ok!\n"); //从消息队列获取
memset(&rcv, 0x00, sizeof(rcv)); //清空
if(msgrcv(id, &rcv, , (long)getpid(), ) ==-)
perror("msgrcv"),exit();
printf("Message back: %s\n", rcv.mtext + sizeof(long));
}
}
return ;
}

client.c

相关makefile

大致使用这些函数便简单地实现了双向通信,效果就像这样

最后总结,消息队列相对命名管道优势。

消息队列与命名管道相比,消息队列的优势在于:

  1.消息队列也可以独立于发送和接收进程而存在,从而消除了在同步命名管道的打开和关闭时可能产生的困难。

  2.基于发送消息机制,避免了像命名管道存在的同步、阻塞问题,不需要由进程自己来提供同步方法。
  3.接收程序可以通过消息类型有选择地接收数据,而不是像命名管道中那样默认地接收数据。
 
  4.实现了双向通信

进程间通信——XSI IPC之消息队列的更多相关文章

  1. Linux 进程间通信(一)(经典IPC:消息队列、信号量、共享存储)

    有3种称作XSI IPC的IPC:消息队列.信号量.共享存储.这种类型的IPC有如下共同的特性. 每个内核中的IPC都用一个非负整数标志.标识符是IPC对象的内部名称,为了使多个合作进程能够在同一IP ...

  2. System V IPC(1)-消息队列

    一.概述                                                    System V三种IPC:消息队列,信号量,共享内存.这三种IPC最先出现在AT&am ...

  3. IPC之——消息队列

    消息队列作用: 可以用于两个没有联系的进程间通信,创建一个消息队列类似于打开了一个文件,两个不同的进程都可以进行操作 消息队列之函数介绍: 头文件:<sys/type.h> <sys ...

  4. 进程间通信IPC:消息队列,信号量,共享内存

    2015.3.4星期三 阴天 进程间通信:IPC 文件对象:记录文件描述符,文件开关等 IPC标示符:系统全局的流水号两个进程要通信,打开的是唯一的对象进行通讯,通过key操作 XSI IPC:消息队 ...

  5. 四十九、进程间通信——System V IPC 之消息队列

    49.1 System V IPC 介绍 49.1.1 System V IPC 概述 UNIX 系统存在信号.管道和命名管道等基本进程间通讯机制 System V 引入了三种高级进程间通信机制 消息 ...

  6. Linux环境编程之IPC进程间通信(五):Posix消息队列1

    对于管道和FIFO来说.必须应该先有读取者存在.否则先有写入者是没有意义的. 而消息队列则不同,它是一个消息链表,有足够写权限的线程可往别的队列中放置消息,有足够读权限的线程可从队列中取走消息.每一个 ...

  7. linux进程间通信-XSI IPC

    一 什么是XSI IPC     有三种 IPC我们称作XSI IPC,即消息队列.信号量以及共享存储器(共享内存),它们之间有很多相似之处. 二 标识符和键     每个内核中的 IPC结构(消息队 ...

  8. System V IPC 之消息队列

    消息队列和共享内存.信号量一样,同属 System V IPC 通信机制.消息队列是一系列连续排列的消息,保存在内核中,通过消息队列的引用标识符来访问.使用消息队列的好处是对每个消息指定了特定消息类型 ...

  9. 进程间通信之信号量、消息队列、共享内存(system v的shm和mmap)+信号signal

    进程间通信方式有:System v unix提供3种进程间通信IPC:信号量.消息队列.共享内存.此外,传统方法:信号.管道.socket套接字. [注意上述6种方式只能用户层进程间通信.内核内部有类 ...

随机推荐

  1. 计算机基础,Python基础--变量以及简单的循环

    一.计算机基础 1.CPU 相当于人体的大脑,用于计算处理数据. 2.内存  用于存储数据,CPU从内存调用数据处理计算,运算速度很快. PS:问:既然在内存里的数据CPU运算速度快,为什么计算机不全 ...

  2. Hive函数:GROUPING SETS,GROUPING__ID,CUBE,ROLLUP

    参考:lxw大数据田地:http://lxw1234.com/archives/2015/04/193.htm 数据准备: CREATE EXTERNAL TABLE test_data ( mont ...

  3. Spark:scala集合转化为DS/DF

    scala集合转化为DS/DF case class TestPerson(name: String, age: Long, salary: Double) val tom = TestPerson( ...

  4. 有没有想过css定位与xpath的区别

    我是这样理解的, css选择如同你尽可能具体的描述一个元素的形态, 包括他的: 标签, 类, id 以及这些的组合, 目标是尽可能确定元素的唯一坐标 , 以方便选择. 而xpath是根据元素的路径去确 ...

  5. CLR-基元类型以及溢出检查

    =========(CLR via C#阅读笔记)======== 基元类型(primitive type): 基元类型也不做过多的解释,举个例子即可清晰的辨别 在java里曾使用过Sting s=& ...

  6. js中三种全局变量声明方法

    声明方式一: 使用var(关键字)+变量名(标识符)的方式在function外部声明,即为全局变量,否则在function声明的是局部变量.该方式即为显式声明详细如下: <script> ...

  7. [LeetCode] Sentence Similarity 句子相似度

    Given two sentences words1, words2 (each represented as an array of strings), and a list of similar ...

  8. 深入理解.net - 2.多态 Polymorphsim

    通过上篇文章 继承的本质 深入介绍了继承过程中对象的的创建过程,相信对继承已经有了一个深入的理解,本文则详细剖析一下面向对象设计的另一要素多态(Polymorphsim). 什么是多态 官方MSDN上 ...

  9. [HNOI 2010]chorus 合唱队

    Description 题库链接 对于一个包含 \(N\) 个整数的数列 \(A\) ,我们可以把它的所有元素加入一个双头队列 \(B\) . 首先 \(A_1\) 作为队列的唯一元素,然后依次加入 ...

  10. 棋盘 chess

    Description 给出一张 n × n 的棋盘,格子有黑有白.现在要在棋盘上放棋子,要求: • 黑格子上不能有棋子 • 每行每列至多只有一枚棋子 你的任务是求出有多少种合法的摆放方案.答案模 1 ...