Linux 进程信息 system V-IPC 之消息队列

消息队列 (MSG)

忆前面所述的管道,这种通信机制的一个弊端是:你无法在管道中读取一个“指定” 的数据,尽管使用有名管道时信息之间不会相互踩踏,但是在多个进程同时往同一个管道发送数据的情况下,无法直接提取自己想要的某个进程发的数据,因为这些数据没有做任何标记,读者进程只能按次序地挨个读取,因此多对进程间的相互通信,除非使用多条管道分别处理,否则无法使用一条管道来完成。

Linux 进程信息 system V-IPC 之消息队列_第1张图片

消息队列的特点

消息队列提供一种带有数据标识的特殊管道,使得每一段被写入的数据都变成带标识 消息,读取该段消息的进程要指定这个标识就可以正确地读取,而不会受到其他消息的干 扰,从运行效果来看,一个带标识的消息队列,就像多条并存的管道一样。

Linux 进程信息 system V-IPC 之消息队列_第2张图片

消息队列的使用方法一般是:

1 ,发送者:

A)  获取消息队列的 ID

B)  将数据放入一个附带有标识的特殊的结构体,发送给消息队列。

 2 ,接收者:

A)  获取消息队列的 ID

B)  将指定标识的消息读出。

当发送者和接收者都不再使用消息队列时,及时删除它以释放系统资源。 面详细解剖消息队列 (MSG) 的 API

取消息队列的 ID

文件

#include <sys/types.h>

#include <sys/ipc.h>

#include <sys/msg.h>

int msgget(key_t key, int msgflg);

参数

key

消息队列的键值

msgflg

IPC CREAT

_

如果 key 对应的 MSG 不存在,则创建该对象

IPC  EXCL

_

果该 key 对应的 MSG 已经存在,则报错

mode

MSG 的访问权 (八进制,如 0644)

回值

成功

息队列的 ID

- 1

 key 指定为为 IPC_PRIVATE,则会自动产生一个随机未用的新键值

 函数 msgget()的接口规范

使用该函数需要注意的以下几点:

1,选项 msgflg 是一个位屏蔽字,因此 IPC_CREATIPC_EXCL 和权限 mode 可以用位 或的方式叠加起来,比如:msgget(key, IPC_CREAT | 0666); 表示如果 key 对应的消息队 列不存在就创,且权限指定为 0666,若已存在则直接获取 ID

2,权限只有读和写,执行权限是无效的,例如 0777 0666 是等价的。

3,当 key 被指定为 IPC_PRIVATE 时,系统会自动产生一个未用的 key 来对应一个新的 消息队列对象。一般用于线程间通信。

发送、接收消息

文件

#include ys/types.h>

#include <sys/ipc.h>

#include <sys/msg.h>

int msgsnd(int msqid, const void *msgp,   size_t msgsz, intmsgflg); ssize_t msgrcv(int msqid, void *msgp,size_t msgsz, long msgtyp, int msgflg);

参数

msqid

送、接收消息的消息队列 ID

msgp

要发送的数据、要接收的数据的存储区域指针

msgsz

要发送的数据、要接收的数据的大小

msgtyp

这是 msgrcv 独有的参数,代表要接收的消息的标识

msgflg

IPC  NOWAIT

_

非阻塞读出、写入消息

MSG  EXCEPT

_

读取标识不等于 msgtyp 的第一个消息

MSG  NOERROR

_

消息寸比 msgsz 大时,截断消息而不报错

回值

成功

msgsnd( )

0

msgrcv( )

真正读取的字节数

- 1

使用这两个收、发消息函数需要注意以下几点:

1,发送消息时,消息必须被组织成以下形式:

struct msgbuf

{

long mtype;  // 消息的标识

char mtext[1];    // 消息的正文

};

就是说:

发送出去的消息必须以一个 long 型数据打头,作为该消息的标识,后面的数据则没有要求

2,消息标识可以是任意长整型数值,但不能是 0L

3,参数 msgsz 是消息中正文的大小,不包含消息的标识。

设置或者获取消息队列的相关属性

文件

#include ys/types.h>

#include <sys/ipc.h>

#include <sys/msg.h>

int msgctl(int msqid, int cmd, struct msqid_ds *buf);

参数

msqid

息队列 ID

cmd

IPC  STAT

_

获取该 MSG 的信息,储存在结构体 msqid_ds 

IPC SET

_

设置该 MSG 的信息,储存在结构体 msqid_ds

IPC  RMID

_

即删除该MSG,并且唤醒所有阻塞在该 MSG 上的进程, 时忽略第三个参数

IPC INFO

_

获得关于当前系统中 MSG 的限制值信息

MSG INFO

_

获得于当前系统中 MSG 的相关资源消耗信息

MSG STAT

_

IPC_STAT,但 msgid 为该消息队列在内核中记录所有 队列信息的数组的下标,因此通过迭代所有的下标 可以获得系统中所有消息队列的相关信息

buf

相关信息结构体缓冲区

回值

成功

IPC  STAT

_

0

IPC  SET

_

IPC  RMID

_

IPC  INFO

_

内核中记录所有消息队列信息的数组的下标最大值

MSG  INFO

_

MSG  STAT

_

消息队列的 ID

- 1

示例代码

创建一个消息队列

#include 
#include 
#include 
#include 

struct msgbuf {
   long mtype;       /* message type, must be > 0 */
   char mtext[128];//mtext[0]    /* message data */
};


int main()
{
	//创建KEY键值
	key_t key = ftok("./", 1);
	//创建消息队列
	int msgid = msgget(key, IPC_CREAT | 0777);//open('',O_CREAT|O_)
	if(-1 == msgid)
	{
		perror("creat msg failed");
		return -1;
	}
	struct msgbuf buf;
	buf.mtype = 1;
	
	while(1)
	{
		fgets(buf.mtext,128,stdin);
		
		msgsnd(msgid, &buf, sizeof(buf), 0); 
		
	}
	
	
}

创建接收队列 

#include 
#include 
#include 
#include 

struct msgbuf {
   long mtype;       /* message type, must be > 0 */
   char mtext[128];//mtext[0]    /* message data */
};


int main()
{
	//创建KEY键值
	key_t key = ftok("./", 1);
	//创建消息队列
	int msgid = msgget(key, IPC_CREAT | 0777);//open('',O_CREAT|O_)
	if(-1 == msgid)
	{
		perror("creat msg failed");
		return -1;
	}
	struct msgbuf msg;
	
	while(1)
	{
		
		
		msgrcv(msgid, &msg, sizeof(msg), 1, 0);
		
		printf("%s\n",msg.mtext);

		
	}
	
	
}

消息队列总结

消息队列使用简单,老少咸宜,但是他跟管道一样,都是需要“代理人”的进程通信机

制:内核充当了这个代人,内核为使用者分配内存,检查边界,设置阻塞,以及各种权限

监控,使得我们用起来常省心省力,但是任何事情都是有代价的:代理人机制使得他们的 效率都不高,因为两个进程的数据传递并不是直接了当的,而是要经过内核的辗转接力的, 因而他们都不适合用来传输海量数据。

而能解决这个问题的,就是下面要解剖的共享内存,欲知后事如何,且听下节分解。

你可能感兴趣的:(linux,c#,c语言)