十二
消息队列
消息队列是
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
<sys/types.h>
#include
<sys/ipc.h>
#include
<sys/msg.h>
extern int
msgget (key_t __key, int __msgflg)
__THROW;
函数
msgget
创建一个新的消息队列
,
或者访问一个已经存在的消息队列
.
调用成功时返回消息队列的标志符
,
否则返回
-1.
(2)
消息队列的发送和接收
在
UNIX
中函数
msgsnd
向消息队列发送消息
,
原型如下
:
#include
<sys/types.h>
#include
<sys/ipc.h>
#include
<sys/msg.h>
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
< 0)
打开
(
或创建
)
队列失败
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
<sys/msg.h>
#include
<sys/types.h>
#include
<sys/ipc.h>
#include
<stdio.h>
#include
<sys/errno.h>
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)) < 0){
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)) < 0){
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
<sys/types.h>
#include
<sys/ipc.h>
#include
<sys/msg.h>
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
<sys/msg.h>
#include
<sys/types.h>
#include
<sys/ipc.h>
#include
<stdio.h>
#include
<sys/errno.h>
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)) < 0){
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)) < 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
<sys/types.h>
#include
<sys/ipc.h>
#include
<sys/msg.h>
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.
学习笔记08-学习《精通UNIX下C语言编程及项目实践》
最新推荐文章于 2021-05-22 07:57:41 发布