linux进程间通信之消息队列
2010-09-03 21:16
169 查看
上周因课题的需要,写了一个进程间使用消息队列通信的程序。之所以,用因为消息队列是随内核存在的,即使进程退出它仍然存在
。消息队列的这种特点正适合我的需要。下面是来自CSDN博客上的一篇文章,转自http://blog.csdn.net/liranke/archive/2010/05/19/5608686.aspx
1.
基本概念
消息队列的最佳定义是:内核地址空间中的内部链表。消息可以顺序地发送到队列中,
并以几种不同的方式从队列中获取。当然,每个消息队列都是由
IPC
标识符所唯一标识的。
2.
内部和用户数据结构
要完成理解象系统
V IPC
这样复杂的问题,关键是要彻底熟悉内核的几个内部数据结构。
甚至对那些最基本的操作来说,直接访问这些结构中的某几个结构也是必要的,而其他的结
构则停留在一个更低的级别上。
3.
消息缓冲区
我们要介绍的第一个结构是
msgbuf
结构。这个特殊的数据结构可以认为是消息数据的模
板。虽然定义这种类型的数据结构是程序员的职责,但是读者绝对必须知道实际上存在
msgbuf
类型的结构。它是在在
linux/msg.h
中定义的,有
2
个成员:
• mtype
—
它是消息类型,以正数来表示。这个数必须为一个正数!
• mtext
—
它就是消息数据。
4.
内核
msg
结构
内核把消息队列中的每个消息都存放在
msg
结构的框架中。该结构是在
linux/msg.h
中定义
的,如下是其成员的描述:
• msg_next
—
这是一个指针,指向消息队列中的下一个消息。在内核寻址空间中,它们
是当作一个链表存储的。
• msg_type
—
这是消息类型,它的值是在用户结构
msgbuf
中赋予的。
• msg_spot
—
这是一个指针,指向消息体的开始处。
• msg_ts
—
这是消息文本
(
消息体
)
的长度。
•
内核
msgid_ds
结构
—
IPC
对象分为三类,每一类都有一个内部数据结构,该数据结构
是由内核维护的。对于消息队列而言,它的内部数据结构是
msqid_ds
结构。对于系统上
创建的每个消息队列,内核均为其创建、存储和维护该结构的一个实例。该结构在
linux/msg.h
中定义,如下所示:
struct msqid_ds{
struct ipc_perm msg_perm;
msgqnum_t msg_qnum;
msglen_t msg_qbytes;
pid_t msg_lspid;
pid_t msg_lrpid;
time_t msg_stime;
time_t msg_rtime;
time_t msg_ctime;
...
...
};
在不同的系统中,此结构会有不同的新成员,这里只列出最少拥有的关键成员。其中,
msg_qbytes
成员以及
msg_qnum
成员在不同的系统也会有不同的上限值,这里就不逐一介绍了,详细内容请参阅相关系统手册。
• msg_perm
—
它是
ipc_perm
结构的一个实例,
ipc_perm
结构是在
linux/ipc.h
中定义的。
该成员存放的是消息队列的许可权限信息,其中包括访问许可信息,以及队列的创建者
的有关信息
(
如
uid
等等
)
。
• msg_first
—
链接到队列中的第一个消息
(
列表头部
)
。
• msg_last
—
链接到队列中的最后一个消息
(
列表尾部
)
。
• msg_stime
—
发送到队列的最后一个消息的时间戳
(time_t)
。
• msg_rtime
—
从队列中获取的最后一个消息的时间戳。
• msg_ctime
—
对队列进行最后一次变动的时间戳。
5.
内核
ipc_perm
结构
内核把
IPC
对象的许可权限信息存放在
ipc_perm
类型的结构中。例如在前面描述的某个消
息队列的内部结构中,
msg_perm
成员就是
ipc_perm
类型的,它的定义是在文件
linux/ipc.h
中,
以上所有的成员都具有相当的自扩展性。对象的创建者以及所有者
(
它们可能会有不同
)
的
有关信息,以及对象的
IPC
关键字都是存放在该结构中的。八进制形式的访问模式也是存放在
这里的,它是以一种无符号短整型的形式存储的。最后,时间片使用序列编号存放在最后面,
每次通过系统调用关闭
IPC
对象
(
摧毁
)
时,这个值将被增加一,至多可以增加到能驻留在系统
中的
IPC
对象的最大数目。用户需要关心这个值吗?答案是
“
不
”
。
有关这个问题,在
Richard Stevens
所著的《
Unix Network Programming
》一书的第
125
页
中作了精辟的讨论。该书还介绍了
ipc_perm
结构的存在和行为在安全性方面的原因。
6.
创建消息队列:
(
1
)
msgget
简介:为了创建一个新的消息队列,或者访问一个现有的队列,可以使用系统调用
msgget ( )
。
msgget ( )
的第一个变元是关键字的值
(
在我们的例子中该值是调用
ftok ( )
的返回值
)
。这个
关键字的值将被拿来与内核中其他消息队列的现有关键字值相比较。比较之后,打开或者访
问操作依赖于
msgflg
变元的内容。
• IPC_CREAT
—
如果在内核中不存在该队列,则创建它。
• IPC_EXCL
—
当与
IPC_CREAT
一起使用时,如果队列早已存在则将出错。
如果只使用了
IPC_CREAT, msgget ( )
或者返回新创建消息队列的消息队列标识符,或者
会返回现有的具有同一个关键字值的队列的标识符。如果同时使用了
I P C _ E X C L
和
IPC_CREAT
,那么将可能会有两个结果。或者创建一个新的队列,或者如果该队列存在,则
调用将出错,并返回-
1
。
IPC_EXCL
本身是没有什么用处的,但在与
IPC_CREAT
组合使用时,
它可以用于保证没有一个现存的队列为了访问而被打开。
有个可选的八进制许可模式,它是与掩码进行
OR
操作以后得到的。这是因为从功能上讲,
每个
IPC
对象的访问许可权限与
Unix
文件系统的文件许可权限是相似的!
(
2
)
msgget
举例:
下面实例演示了使用
msgget
函数创建一个队列,函数中参数
falgs
指定为
IPC_CREAT|0666
,说明新建一个权限为
0666
的消息队列,其中组用户、当前用户以及其他用户拥有读写的权限。并在程序的最后使用
shell
命令
ipcs –q
来查看系统
IPC
的状态。
(
1
)在
vi
编辑器中编辑该程序如下:
程序清单
14-12 create_msg.c msgget
函数
#include <sys/msg.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <stdio.h>
#include <stdlib.h>
int main ( void )
{
int qid;
key_t key;
key = 113;
qid=msgget( key, IPC_CREAT | 0666 )
;
/*
创建一个消息队列
*/
if ( qid < 0 ) { /*
创建一个消息队列失败
*/
perror ( "msgget" );
exit (1) ;
}
printf ("created queue id : %d /n", qid ); /*
输出消息队列的
ID */
system( "ipcs -q" ); /*
查看系统
IPC
的状态
*/
exit ( 0 );
}
(
2
)在
shell
中编译该程序如下:
$gcc create_msg.c–o create_msg
(
3
)在
shell
中运行该程序如下:
$./ create_msg
created queue id : 0
------ Message Queues --------
key msqid owner perms used-bytes messages
0x0000af40 623430 root 666 0 0
0x0000007b 0 root 666 0 0
在程序中使用了系统命令
ipcs
,命令参数
-q
说明只查看消息队列的状态。注意在输出消息中,
key
段标明的是
IPC
的
key
值,
msqid
为该队列的
ID
值,
perms
为执行权限。同样,队列的执行权限像其他
IPC
对象一样没有执行权限。函数
msgctl
可以在队列上做多种操作,函数原型如下:
#include <sys/msg.h>
int msgctl( int msqid, int cmd , struct msqid_ds *buf );
参数
msqid
为指定的要操作的队列,
cmd
参数指定所要进行的操作,其中有些操作需要
buf
参数。
cmd
参数的详细取值及操作如表
14-9
所示。
表
14-9 cmd
参数详解
下面实例演示了调用
msgctl
函数操作队列,程序中先读取命令行参数,如没有,则打印命令提示信息,在调用
msgctl
函数执行删除操作的前后分别调用了一次
shell
命令
ipcs –q
来查看系统
IPC
的状态。
(
1
)在
vi
编辑器中编辑该程序如下:
程序清单
14-13 del_msg.c
调用
msgctl
删除指定队列
#include <sys/msg.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <stdio.h>
#include <stdlib.h>
int main ( int argc ,char *argv[] )
{
int qid ;
if ( argc != 2 ){ /*
命令行参数出错
*/
puts ( "USAGE: del_msgq.c <queue ID >" );
exit ( 1 );
}
qid = atoi ( argv[1] ); /*
通过命令行参数得到组
ID */
system( "ipcs -q");
if ( ( msgctl( qid, IPC_RMID, NULL ) ) < 0 ){ /*
删除指定的消息队列
*/
perror ("msgctl");
exit (1 );
}
system( "ipcs -q");
printf ( "successfully removed %d queue/n", qid ); /*
删除队列成功
*/
exit( 0 );
}
(
2
)在
shell
中编译该程序如下:
$gcc del_msg.c–o del_msg
(
3
)在
shell
中运行该程序如下:
$./ del_msg
------ Message Queues --------
key msqid owner perms used-bytes messages
0x0000007b 0 root 666 0 0
------ Message Queues --------
key msqid owner perms used-bytes messages
successfully removed 0 queue
7.
读写消息队列
一旦获得了队列标识符,用户就可以开始在该消息队列上执行相关操作了。为了向队列
传递消息,用户可以使用
msgsnd
系统调用.
由于消息队列的特殊性,系统为这个数据类型提供了两个接口(
msgsnd
函数,
msgrcv
函数),分别对应写消息队列及读消息队列。将一个新的消息写入队列,使用函数
msgsnd
,函数原型如下:
#include <sys/msg.h>
int msgsnd ( int msqid, const void *prt, size_t nbytes, int flags);
对于写入队列的每一个消息,都含有三个值,正长整型的类型字段、数据长度字段和实际数据字节。新的消息总是放在队列的尾部,函数中参数
msqid
指定要操作的队列,
ptr
指针指向一个
msgbuf
的结构,定义如下:
struct msgbuf{
long mtype;
char mbuf[];
};
这是一个模板的消息结构,其中成员
mbuf
是一个字符数组,长度是根据具体的消息来决定的,切忌消息不能以
NULL
结尾。成员
mtype
是消息的类型字段。
函数参数
nbytes
指定了消息的长度,参数
flags
指明函数的行为。函数成功返回
0
,失败返回
–1
并设置错误变量
errno
。
errno
可能出现的值有:
EAGAIN
、
EACCES
、
EFAULT
、
EIDRM
、
EINTR
、
EINVAL
和
ENOMEM
。当函数成功返回后会更新相应队列的
msqid_ds
结构。
使用函数
msgrcv
可以从队列中读取消息,函数原型如下:
#include <sys/msg.h>
ssize_t msgrcv ( int msqid, void *ptr, size_t nbytes, long type , int flag);
函数中参数
msqid
为指定要读的队列,参数
ptr
为要接收数据的缓冲区,
nbytes
为要接收数据的长度,当队列中满足条件的消息长度大于
nbytes
的值时,则会参照行为参数
flag
的值决定如何操作:当
flag
中设置了
MSG_NOERROR
位时,则将消息截短到
nbytes
指定的长度后返回。如没有
MSG_NOERROR
位,则函数出错返回,并设置错误变量
errno
。设置
type
参数指定
msgrcv
函数所要读取的消息,
tyre
的取值及相应操作如表
14-10
所示。
表
14-10 type
值详解
参数
flag
定义函数的行为,如设置了
IPC_NOWAIT
位,则当队列中无符合条件的消息时,函数出错返回,
errno
的值为
ENOMSG
。如没有设置
IPC_NOWAIT
位,则进程阻塞直到出现满足条件的消息出现为止,然后函数读取消息返回。
下面实例演示了消息队列在进程间的通信。程序中创建了一个消息的模板结构体,并对声明变量做初始化。使用
msgget
函数创建了一个消息队列,使用
msgsnd
函数向该队列中发送了一条消息。
(
1
)在
vi
编辑器中编辑该程序如下:
程序清单
14-14 snd_msg.c
调用
msgsnd
函数向队列中发送消息
#include <sys/msg.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <stdio.h>
#include <stdlib.h>
struct msg{ /*
声明消息结构体
*/
long msg_types; /*
消息类型成员
*/
char msg_buf[511]; /*
消息
*/
};
int main( void ) {
int qid;
int pid;
int len;
struct msg pmsg; /*
一个消息的结构体变量
*/
pmsg.msg_types = getpid(); /*
消息类型为当前进程的
ID*/
sprintf (pmsg.msg_buf,"hello!this is :%d/n/0", getpid() ); /*
初始化消息
*/
len = strlen ( pmsg.msg_buf ); /*
取得消息长度
*/
if ( (qid=msgget(IPC_PRIVATE, IPC_CREAT | 0666)) < 0 ) { /*
创建一个消
息队列
*/
perror ( "msgget" );
exit (1) ;
}
if ( (msgsnd(qid, &pmsg, len, 0 )) < 0 ){ /*
向消息队列中发送消息
*/
perror ( "msgsn" );
exit ( 1 );
}
printf ("successfully send a message to the queue: %d /n", qid);
exit ( 0 ) ;
}
(
2
)在
shell
中编译该程序如下:
$gcc snd_msg.c –o snd_msg
(
3
)在
shell
中运行该程序如下:
$./ snd_msg
successfully send a message to the queue 0
上述程序中,先定义了一个消息的结构体。该结构体中包含两个成员,
long
类型成员
msg_types
是消息的类型,注意,在消息队列中是以消息类型做索引值来进行检索的。
char
类型数组存放消息。在程序中先声明了一个消息的结构体变量,并做相应初始化,然后使用了
msgget
函数创建一个消息队列,并将该消息发送到此消息队列中。以下是一个使用消息队列发送消息的程序。
下面实例演示了如何使用队列读取消息。在程序的开始部分,判断用户是否输入了目标消息队列
ID
,如果没有,则打印命令的帮助信息;如果用户输入了队列的
ID
,则从队列中取出该消息,并输出到标准输出。
(
1
)在
vi
编辑器中编辑该程序。
程序清单
14-15 rcv_msg.c
使用
msgrcv
函数从指定队列中读出消息
#include <sys/msg.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <stdio.h>
#include <stdlib.h>
#define BUFSZ 4096
struct msg{ /*
声明消息结构体
*/
long msg_types; /*
消息类型成员
*/
char msg_buf[511]; /*
消息
*/
};
int main( int argc, char * argv[] ) {
int qid;
int len;
struct msg pmsg;
if ( argc != 2 ){ /**/
perror ( "USAGE: read_msg <queue ID>" );
exit ( 1 );
}
qid = atoi ( argv[1] ); /*
从命令行中获得消息队列的
ID*/
/*
从指定队列读取消息
*/
len = msgrcv ( qid, &pmsg, BUFSZ, 0, 0 );
if ( len > 0 ){
pmsg.msg_buf[len] = '/0'; /*
为消息添加结束符
*/
printf ("reading queue id :%05ld/n", qid ); /*
输出队列
ID*/
/*
该消息类型就是发送消息的进程
ID*/
printf ("message type : %05ld/n", pmsg.msg_types );
printf ("message length : %d bytes/n", len ); /*
消息长度
*/
printf ("mesage text: %s/n", pmsg.msg_buf); /*
消息内容
*/
}
else if ( len == 0 )
printf ("have no message from queue %d/n", qid );
else {
perror ( "msgrcv");
exit (1);
}
system("ipcs -q")
exit ( 0 ) ;
}
(
2
)在
shell
中编译该程序如下:
$gcc rcv_msg.c–o rcv _msg
(
3
)在
shell
中运行该程序如下:
$./ rcv_msg 0
reading queue id :0
message type : 03662
message length : 20 bytes
mesage text: hello!this is :3662
------ Message Queues --------
key msqid owner perms used-bytes messages
0x00000000 0 root 666 0 0
该程序中声明了一个消息的结构体类型变量,并从命令行中得到所要操作的消息队列,然后使用函数
msgrcv
从指定消息队列中读取队列中最上面的一条消息(函数的第
4
个参数等于
0
,说明根据先进先出规则,应从队列的最上面读取一条消息),并将该消息输出到标准输出。在发送消息的程序中,消息类型字段指定的是发送消息进程的
ID
,可以使用该内容来判断信息的来源。
另外,IBM开发中心的一篇文章也很好,http://www.ibm.com/developerworks/cn/linux/l-ipc/part3/
。消息队列的这种特点正适合我的需要。下面是来自CSDN博客上的一篇文章,转自http://blog.csdn.net/liranke/archive/2010/05/19/5608686.aspx
1.
基本概念
消息队列的最佳定义是:内核地址空间中的内部链表。消息可以顺序地发送到队列中,
并以几种不同的方式从队列中获取。当然,每个消息队列都是由
IPC
标识符所唯一标识的。
2.
内部和用户数据结构
要完成理解象系统
V IPC
这样复杂的问题,关键是要彻底熟悉内核的几个内部数据结构。
甚至对那些最基本的操作来说,直接访问这些结构中的某几个结构也是必要的,而其他的结
构则停留在一个更低的级别上。
3.
消息缓冲区
我们要介绍的第一个结构是
msgbuf
结构。这个特殊的数据结构可以认为是消息数据的模
板。虽然定义这种类型的数据结构是程序员的职责,但是读者绝对必须知道实际上存在
msgbuf
类型的结构。它是在在
linux/msg.h
中定义的,有
2
个成员:
• mtype
—
它是消息类型,以正数来表示。这个数必须为一个正数!
• mtext
—
它就是消息数据。
4.
内核
msg
结构
内核把消息队列中的每个消息都存放在
msg
结构的框架中。该结构是在
linux/msg.h
中定义
的,如下是其成员的描述:
• msg_next
—
这是一个指针,指向消息队列中的下一个消息。在内核寻址空间中,它们
是当作一个链表存储的。
• msg_type
—
这是消息类型,它的值是在用户结构
msgbuf
中赋予的。
• msg_spot
—
这是一个指针,指向消息体的开始处。
• msg_ts
—
这是消息文本
(
消息体
)
的长度。
•
内核
msgid_ds
结构
—
IPC
对象分为三类,每一类都有一个内部数据结构,该数据结构
是由内核维护的。对于消息队列而言,它的内部数据结构是
msqid_ds
结构。对于系统上
创建的每个消息队列,内核均为其创建、存储和维护该结构的一个实例。该结构在
linux/msg.h
中定义,如下所示:
struct msqid_ds{
struct ipc_perm msg_perm;
msgqnum_t msg_qnum;
msglen_t msg_qbytes;
pid_t msg_lspid;
pid_t msg_lrpid;
time_t msg_stime;
time_t msg_rtime;
time_t msg_ctime;
...
...
};
在不同的系统中,此结构会有不同的新成员,这里只列出最少拥有的关键成员。其中,
msg_qbytes
成员以及
msg_qnum
成员在不同的系统也会有不同的上限值,这里就不逐一介绍了,详细内容请参阅相关系统手册。
• msg_perm
—
它是
ipc_perm
结构的一个实例,
ipc_perm
结构是在
linux/ipc.h
中定义的。
该成员存放的是消息队列的许可权限信息,其中包括访问许可信息,以及队列的创建者
的有关信息
(
如
uid
等等
)
。
• msg_first
—
链接到队列中的第一个消息
(
列表头部
)
。
• msg_last
—
链接到队列中的最后一个消息
(
列表尾部
)
。
• msg_stime
—
发送到队列的最后一个消息的时间戳
(time_t)
。
• msg_rtime
—
从队列中获取的最后一个消息的时间戳。
• msg_ctime
—
对队列进行最后一次变动的时间戳。
5.
内核
ipc_perm
结构
内核把
IPC
对象的许可权限信息存放在
ipc_perm
类型的结构中。例如在前面描述的某个消
息队列的内部结构中,
msg_perm
成员就是
ipc_perm
类型的,它的定义是在文件
linux/ipc.h
中,
以上所有的成员都具有相当的自扩展性。对象的创建者以及所有者
(
它们可能会有不同
)
的
有关信息,以及对象的
IPC
关键字都是存放在该结构中的。八进制形式的访问模式也是存放在
这里的,它是以一种无符号短整型的形式存储的。最后,时间片使用序列编号存放在最后面,
每次通过系统调用关闭
IPC
对象
(
摧毁
)
时,这个值将被增加一,至多可以增加到能驻留在系统
中的
IPC
对象的最大数目。用户需要关心这个值吗?答案是
“
不
”
。
有关这个问题,在
Richard Stevens
所著的《
Unix Network Programming
》一书的第
125
页
中作了精辟的讨论。该书还介绍了
ipc_perm
结构的存在和行为在安全性方面的原因。
6.
创建消息队列:
(
1
)
msgget
简介:为了创建一个新的消息队列,或者访问一个现有的队列,可以使用系统调用
msgget ( )
。
msgget ( )
的第一个变元是关键字的值
(
在我们的例子中该值是调用
ftok ( )
的返回值
)
。这个
关键字的值将被拿来与内核中其他消息队列的现有关键字值相比较。比较之后,打开或者访
问操作依赖于
msgflg
变元的内容。
• IPC_CREAT
—
如果在内核中不存在该队列,则创建它。
• IPC_EXCL
—
当与
IPC_CREAT
一起使用时,如果队列早已存在则将出错。
如果只使用了
IPC_CREAT, msgget ( )
或者返回新创建消息队列的消息队列标识符,或者
会返回现有的具有同一个关键字值的队列的标识符。如果同时使用了
I P C _ E X C L
和
IPC_CREAT
,那么将可能会有两个结果。或者创建一个新的队列,或者如果该队列存在,则
调用将出错,并返回-
1
。
IPC_EXCL
本身是没有什么用处的,但在与
IPC_CREAT
组合使用时,
它可以用于保证没有一个现存的队列为了访问而被打开。
有个可选的八进制许可模式,它是与掩码进行
OR
操作以后得到的。这是因为从功能上讲,
每个
IPC
对象的访问许可权限与
Unix
文件系统的文件许可权限是相似的!
(
2
)
msgget
举例:
下面实例演示了使用
msgget
函数创建一个队列,函数中参数
falgs
指定为
IPC_CREAT|0666
,说明新建一个权限为
0666
的消息队列,其中组用户、当前用户以及其他用户拥有读写的权限。并在程序的最后使用
shell
命令
ipcs –q
来查看系统
IPC
的状态。
(
1
)在
vi
编辑器中编辑该程序如下:
程序清单
14-12 create_msg.c msgget
函数
#include <sys/msg.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <stdio.h>
#include <stdlib.h>
int main ( void )
{
int qid;
key_t key;
key = 113;
qid=msgget( key, IPC_CREAT | 0666 )
;
/*
创建一个消息队列
*/
if ( qid < 0 ) { /*
创建一个消息队列失败
*/
perror ( "msgget" );
exit (1) ;
}
printf ("created queue id : %d /n", qid ); /*
输出消息队列的
ID */
system( "ipcs -q" ); /*
查看系统
IPC
的状态
*/
exit ( 0 );
}
(
2
)在
shell
中编译该程序如下:
$gcc create_msg.c–o create_msg
(
3
)在
shell
中运行该程序如下:
$./ create_msg
created queue id : 0
------ Message Queues --------
key msqid owner perms used-bytes messages
0x0000af40 623430 root 666 0 0
0x0000007b 0 root 666 0 0
在程序中使用了系统命令
ipcs
,命令参数
-q
说明只查看消息队列的状态。注意在输出消息中,
key
段标明的是
IPC
的
key
值,
msqid
为该队列的
ID
值,
perms
为执行权限。同样,队列的执行权限像其他
IPC
对象一样没有执行权限。函数
msgctl
可以在队列上做多种操作,函数原型如下:
#include <sys/msg.h>
int msgctl( int msqid, int cmd , struct msqid_ds *buf );
参数
msqid
为指定的要操作的队列,
cmd
参数指定所要进行的操作,其中有些操作需要
buf
参数。
cmd
参数的详细取值及操作如表
14-9
所示。
表
14-9 cmd
参数详解
cmd | 操 作 |
IPC_STAT | 取队列的 msqid_ds 结构,将它存放在 buf 所指向的结构中(需要 buf 参数) |
IPC_SET | 使用 buf 所指向结构中的值对当前队列的相关结构成员赋值,其中包括: msg_perm.uid 、 msg_perm.gid 、 msg_perm.mode 以及 msg_perm.cuid 。该命令只能由具有以下条件的进程执行:进程有效用户 ID 等于 msg_perm.cuid 或 msg_perm.uid 超级用户进程。其中只有超级用户才可以增加队列的 msg_qbytes 的值 |
IPC_RMID | 删除队列,并清除队列中的所有消息。此操作会影响后续进程对这个队列的相关操作。该命令只能由具有以下条件的进程执行。进程有效用户 ID 等于 msg_perm.cuid 或 msg_perm.uid ,超级用户进程 |
msgctl
函数操作队列,程序中先读取命令行参数,如没有,则打印命令提示信息,在调用
msgctl
函数执行删除操作的前后分别调用了一次
shell
命令
ipcs –q
来查看系统
IPC
的状态。
(
1
)在
vi
编辑器中编辑该程序如下:
程序清单
14-13 del_msg.c
调用
msgctl
删除指定队列
#include <sys/msg.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <stdio.h>
#include <stdlib.h>
int main ( int argc ,char *argv[] )
{
int qid ;
if ( argc != 2 ){ /*
命令行参数出错
*/
puts ( "USAGE: del_msgq.c <queue ID >" );
exit ( 1 );
}
qid = atoi ( argv[1] ); /*
通过命令行参数得到组
ID */
system( "ipcs -q");
if ( ( msgctl( qid, IPC_RMID, NULL ) ) < 0 ){ /*
删除指定的消息队列
*/
perror ("msgctl");
exit (1 );
}
system( "ipcs -q");
printf ( "successfully removed %d queue/n", qid ); /*
删除队列成功
*/
exit( 0 );
}
(
2
)在
shell
中编译该程序如下:
$gcc del_msg.c–o del_msg
(
3
)在
shell
中运行该程序如下:
$./ del_msg
------ Message Queues --------
key msqid owner perms used-bytes messages
0x0000007b 0 root 666 0 0
------ Message Queues --------
key msqid owner perms used-bytes messages
successfully removed 0 queue
7.
读写消息队列
一旦获得了队列标识符,用户就可以开始在该消息队列上执行相关操作了。为了向队列
传递消息,用户可以使用
msgsnd
系统调用.
由于消息队列的特殊性,系统为这个数据类型提供了两个接口(
msgsnd
函数,
msgrcv
函数),分别对应写消息队列及读消息队列。将一个新的消息写入队列,使用函数
msgsnd
,函数原型如下:
#include <sys/msg.h>
int msgsnd ( int msqid, const void *prt, size_t nbytes, int flags);
对于写入队列的每一个消息,都含有三个值,正长整型的类型字段、数据长度字段和实际数据字节。新的消息总是放在队列的尾部,函数中参数
msqid
指定要操作的队列,
ptr
指针指向一个
msgbuf
的结构,定义如下:
struct msgbuf{
long mtype;
char mbuf[];
};
这是一个模板的消息结构,其中成员
mbuf
是一个字符数组,长度是根据具体的消息来决定的,切忌消息不能以
NULL
结尾。成员
mtype
是消息的类型字段。
函数参数
nbytes
指定了消息的长度,参数
flags
指明函数的行为。函数成功返回
0
,失败返回
–1
并设置错误变量
errno
。
errno
可能出现的值有:
EAGAIN
、
EACCES
、
EFAULT
、
EIDRM
、
EINTR
、
EINVAL
和
ENOMEM
。当函数成功返回后会更新相应队列的
msqid_ds
结构。
使用函数
msgrcv
可以从队列中读取消息,函数原型如下:
#include <sys/msg.h>
ssize_t msgrcv ( int msqid, void *ptr, size_t nbytes, long type , int flag);
函数中参数
msqid
为指定要读的队列,参数
ptr
为要接收数据的缓冲区,
nbytes
为要接收数据的长度,当队列中满足条件的消息长度大于
nbytes
的值时,则会参照行为参数
flag
的值决定如何操作:当
flag
中设置了
MSG_NOERROR
位时,则将消息截短到
nbytes
指定的长度后返回。如没有
MSG_NOERROR
位,则函数出错返回,并设置错误变量
errno
。设置
type
参数指定
msgrcv
函数所要读取的消息,
tyre
的取值及相应操作如表
14-10
所示。
表
14-10 type
值详解
type | 操 作 |
等于 0 | 返回队列最上面的消息(根据先进先出规则) |
大于 0 | 返回消息类型与 type 相等的第 1 条消息 |
小于 0 | 返回消息类型小于等于 type 绝对值的最小值的第 1 条消息 |
flag
定义函数的行为,如设置了
IPC_NOWAIT
位,则当队列中无符合条件的消息时,函数出错返回,
errno
的值为
ENOMSG
。如没有设置
IPC_NOWAIT
位,则进程阻塞直到出现满足条件的消息出现为止,然后函数读取消息返回。
下面实例演示了消息队列在进程间的通信。程序中创建了一个消息的模板结构体,并对声明变量做初始化。使用
msgget
函数创建了一个消息队列,使用
msgsnd
函数向该队列中发送了一条消息。
(
1
)在
vi
编辑器中编辑该程序如下:
程序清单
14-14 snd_msg.c
调用
msgsnd
函数向队列中发送消息
#include <sys/msg.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <stdio.h>
#include <stdlib.h>
struct msg{ /*
声明消息结构体
*/
long msg_types; /*
消息类型成员
*/
char msg_buf[511]; /*
消息
*/
};
int main( void ) {
int qid;
int pid;
int len;
struct msg pmsg; /*
一个消息的结构体变量
*/
pmsg.msg_types = getpid(); /*
消息类型为当前进程的
ID*/
sprintf (pmsg.msg_buf,"hello!this is :%d/n/0", getpid() ); /*
初始化消息
*/
len = strlen ( pmsg.msg_buf ); /*
取得消息长度
*/
if ( (qid=msgget(IPC_PRIVATE, IPC_CREAT | 0666)) < 0 ) { /*
创建一个消
息队列
*/
perror ( "msgget" );
exit (1) ;
}
if ( (msgsnd(qid, &pmsg, len, 0 )) < 0 ){ /*
向消息队列中发送消息
*/
perror ( "msgsn" );
exit ( 1 );
}
printf ("successfully send a message to the queue: %d /n", qid);
exit ( 0 ) ;
}
(
2
)在
shell
中编译该程序如下:
$gcc snd_msg.c –o snd_msg
(
3
)在
shell
中运行该程序如下:
$./ snd_msg
successfully send a message to the queue 0
上述程序中,先定义了一个消息的结构体。该结构体中包含两个成员,
long
类型成员
msg_types
是消息的类型,注意,在消息队列中是以消息类型做索引值来进行检索的。
char
类型数组存放消息。在程序中先声明了一个消息的结构体变量,并做相应初始化,然后使用了
msgget
函数创建一个消息队列,并将该消息发送到此消息队列中。以下是一个使用消息队列发送消息的程序。
下面实例演示了如何使用队列读取消息。在程序的开始部分,判断用户是否输入了目标消息队列
ID
,如果没有,则打印命令的帮助信息;如果用户输入了队列的
ID
,则从队列中取出该消息,并输出到标准输出。
(
1
)在
vi
编辑器中编辑该程序。
程序清单
14-15 rcv_msg.c
使用
msgrcv
函数从指定队列中读出消息
#include <sys/msg.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <stdio.h>
#include <stdlib.h>
#define BUFSZ 4096
struct msg{ /*
声明消息结构体
*/
long msg_types; /*
消息类型成员
*/
char msg_buf[511]; /*
消息
*/
};
int main( int argc, char * argv[] ) {
int qid;
int len;
struct msg pmsg;
if ( argc != 2 ){ /**/
perror ( "USAGE: read_msg <queue ID>" );
exit ( 1 );
}
qid = atoi ( argv[1] ); /*
从命令行中获得消息队列的
ID*/
/*
从指定队列读取消息
*/
len = msgrcv ( qid, &pmsg, BUFSZ, 0, 0 );
if ( len > 0 ){
pmsg.msg_buf[len] = '/0'; /*
为消息添加结束符
*/
printf ("reading queue id :%05ld/n", qid ); /*
输出队列
ID*/
/*
该消息类型就是发送消息的进程
ID*/
printf ("message type : %05ld/n", pmsg.msg_types );
printf ("message length : %d bytes/n", len ); /*
消息长度
*/
printf ("mesage text: %s/n", pmsg.msg_buf); /*
消息内容
*/
}
else if ( len == 0 )
printf ("have no message from queue %d/n", qid );
else {
perror ( "msgrcv");
exit (1);
}
system("ipcs -q")
exit ( 0 ) ;
}
(
2
)在
shell
中编译该程序如下:
$gcc rcv_msg.c–o rcv _msg
(
3
)在
shell
中运行该程序如下:
$./ rcv_msg 0
reading queue id :0
message type : 03662
message length : 20 bytes
mesage text: hello!this is :3662
------ Message Queues --------
key msqid owner perms used-bytes messages
0x00000000 0 root 666 0 0
该程序中声明了一个消息的结构体类型变量,并从命令行中得到所要操作的消息队列,然后使用函数
msgrcv
从指定消息队列中读取队列中最上面的一条消息(函数的第
4
个参数等于
0
,说明根据先进先出规则,应从队列的最上面读取一条消息),并将该消息输出到标准输出。在发送消息的程序中,消息类型字段指定的是发送消息进程的
ID
,可以使用该内容来判断信息的来源。
另外,IBM开发中心的一篇文章也很好,http://www.ibm.com/developerworks/cn/linux/l-ipc/part3/
相关文章推荐
- Linux 进程间通信--- 消息队列
- Linux 进程间通信(posix消息队列 简单)实例
- UNIX/Linux进程间通信IPC系列(四)消息队列
- linux进程间通信(posix消息队列)实例
- PHP下操作Linux消息队列完成进程间通信的方法
- linux进程间通信之消息队列
- Linux进程间通信-消息队列
- Linux进程间通信——使用消息队列
- Linux进程间通信之消息队列
- linux C-(进程间通信 消息队列)
- Linux进程间通信——消息队列
- Linux进程间通信&mdash;&mdash;消息队列
- Linux进程间通信之信号量(semaphore)、消息队列(Message Queue)和共享内存(Share Memory)
- linux进程间通信-------消息队列
- Linux进程间通信——使用消息队列
- Linux进程间通信--进程,信号,管道,消息队列,信号量,共享内存
- Unix/Linux进程间通信——(System V)消息队列
- Linux进程间通信——消息队列
- Linux环境进程间通信(三)消息队列
- 【Linux】Linux进程间通信——使用消息队列