- 论坛徽章:
- 0
|
欢迎转载,请保留作者信息bill@华中科技大学
[/url]
[url=http://billstone.cublog.cn/]http://billstone.cublog.cn
十二 消息队列
消息队列是UNIX内核中的一个先进先出的链表结构. 相对于管道, 消息队列有明显的优势, 原因在于:
(1) 消息队列是一种先进先出的队列型数据结构, 可以保证先送的货物先到达, 后送的货物后到达, 避免了插队现象.
(2) 信息队列将输出的信息进行了打包处理, 这样就可以保证以每个消息为单位进行接收了.
(3) 消息队列还可以对信息进行分类处理, 标记各种类别的信息, 这样就可以根据信息类别分别出列.
IPC就是进程间通信, 侠义上讲, IPC指消息队列, 信号量和共享内存三种对象. 通过shell命令ipcs可以查询当前系统的IPC对象信息:
[bill@billstone Unix_study]$ ipcs
------ Shared Memory Segments --------
key shmid owner perms bytes nattch status
0x00000000 196609 bill 777 393216 2 dest
0x00000000 491522 root 644 106496 2 dest
0x00000000 524291 root 644 110592 2 dest
0x00000000 557060 root 644 110592 2 dest
0x00000000 589829 root 644 110592 2 dest
------ Semaphore Arrays --------
key semid owner perms nsems
------ Message Queues --------
key msqid owner perms used-bytes messages
[bill@billstone Unix_study]$
消息队列简介
UNIX内核使用结构msqid_ds来管理消息队列:
struct msqid_ds {
struct ipc_perm msg_perm;
struct msg
*msg_first; /* first message
on queue, unused */
struct msg
*msg_last; /* last message in
queue, unused */
__kernel_time_t
msg_stime; /* last msgsnd time */
__kernel_time_t
msg_rtime; /* last msgrcv time */
__kernel_time_t
msg_ctime; /* last change time */
unsigned long msg_lcbytes; /* Reuse junk fields for 32 bit */
unsigned long msg_lqbytes; /* ditto */
unsigned short
msg_cbytes; /* current number of
bytes on queue */
unsigned short msg_qnum; /* number of messages in queue */
unsigned short
msg_qbytes; /* max number of bytes
on queue */
__kernel_ipc_pid_t
msg_lspid; /* pid of last msgsnd */
__kernel_ipc_pid_t
msg_lrpid; /* last receive pid */
};
其中msg结构的定义如下, 我们在实际项目中几乎很少使用如下的结构, 一般都需要我们根据实际的需求自行定义.
Struct msg{
struct msg* msg_next;
long msg_type;
long msg_ts;
short msg_spot;
};
理论上可以通过结构msqid_ds的成员msg_first, msg_last和结构msg的成员msg_next遍历全部消息队列并完成管理和维护消息队列的功能, 但实际上这三个成员是UNIX内核的直辖数据, 用户无法引用.
消息队列中消息本身是由消息类型和消息数据组成, 我们常常使用如下结构作为消息模板:
struct msgbuf {
long mtype; /* type of message */
char mtext[1]; /* message text */
};
根据消息类型的不同, 我们可以在同一个信息队列中定义不同功能的消息.
使用消息队列
(1) 消息队列的创建
在UNIX中, 采用函数msgget创建消息队列
#include
#include
#include
extern int msgget (key_t __key, int __msgflg)
__THROW;
函数msgget创建一个新的消息队列, 或者访问一个已经存在的消息队列. 调用成功时返回消息队列的标志符, 否则返回-1.
(2) 消息队列的发送和接收
在UNIX中函数msgsnd向消息队列发送消息, 原型如下:
#include
#include
#include
extern int msgsnd (int __msqid, __const void
*__msgp, size_t __msgsz, int __msgflg) __THROW;
发送消息一般分五个步骤:
a) 根据自己的需要定义消息结构
struct msgbuf {
long mtype; /* type of message */
char ctext[100]; /* message data */
};
b) 打开或创建消息队列
msgid = msgget(Key, 0666 | IPC_CREAT);
if(msgid 打开(或创建)队列失败
c) 组装消息
d) 发送消息
int ret;
ret = msgsnd(msgid, (void *)&buf,
strlen(buf.ctext), IPC_NOWAIT);
e) 发送判断
If (ret == -1){
if (errno == EINTR) 信号中断, 重新发送;
else 系统错误
}
下面是一个发送消息的实例: 它循环读取键盘输入, 将输入的字符串信息写入到消息队列(关键字为0x1234)中.
[bill@billstone Unix_study]$ cat msg1.c
#include
#include
#include
#include
#include
extern int errno;
struct mymsgbuf{
long mtype;
char ctext[100];
};
int main(void)
{
struct mymsgbuf buf;
int msgid;
if((msgid =
msgget(0x1234, 0666 | IPC_CREAT))
fprintf(stderr,
"open msg %X failed\n", 0x1234);
exit(1);
}
while(1){
printf("Please input: ");
memset(&buf,
0, sizeof(buf));
fgets(buf.ctext,
sizeof(buf.ctext), stdin);
buf.mtype =
getpid();
while((msgsnd(msgid, (void *)&buf,
strlen(buf.ctext), IPC_NOWAIT))
if(errno
== EINTR)
continue;
exit(2);
}
if(!strncmp(buf.ctext, "exit", 4))
break;
}
return 0;
}
[bill@billstone Unix_study]$
运行结果如下:
[bill@billstone Unix_study]$ make msg1
cc msg1.c -o msg1
[bill@billstone Unix_study]$ ipcs -q
------ Message Queues --------
key msqid owner perms used-bytes messages
[bill@billstone Unix_study]$ ./msg1
Please input: Hello world!
Please input: Nice to meet you!
Please input: bye!
Please input: exit
[bill@billstone Unix_study]$ ipcs -q
------ Message Queues --------
key msqid owner perms used-bytes messages
0x00001234 98304 bill 666 41 4
[bill@billstone Unix_study]$
在UNIX中函数msgrcv从消息队列中接收消息, 原型如下:
#include
#include
#include
extern int msgrcv (int __msqid, void *__msgp, size_t
__msgsz, long int __msgtyp, int __msgflg) __THROW;
与函数msgsnd不同,这里参数msgsz指的是缓冲区的最大容量,包括消息类型占用的部分.
这里配合上面的例子设计接收消息的实例: 以阻塞方式不断地从消息队列(关键字为0x1234)中读取消息, 并打印接收到的消息类型, 长度和数据等信息, 当接收到数据内容为'exit'时程序结束.
[bill@billstone Unix_study]$ cat msg2.c
#include
#include
#include
#include
#include
extern int errno;
struct mymsgbuf{
long mtype;
char ctext[100];
};
int main(void)
{
struct mymsgbuf buf;
int msgid, ret;
if((msgid =
msgget(0x1234, 0666 | IPC_CREAT))
fprintf(stderr,
"open msg %X failed\n", 0x1234);
exit(1);
}
while(1){
memset(&buf,
0, sizeof(buf));
while((ret =
msgrcv(msgid, (void *)&buf, sizeof(buf), 0, 0))
if(errno
== EINTR)
continue;
fprintf(stderr, "Error no: %d", errno);
exit(2);
}
fprintf(stderr,
"Msg: Type=%d, Len=%d, Text:%s", buf.mtype, ret, buf.ctext);
if(!strncmp(buf.ctext, "exit", 4))
break;
}
return 0;
}
[bill@billstone Unix_study]$
运行结果如下:
[bill@billstone Unix_study]$ make msg2
cc msg2.c -o msg2
[bill@billstone Unix_study]$ ipcs -q
------ Message Queues --------
key msqid owner perms used-bytes messages
0x00001234 98304 bill
666 41 4
[bill@billstone Unix_study]$ ./msg2
Msg: Type=15666, Len=13, Text:Hello world!
Msg: Type=15666, Len=18, Text:Nice to meet you!
Msg: Type=15666, Len=5, Text:bye!
Msg: Type=15666, Len=5, Text:exit
[bill@billstone Unix_study]$ ipcs -q
------ Message Queues --------
key msqid owner perms used-bytes messages
0x00001234 98304 bill 666 0 0
[bill@billstone Unix_study]$
从上面可以看到, 采用消息队列通信比采用管道通信具有更多的灵活性.
系统调用msgctl对消息队列进行各种控制, 包括查询消息队列数据结构, 改变消息队列访问权限, 改变消息队列属主信息和删除消息队列等, 原型如下:
#include
#include
#include
int msgctl(int msqid, int cmd, struct msqid_ds
*buf);
根据cmd参数对msqid消息队列操作:
a) IPC_RMID:
删除消息队列
b) IPC_STAT:
读取消息队列
c) IPC_SET: 重置消息队列结构msqid_ds中的成员uid, gid, mode及msg_qbytes.
本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u1/47687/showart_1907973.html |
|