第46章 System V消息队列

本章介绍 System V消息队列。消息队列允许进程以消息的形式交换数据。尽管消息队列在某些方面与管道和FIFO类似但是他们之间仍然存在显著的差别。

  • 用来引用消息队列的句柄是一个由msgget()调用返回的标识符。这些标识符与UNIX系统上大多数其他形式的I/O所使用的文件描述符是不同的
  • 通过消息队列进行的通信是面向消息的,即读者收到由写者写入的整条消息。读取一条消息的一部分而让剩余部分遗留在队列中或一次读取多条消息是不可能的。这一点与管道不通,管道提供的是一个无法区分的字节流(即使用管道时读者一次可以读取任意数量的字节数,不管写者写入的数据块是什么)。
  • 除了包含数据之外,每条消息还有一个用用整数标识的类型,从消息队列中既可以按照先入先出的顺序,也可以根据消息的类型来读取消息
  • 本章最后(46.9节),将会对System V消息队列所存在的限制进行总结。由于存在这些限制,因此新应用程序应该尽可能避免使用System V消息队列,而应使用其他形式的IPC机制,如FIFO,POSIX消息队列以及socket,但在消息队列一开始被设计出来的时候。这些候选机制不是还没有被设计出来就是还没有在UNIX实现中被广泛应用,其结果是存在各类使用消息队列的既有应用程序。这写也是在这里对消息队列进行介绍的主要原因之一。

46.1创建或打开一个消息队列,

msgget()系统调用创建一个新消息队列或取得一个既有队列的标识符

#include<sys/types.h>
#include<sys/msg.h>
int msgget(key_t key,int msgflag);

key参数是使用值IPC_PRIVATE或者是ftok()返回的一个键。msgflag参数是一个指定施加于新消息对抗列上的权限或检查一个既有队列的权限的位掩码。此外msgflag参数中还可以将下列标记中的零个或多个标记取==|==以控制msgget()操作。
IPC_CREAT
如果没有专门与指定的key对应的消息队列,那么就创建一个新队列。
IPC_EXCL,
如果同时指定了IPC_CREAT,并且指定的key已经存在,那么调用就会失败会返回EEXIST错误。

 ______________________________________svmsg_create.c
 #include<sys/types.h>
 #include<sys/ipc.h>
 #include<sys/msg.h>
 #include<sys/stat.h>
 int main()
 {
 	int msqid = 0;
 	msqid = msgget(IPC_PRIVATE,IPC_CREAT|IPC_EXCL|S_IRUSR|S_IWUSR);
 	if(msqid ==-1)
 	{
 		perror("msgget failed");
 		return -1;
 	}
 	printf("msqid = %d\n",msqid);
 	return 0;
}

46.2 交换消息

msgsnd()和msgrcv()系统调用执行消息队列上的I/O.这两个系统调用接受的第一个参数是消息队列标识符(msqid),第二个参数msgp是一个由程序员定义的结构体指针,该结构用于存放被发送或者被接受的消息,该结构的常规形式如下

struct mymsg{
	long mtype;  //消息类型
	char mtext[];  //消息内容  body
}

这个定义仅仅简要的说明了消息的第一个部分包干了消息类型,他用一个类型为long的整数表示,而消息的剩余部分则是由程序员第一的一个结构体,长度和内容是任意的,而无需是一个字符数组,因此msgp参数的类型为void *,这样就允许传入任意类型的指针结构了
mtext字段长度可以为0,,当对于接收进程来讲所需传递的信息仅仅通过消息类型就能表示或只需要一条消息是否存在时,这种做法就非常有用了

46.2.1 发送消息

msgsnd()系统调用向消息队列写入一条消息。函数定义如下

#include<sys/type.h>
#include<sys/msg.h>
int msgsnd(int msqid,const void *msgp,size_t msgz,int msgflg);
					Returns 0 on sucess,or -1 on error
注:在使用msgsnd发送消息时不存在write()所具备部分写的概念,这也是成功别的msgsnd只需要返回0,而不是所发送的字节数的原因。

使用msgsnd()发送消息必须要将消息结构中的mtype字段值设置为一个大于0的操作(下一节讨论msgrcv()时会介绍这个值的用法),并将所传入的信息赋值到程序员定义的mtext字段中,msgsz参数指定了mtext字段中包含字节的字节数
最后一个参数 msgflg是一组位掩码的标记目前只定义了一个这样的标记。
IPC_NOWAIT
执行一个非阻塞的发送操作通常消息满时,msgsnd()会阻塞直到队列中有足够的空间来存放这些消息。但是如果指定了这个标记,那么msgsnd(),就会立即返回EAGAIN错误。
像消息队列写入消息要求具备在该队列上的写权限。

----------svmsg_send.c
#include<sys/type.c>
#include<sys/msg.h>
#define MAX_MTETX 1024
struct mbuf{
	long mtype;
	char- mtext[MAX_MTETX];  //Messg body
}
int main()
{
	char *msg = "system v ipc msgq";
	int msgLen = 0;
	int msqid = msgget(IPC_PRIVATE,IPC_CREAT|S_IRUSR|S_IWUSR);
	struct mbuf msg;
	memcpy(msg.mtext,msg,streln(msg));
	msg.mtype = 1;
	msgLen = streln(msg);
	//sendmsg
if(msgsnd(msqid,&msg,msgLen,flags) == -1)
{
	perror(""msgsnd failed);
}	
return 0;
}

46.2.2接受消息

msgrcv()系统调用从消息队列中读取(以及删除)一条消息并将其复制进msgp指向的缓冲区中。定义如下:

#include<sys/types.h>
#include<sys/msg.h>
ssize_t msgrcv(int msqid,void *msgp,size_tmaxmsgsz,long msgtyp,int msgflg);
Returns number of bytes copied int mtext filed,or -1 on error

msgp 缓冲区mtext字段的最大可用空间是通过maxmagsz指定的。如果队列中待删除的消息体超过了maxmsgsz字节,那么就不会从队列中删除消息,并且msgrcv()会返回E2BIG.这个是默认行为。可以通过MSG_NOERROR标记来改变这种行为
,稍后会对此介绍。
读取消息的顺序无需与发送的一致,可以根据mtype字段的值来选择消息,而这个选择过程是由msgtyp参数控制的,具体如下描述。

-== 如果msgtyp等于0,那么就会删除队列中的第一条消息并将其返回给调用进程。==

  • 如果msgtyp大于0,如果msgtyp大于0,那么就会将队列中第一条msgtype等于msgtyp的消息删除并将其返回给调用进程。通过指定不同的mygtyp值,多个进程能够从同一消息队列中读取消息,而不会出现竞争读取同一条消息的情况。比较有用的一项技术是让各个进程选取与自己进程ID匹配的消息。
  • 如果msgtyp小于0,那么就会将等待消息当成优先队列来处理。队列中mtype最小并且其值小于或等于msgtyp的绝对值的第一条消息就会被删除并返回给调用进程。
    msgflag参数是一个位掩码,他的值通过将下列标记中的零个或多个取OR来确定。
    IPC_NOWAIT
    执行一个非阻塞接收,通常如果队列中没有匹配msgtyp消息,那么msgrcv()会阻塞直到队列中存在匹配的消息为止。指定IPC_NOWAIT标记会导致msgrcv()立即返回ENOMSG错误
    MSG_EXCEPT
    只有当msgtyp大于0时,这个标记才会起作用,他会强制对常规操作进行补足,即将队列中的第一条不等于msgtyp的消息删除并返回给调用者。这个标记时linux特有的下图给出的消息队列上执行一系列形式为msgrcv(id,&msg,maxmsgsz,100,MSG_EXCEPT)的调用将会按照1,3,4顺序执行之后发生阻塞。
消息类型消息正文
300
100
200
400
100
MSG_NOERROR
在默认情况下,当消息的mtext字段的大小超出了可用空间时,(由maxmsgsz参数定义),msgrcv()调用会失败,如果指定了MSG_NOERROR标记,那么msgrcv()将会从队列中删除消息并将其mtext字段大小截断为maxmsgsz字节,然后将消息返回给调用者,被截取的字段消失
msgrcv()成功之后会返回接收到消息的mtext字段的大小,发生错误时返回-1.
从消息队列上读取消息需要具备在队列上的读权限

46.3消息队列控制操作

msgctl()系统调用在标识符为msqid的消息队列上执行控制操作。定义如下

#include<sys/types.h>
#include<sys/msg.h>
int msgctl(int msqid,int cmd,struct msqid_ds *buf);
		Returns 0 on success or -1 on error;

cmd 参数指定了在队列上执行的操作,取值时下列值中的一个。
IPC_RMID
立即删除消息队列及其关联的msqid_ds数据结构。队列中所有剩余的消息会丢失。所有被阻塞的读者和写者会立即醒来。msgsnd()和msgrcv()会失败并返回错误EIDRM,。这个操作会忽略传递给msgctl()的第三个参数。
IPC_STAT
将与这个消息队列关联的msqid_ds数据结构的复本放到buf指向的缓冲区中
IPC_SET
使用buf指向的缓冲区提供的值更新与这个消息队列先相关联的msqid_ds数据结构中被选中的字段。

//删除System V消息队列                               svmsg_rm.c
#include<sys/type.h>
#include<sys/msg.h>

int main()
{
	int msqid;
	msqid = msgget(IPC_PRIVATE,IPC_EXCL);
	if(msgctl(msqid,IPC_RMID,NULL) ==-1)
	{
		return -1;
	}
}

46.5 消息队列的限制

大多数UNIX实现会对System V 消息队列的操作施加各种各样的限制。下面会对linux系统上的限制进行介绍并指出与其他UNIX实现的差别。
Linux会对队列操作操作施加下列限制,括号中列出了限制所影响到的系统调用以及当达到限制时所产生的错误。
MSGMNI
这是一个系统级别的限制,它规定了系统中所能创建的消息队列标识符(h换句话说时消息队列的数量)
MSGMAX
这是一个系统及北的限制,它规定了单挑消息中最多可写入的字节数msgsnd()
MSGMNB
一个消息队列中一次最多保存的字节数,这个限制是一个系统级别的参数,它用来初始化与消息队列相关联的msqid_da数据结构的msg_qbytes字段。如果达到一个队列的msg_qbytes限制,那么msgsnd()会阻塞或在IPC_NOWAIT被设置时返回EAGAIN错误,
在linux上可以修改/proc文件系统中的文件来查看和修改这些限制
System V消息队列限制

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值