Linux下的通信方式之 System_V 消息队列
消息队列:允许进程之间使用以消息的形式传输数据。虽然消息队列在某些地方和管道|FIFO类似,但是仍然存在者显著的差别。
消息队列的特点有:
用来引用消息队列的句柄是一个由 msgget()调用返回的标识符。这些标识符与 UNIX系统上大多数其他形式的 I/O所使用的文件描述符是不同的。
每次在读取数据的时候,读取的单位只能是以整条消息为单位的读取,无法像管道那样想一次读取多少数据就可以有多少数据。
除了包含数据之外,每条消息还有一个用整数表示的类型。从消息队列中读取消息既可以按照先入先出的顺序,也可以根据类型来读取消息。
需要注意的是,在新的一个应用程序中应该要少用 System V 消息队列,这是因为这类消息队列有很大的限制。我们可以使用管道、FIFO、POSIX 消息队列、Socket 来进行替代。
每个队列都有一个msqid_ds结构与其关联:
1 2 3 4 5 6 7 8
| struct msqid_ds{ struct ipc_perm msg_perm; msgqnum_t msg_qnum; msglen_t msg_qbytes; pid_t msg_lspid; pid_t msg_lrpid; ... }
|
关于消息队列的几个函数
在前面我们提到,对于 IPC 的创建需要使用 get
系列的函数:
其中void* msg_ptr
这个参数是有要求的,即该结构必须以 loog
类型开头:
1 2 3 4 5 6 7
| struct mysmg{ long mtype char mtext[] }
其中第一个参数表示这个消息的类型,直接使用数字表示即可,如1,2,3等等 第二个参数是就是消息的存储空间了,并且 mtext 的长度是可以为 0 的,此时对于接收进程来说可以知道某条消息是否存在。
|
msgget()
1 2 3 4 5 6
| #include <sys/types> #include <sys/msg.h>
int msgget(key_t key, int msgflg);
|
常用的 megflg
如下:
IPC_CREAT
如果没有与指定的 key对应的消息队列,那么就创建一个新队列。
IPC_EXCL
如果同时还指定了 IPC_CREAT并且与指定的 key对应的队列已经存在,那么调用就会失败并返回 EEXIST错误。
megsnd()
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| #include<sys/msg.h>
int msgsnd(int msqid, const void *msg_ptr, size_t msg_sz, int msgflg);
第一个参数msqid是由msgget函数返回的消息队列标识符; 第二个参数msg_ptr是一个指向准备发送消息的指针,消息必须像刚才说的那样以一个长整型成员变量开始; 第三个参数msg_sz是msg_ptr指向的消息的长度。这个长度不能包括长整型消息类型成员变量的长度; 第四个参数msgfig: 0:当消息队列满时,msgsnd将会阻塞,直到消息能写进消息队列。 IPC_NOWAIT:当消息队列已满的时候,msgsnd函数不等待立即返回。 IPC_NOERROR:若发送的消息大于size字节,则把该消息截断,截断部分将被丢弃,且不通知发送进程
返回值: 成功:0; 失败:-1;
|
msgrcv()
1 2 3 4 5 6 7 8 9 10 11 12
| #include<sys/msg.h>
int msgrcv(int msqid, void *msg_ptr, size_t msg_sz, long int msgtype, int msgflg); 第一个参数msqid是由msgget函数返回的消息队列标识符;
第二个参数msg_ptr是一个指向准备接收消息的指针,消息必须像前面msgsnd函数中介绍的那样以一个长整型成员变量开始。
第三个参数msg_sz是msg_ptr指向的消息的长度,它不包括长整型消息类型成员变量的长度。
第四个参数msgtype是一个长整型,它可以实现一种简单形式的接收优先级。如果msgtype的值为0,就获取队列中的第一个可用消息。如果它的值大于零,将获取具有相同消息类型的第一个消息。如果它的值小于零,将获取消息类型等于或小于msgtype的绝对值的第一个消息。 这个函数看起来好像很复杂,但实际应用很简单。如果只想按照消息发送的顺序来接收它们,就把msgtype设置为0。如果只想获取某一特定类型的消息,就把msgtype设置为相应的类型值。如果想接收类型等于或小于n的消息,就把msgtype设置为-n。 第五个参数msgflg: 0: 阻塞式接收消息,没有该类型的消息msgrcv函数一直阻塞等待。 IPC_NOWAIT:如果没有返回条件的消息调用立即返回,此时错误码为ENOMSG。 IPC_EXCEPT:与msgtype配合使用返回队列中第一个类型不为msgtype的消息。 IPC_NOERROR:如果队列中满足条件的消息内容大于所请求的size字节,则把该消息截断,截断部成功:实际读取到的消息数据长度。 出错:-1,错误原因存于error中。
|
作用:从队列中取用消息。
其中对第四个参数举例说明:消息队列中要求通过一个数据类型来索引指定的数据。也就是在msgsng发送数据前会对long int message_type变量进行复制。通过这个变量来标识数据。而且这个变量必须要大于0.
那么在msgrcv中,对第四个参数msgtype进行选值时。
如果等于0,则按照队列的顺序去取,先到先取的原则。
如果大于0,假如msgtype=1。就会取找队列中message_type等于1的队列的排在第一个的消息。
如果小于0,假如msgtype=-2。就会-2取绝对值,也就是等于2。再取队列中message_type等于或者小于2的排在第一个的消息。
msgrcv()解除阻塞的条件有以下三个:
① 消息队列中有了满足条件的消息。
② msqid代表的消息队列被删除。
③ 调用msgrcv()的进程被信号中断。
msgctl()
1 2 3 4 5 6 7 8 9 10
| #include<sys/msg.h>
int msgctl(int msqid, int command, struct msqid_ds *buf); 第一个参数msqid是由msgget返回的消息队列标识符; 第二个参数command是将要采取的动作,它可以取3个值。 IPC_STAT 取出队列的msqid_ds结构,并将它存放在buf指向的结构中。 IPC_SET 如果进程有足够的权限,将buf指向的结构复制到与这个队列相关的msqid_ds结构中。 IPC_RMID 从系统中删除该消息队列以及仍在该队列中的所有数据。这种删除立即生效。仍在使用这一消息队列的其他进程在它们下一次试图对此队列操作,将得到EIDRM错误。
返回值: 成功:0; 失败:-1
|