消息队列是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. |
消息队列
最新推荐文章于 2023-08-07 15:55:07 发布