1. 基本特点
1) 消息队列是一个由系统内核负责存储和管理,并通过消息队列标识引用的数据链表,消息队列 和有名管道fifo的区别在: 后者一次只能放一个包,而前者则可以放很多包,这样就能处理发包快,哪包慢的问题
2) 可以通过msgget函数创建一个新的消息队列, 或获取一个已有的消息队列。 通过msgsnd函数 (send)向消息队列的后端追加消息, 通过msgrcv(receive)函数从消息队列的前端提取消息。
3) 消息队列中的每个消息单元除包含消息数据外,还包含消息类型和数据长度。消息类型的存在意义:同一个消息队列处理不同类型的消息,比如现在处理存款和取款两种情况,我们就可以定义两种类型的数据而不是定义两条消息队列
4) 内核为每个消息队列,维护一个msqid_ds结构体形式的消息队列对象。
truct msqid_ds { struct ipc_perm msg_perm; // 权限信息 time_t msg_stime; // 随后发送时间 time_t msg_rtime; // 最后接收时间 time_t msg_ctime; // 最后改变时间 unsigned long __msg_cbytes; // 消息队列中的字节数 msgqnum_t msg_qnum; // 消息队列中的消息数 msglen_t msg_qbytes; // 消息队列能容纳的最大字节数 pid_t msg_lspid; // 最后发送进程PID pid_t msg_lrpid; // 最后接收进程PID };
struct ipc_perm { key_t __key; // 键值 uid_t uid; // 有效属主ID gid_t gid; // 有效属组ID uid_t cuid; // 有效创建者ID gid_t cgid; // 有效创建组ID unsigned short mode; // 权限字 unsigned short __seq; // 序列号 };
2. 常用函数
头文件 : #include <sys/msg.h>
1) 创建/获取消息队列
int msgget (key_t key, int msgflg);
A. 该函数以key参数为键值创建消息队列,或获取已有的消息队列。
B. msgflg取值:
0 - 获取,不存在即失败。
IPC_CREAT - 创建,不存在即创建,已存在即获取,除非...
IPC_EXCL - 排斥,已存在即失败。
C. 成功返回消息队列标识msqid,失败返回-1。
2) 向消息队列发送消息
int msgsnd (int msqid, const void* msgp,size_t msgsz, int msgflg);
A. msgp参数指向一个包含消息类型和消息数据的内存块。该内存块的前4个字节必须是一个大于0的整数,代表消息类型,其后紧跟消息数据。消息数据的字节长度用msgsz参数表示。
注意:msgsz参数并不包含消息类型的字节数(4)。
B. 若内核中的消息队列缓冲区有足够的空闲空间(由内核参数设定),则此函数会将消息拷入该缓冲区并立即返回0,表示发送成功,否则此函数会阻塞,直到内核中的消息队列缓冲区有足够的空闲空间为止
C. 若msgflg参数包含IPC_NOWAIT位, 则当内核中的消息队列缓冲区没有足够的空闲空间时, 此函数不会阻塞,而是返回-1,errno为EAGAIN。
D. 成功返回0,失败返回-1。
3) 从消息队列接收消息
ssize_t msgrcv (int msqid, void* msgp, size_t msgsz,long msgtyp, int msgflg);
A. msgp参数指向一个包含消息类型(4字节),和消息数据的内存块.
B. 若所接收到的消息数据字节数大于msgsz参数,即消息太长,且msgflg参数包含MSG_NOERROR位,则该消息被截取msgsz字节返回,剩余部分被丢弃。
C. 若msgflg参数不包含MSG_NOERROR位,消息又太长,则不对该消息做任何处理,直接返回-1。
D. msgtyp参数表示期望接收哪类消息:10和20(注意:msgtype参数并不包含消息类型的字节数(4)。)
=0 - 返回消息队列中的第一条消息。
>0 - 若msgflg参数不包含MSG_EXCEPT位,则返回消息队列中第一个类型为msgtyp的消息;若msgflg参数包含MSG_EXCEPT位,则返回消息队列中第一个类型不为msgtyp的消息。
<0 - 返回消息队列中类型小于等于msgtyp的绝对值的消息。若有多个,则取类型最小者。
E. 若消息队列中有可接收消息,则此函数会将该消息移出消息队列并立即返回0, 表示接收成功,否则此函数会阻塞,直到消息队列中有可接收消息为止。
F. 若msgflg参数包含IPC_NOWAIT位,则当消息队列中没有可接收消息时,此函数不会阻塞, 而是返回-1,errno为ENOMSG。
G. 成功返回所接收到的消息数据的字节数,失败返回-1。
4) 销毁/控制消息队列
int msgctl (int msqid, int cmd, struct msqid_ds* buf);
A. cmd取值:
IPC_STAT - 获取消息队列的属性,通过buf参数输出。
IPC_RMID - 立即删除消息队列。此时所有阻塞在对该消息队列的,msgsnd和msgrcv函数调用,都会立即返回失败errno为EIDRM。
IPC_SET - 设置消息队列的属性,通过buf参数输入,仅有四个属性可设置
B. 成功返回0,失败返回-1。
3. 编程模型
#include <stdio.h> #include <string.h> #include <sys/msg.h> int main (void) { printf ("创建消息队列...\n"); key_t key = ftok (".", 100); if (key == -1) { perror ("ftok"); return -1; } int msqid = msgget (key, 0644 | IPC_CREAT | IPC_EXCL); if (msqid == -1) { perror ("msqget"); return -1; } printf ("向消息队列(0x%08x/%d)发送数据...\n", key, msqid); for (;;) { printf ("> "); struct { long mtype; char mtext[1024]; } msgbuf = {1234, ""}; gets (msgbuf.mtext); if (! strcmp (msgbuf.mtext, "!")) break; if (msgsnd (msqid, &msgbuf, (strlen (msgbuf.mtext) + 1) * sizeof (msgbuf.mtext[0]), 0) == -1) { perror ("msgsnd"); return -1; } } printf ("销毁消息队列(0x%08x/%d)...\n", key, msqid); if (msgctl (msqid, IPC_RMID, NULL) == -1) { perror ("msgctl"); return -1; } printf ("大功告成!\n"); return 0; }
#include <stdio.h> #include <unistd.h> #include <errno.h> #include <sys/msg.h> int main (void) { printf ("获取消息队列...\n"); key_t key = ftok (".", 100); if (key == -1) { perror ("ftok"); return -1; } int msqid = msgget (key, 0); if (msqid == -1) { perror ("msgget"); return -1; } printf ("从消息队列(0x%08x/%d)接收消息...\n", key, msqid); for (;;) { struct { long mtype; char mtext[1024]; } msgbuf = {}; ssize_t msgsz = msgrcv (msqid, &msgbuf, sizeof (msgbuf.mtext) - sizeof (msgbuf.mtext[0]), 1234, MSG_NOERROR/* | IPC_NOWAIT*/); if (msgsz == -1) if (errno == EIDRM) { printf ("消息队列(0x%08x/%d)已销毁!\n", key, msqid); break; } else if (errno == ENOMSG) { printf ("现在没有消息,干点儿别的...\n"); sleep (1); } else { perror ("msgrcv"); return -1; } else printf ("%04d< %s\n", msgsz, msgbuf.mtext); } printf ("大功告成!\n"); return 0; }
命令: ipcs -q 查看消息队列的信息
版权声明:本文为博主原创文章,未经博主允许不得转载。