进程间通信:消息队列(Message queue)篇

2017-05-19 00:46 阅读 648 次 评论 1 条

Linux中,IPC消息队列是一个双向通信的全内存设计,即内核保证了读写顺序和数据同步,并且是性能比较优越的先进先出数据结构。消息队列应用于很多场景:比如异步任务处理抢占式的数据分发,以及顺序缓存区等。

消息队列的产生原因

消息队列起始就是消息传输过程中保存的容器,既然有了管道IPC,为什么要出现消息队列呢?理由如下:

①生命周期:匿名管道和命名管道是随进程的,意味着管道的生命周期是随进程的退出而退出的。

②传送方式:管道传送数据时以无格式字节流的形式传送,给程序的开发带来不便。

③信号传递量:担当数据传送媒介的管道,其缓冲区大小受到较大的限制。

鉴于上者的三种原因:IPC方式下的另一种进程间通信->消息队列应运而生。它克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。

什么是消息队列?

[普通定义]: 消息队列提供了一种从一个进程向另一个进程发送一个数据块的方法。每个数据块都被认为含有一个类型,接受者接受的数据块可以有不同的类型值,我们可以通过发送消息来避免命名管道的同步和阻塞问题。

[最佳定义]: 内核地址空间中的内部链表,消息可以顺序地发送到队列中,并以几种不同的方式从队列中获取,每个消息队列是由IPC标识符所唯一表示的。

消息队列与管道的不同在于:消息队列是基于消息的,而管道是基于字节流的,且消息队列的读取不一定是先入先出。

消息队列的上限

消息队列与管道也有一样的不足,就是每个消息的最大长度是有上限的(MSGMAX),每个消息队列的总的字节数是有上限的(MSGMNB),系统上消息队列的总数也有一个上限(MSGMNI)。

IPC对象数据结构

内核为每个IPC对象维护了一个数据结构(存在于/usr/include/linux/ipc.h)中。

不仅如此,消息队列,共享内存和信号量都有这样一个共同的数据结构。

消息队列结构

消息队列结构体存在于/usr/include/linux/msg.h目录下:

从上图可以看出,消息队列结构体中的第一条内容就是IPC结构体,即IPC结构体是共用的,后面的都是消息队列所私有的成员。并且消息队列是由链表来实现的。

消息队列相关函数接口说明

1.msgget()函数(用于创建和访问一个消息队列)。

参数 [key]: 类似于端口号,也可以由ftok函数生成。

参数 [msgflg]: 存在两个IPC标志,即IPC_CREATIPC_EXCL

①IPC_CREAT: 如果IPC不存在,则创建一个IPC资源,否则打开操作。

②IPC_EXCL: 只有在共享内存不存在的时候,新的共享内存才建立,否则出错。如果单独使用IPC_EXCL,xxxget()函数要么返回一个已经存在的共享内存的操作符,要么返回一个新建的共享内存的标识符。

③如果IPC_CREAT和IPC_EXCL同时使用,xxxget()函数将返回一个新建的IPC标识符。如果该IPC资源已经存在,则返回-1出错。这样就保证了只有是二者同时使用,我们就可以保证所得的对象一定是新建的。

2.msgsnd()和msgrcv()函数(msgrcv从队列中取出消息,msgsnd将数据放到消息队列中)。

参数 [msgid]:由msgget函数返回的消息队列标识符。

参数 [msgp]:指向一个准备发送消息的指针,此位置用来暂时存储发送和接受的消息:是一个用户可以定义的通用结构,形态如下:

参数 [msgsz]:指msgp指向的消息的长度。

参数 [msgtyp]:从消息队列内读取的消息形态。如果值为0,则表示消息队列中的所有消息都会被读取。

参数 [msgflag]:用来指明核心程序在队列没有数据的情况下采取的行动。如果msgflg和常数IPC_NOWAIT合用,则在msgsnd()执行时:若消息队列已满,则msgsnd()将不会阻塞,而会立即返回-1;如果执行的是msgrcv(),则在消息队列呈空时,不做等待马上返回-1,并设定错误码为ENOMSG。当msgflg为0时,msgsnd()及msgrcv()在队列呈满或呈空的情况下,采取阻塞等待的处理模式。

3.msgctl()函数(控制消息队列,与共享内存的shmctl类似)

参数 [msgid]:由msgget函数返回的消息队列标识符。

参数 [cmd]:指将要采取的动作,系统定义了3中cmd操作:

IPC_STAT:该命令用来获取消息队列对应的 msgid_ds数据结构,并将保存到buf指定的地址空间。
IPC_SET:设定消息队列的属性,要设置的属性存储在buf里面。
IPC_RMID:从内核中删除msgid表示的消息队列。

参数 [buf]:指向msg_ds结构的指针,它指向消息队列模式和访问权限的结构。msg_ds结构体的内容如下:

成功时返回0,失败时返回-1。

key_t键

System_v IPC使用key_t键作为他们的名字,在CentOS6.5下key_t的值被定义为int类型。

/usr/include/sys/ipc.h

/usr/include/bits/types.h

/usr/include/bits/typesizes.h

/usr/include/bits/types

ftok()函数

函数ftok把一个已经存在的路径名和一个整数标识得转换成一个key_t值,称为IPC键:

参数 [pathname]:通常是跟本应用用关的目录。

参数 [proj_id]:指的是本应用所用到的IPC的一个序列号,成功返回IPC键,失败返回-1。

注:两进程如在pathname和proj_id上达成一致(或约定好),双方就都能够通过调用ftok函数得到同一个IPC键。

pathname的实现是组合了三个键,分别是:

①pathname所在文件系统的信息(stat结构的st_dev成员)。

②pathname在本文件系统内的索引节点号(stat结构的st_ino成员)。

③id的低序8位(不能为0)。

ftok调用返回的整数IPC键由 proj_id的低序8位,st_dev成员的低序8位,st_info的低序16位组合而成。

不能保证两个不同的路径名与同一个proj_id的组合产生不同的键,因为上面所列的三个条目(文件系统标识符、索引节点、proj_id)中的信息位数可能大于一个整数的信息位数。

消息队列模拟实现客户端与服务端通信

版权声明:本文著作权归原作者所有,欢迎分享本文,谢谢支持!
转载请注明:进程间通信:消息队列(Message queue)篇 | 术与道的分享
分类:操作系统 标签:, ,
1024do.com导航_术与道导航平台

发表评论


表情