Linux基础入门--进程间通信--消息队列

扶醉桌前 提交于 2020-01-13 00:18:39

Linux基础入门--进程间通信--消息队列

1.概述

System V提供的IPC机制主要有消息队列,信号量和共享内存3种机制。和文件一样,IPC在使用前必须先创建,每种IPC都有特定的生产者,所有者和访问权限。使用ipcs命令可以查看当前系统正在使用的IPC工具:

[root@localhost swz] # ipcs
------- Shared MemorY Segments ---------    //共享内存
key     shmid    ower    perms   bytes   nattch   status

------- Semaphore Arrays  ---------   //信号量
key     shmid    ower    perms   nsems

------- Message Queues  -----------  //消息队列
key     shmid    ower    perms   used-bytes   messages 

由以上可以看出,一个IPC工具至少包含key值,ID值,拥有者,权限和使用的大小等关键信息。如果需要手动删除某个IPC机制,可以使用ipcrm命令。

2.key值和ID值

Linux系统为每个IPC机制都分配了唯一的ID,所有针对该IPC机制的操作都使用该ID值。因此,通信的双方都需要通过某个方法来获取ID值。创建者根据创建函数的返回值可以获取该值,但另一个进程如何实现呢?Linux两个进程不能随意访问对方的空间(一个特殊是子进程可以继承父进程的数据,实现父进程向子进程的单向传递),也就不能够直接获取到这一ID值。
为解决这一问题,IPC在实现时约定使用key值做为参数创建,如果在创建时使用相同的key值将得到同一个IPC对象的ID值(即一方创建,另一方获取的是ID),这样就保证了双方可以获取用于传递数据的IPC机制ID值。key值为一个32位的整型数据。
但如果所有的程序使用固定的key创建这些IPC机制则有违软件设计思想,因此,为了尽可能与系统信息的载体(文件)关联,Linux提供函数ftok()来创建key值,在此函数的参数中,需要特定文件作为参数。此函数声明如下:

extern key_t ftok(__const char *__pathname, int __proj_id);

此函数有两个参数,pathname为文件路劲名,可以为特殊文件(例如目录文件),也可以是当前目录“.”,通常设置此参数为当前目录,因为当前目录一般都存在,并且不会被立即删除,第二个蚕食为一个int型变量。
每个文件都有其自身的属性,可以通过stat()函数读取,在ftok()函数创建key值过程中使用了该文件属性的st_dev和st_ino。具体构成如下:
(1)key值的第31~24位(8bit)为ftok()第2个参数的低8位
(2)key值的第23~16位(8bit)为该文件的st_dev属性的低8位
(3)key值的第15~0位(16bit)为该文件的st_ino属性的低16位
因此,如果使用相同的文件路劲及整数(第2个参数),得到的key值是唯一的

3.消息队列IPC原理

3.1 消息队列模型
消息队列是消息的链式队列,由msqid_ds和msg结构组成
msgid_ds消息队列数据结构:描述整个消息队列的属性,主要包括整个消息队列的权限,拥有者,两个重要的指针分别指向消息队列的第一个消息和最后一个消息。
msg消息队列数据结构:整个消息队列的主体,一个消息队列有若干消息,每个消息数据结构的基本成员包括消息类型,消息大小,消息内容指针和下一个消息数据结构位置。
默认情况下,整个系统中最多允许有16个消息队列
每个消息队列最大为16384字节
消息队列中的每个消息最大为8192字节

3.2 创建消息队列
在使用一个消息队列钱,需要使用msgget函数创建该消息队列,其函数声明如下:

extern int msgget(key_t __key, int __msgflg);

第1个参数key为由ftok创建的key值
第2个参数msgflg的低位用来确定消息队列的访问权限,其最终权限为当前进程umask值与设置值perm类似open函数,即最终值为perm&~umask,

// include /usr/include/bit/ipc.h
/* resource get request flags */
#define IPC_CREAT           00001000  //如果key不存在,则创建,存在,则返回ID
#define IPC_EXCL              00002000  //如果key存在,返回失败
#define IPC_NOWAIT         00004000  //如果需要等待,则直接返回错误

3.3 消息队列属性控制
创建消息队列后,可以对该消息队列的基本属性进行控制(修改),控制消息队列属性的函数为msgctl:

extern int msgctl(int __msqid, int __cmd, struct maqid_ds *_buf);

此函数包括3个参数
第1个参数__msqid为消息队列的标识符,该值由msgget函数创建返回值
第2个参数__cmd为执行的控制命令,即要执行的操作,包括以下选项:

//include /usr/include/linux/ipc.h
/* control commands used with semctl,msgctl and shmctl see also specific commands in sem.h,msg.h,shm.h */
#define IPC_RMID  0    //删除
#define IPC_SET    1    //设置ipc_perm参数
#define IPC_STAT   2    //获取ipc_perm参数
#define IPC_INFO   3    //如ipcs,获取限制信息

3.4 发送信息到消息队列
msgsnd()函数将新的消息添加到消息队列尾端。此函数声明如下:

//include /usr/include/sys/msg.h
//Send message to message queue
extern int msgsnd(int _msqid, _const void *_magp, size_t _msgsz, int _msgflg);

此函数参数说明如下:
第1个参数msqid为指定的消息队列标识符(由msgget生成的消息队列标识符),即将消息添加到哪个消息队列中。
第2个参数msgp指向的用户定义缓冲区,下面是用户定义缓冲区结构:

//include /usr/include/linux/msg.h
//message buffer for msgsnd and msgrcv calls
struct msgbuf {
	long mtype;    		//消息类型
	char mtest[1];		//消息内容,在使用时自己重新定义此结构
}

第3个参数为接受信息的大小,其数据类型为size_t,即unsigned int 类型,其大小为0到系统对消息队列的限制值
第4个参数用来指定在达到系统为消息队列所界定是应采取的操作。
如果设置为IPC_NOWAIT,如果需要等待,则不发送消息并且调用进程立即返回错误信息EAGAIN.
如果为设置IPC_NOWAIT,则阻塞调用进程
成功调用后,此函数将返回0,否则返回-1,同时将对消息队列msqid数据结构的成员执行下列操作。
msg_qnum以1位增量递增
msg_lspid设置为调用进程的进程ID
msg_stime设置为当前时间

3.5 从消息队列接受信息
msgrcv用于从队列中取消息,其函数声明如下:

extern int msgrcv(int _msqid, void *_msgp, size_t _msgsz, long int _msgtype,int _msgflg);

此函数从msqid指定的消息队列中读取消息,并将其放置到由msgp指向的内存空间中。
第1个参数为读的对象,即从哪个消息队列获取消息
第2个参数为一个临时消息数据结构,用来保存读取的信息。其结构如下:

//include /usr/include/linux/msg.h
//message buffer for msgsnd and msgrcv calls
struct msgbuf {
	long mtype;    		//消息类型
	char mtest[1];		//消息内容,在使用时自己重新定义此结构
}

mtype数接受到消息的类型,mtext为消息内容位置。
第3个参数msgsz用于指定mtext的大小(以字节为单位)。如果收到的消息大于msgsz并且msgflg&MSG_NOERROR为真,则将该消息截至msgsz字节,消息的截断部分将丢失,并且不向调用进程提供截断的提示。
第4个参数msgtyp用于指定请求的消息类型,具体如下所示:
msgtype=0:接受队列中的第一条信息,任意类型
msgtype>0:接收第一条msgtype类型的消息
msgtype<0:接收第一条最低类型的消息
第5个参数msgflg用于指定所需类型消息不在队列上时将要采取的操作。
如果设置为IPC_NOWAIT,如果现在没有消息,调用进程立即返回,同时返回-1,并将errno设为ENOMSG.
如果为设置IPC_NOWAIT,则阻塞调用进程行,直至出现以下任何一种情况发生。
某一所需类型的消息被放置到队列中
msqid从系统中删除,当该情况发生时,将errno设为EIDRM,并返回-1
调用进程收到一个要捕获的信号,在这种情况下,未收到消息,并且调用进程按signal(SIGTRAP)中指定方式恢复执行
接收消息成功完成后,该消息将自动从消息队列中删除,并返回接收到的消息大小,同时将对消息队列msqid数据结构的成员执行下列操作。
msg_qnum以1位增量递减
msg_lspid设置为调用进程的进程ID
msg_stime设置为当前时间

温馨提示:
以上文章描述如有不清晰之处,欢迎在评论区评论,如有时间,会第一时间回复,谢谢!

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!