1. 概念
- 消息队列是由消息组成的链表,存放于内存中,由内核维护。
- 每个消息可定义为一个结构体,存放消息类型、消息正文等数据。
- 只有内核重启或用户删除才会删除消息队列。
- 每个消息队列的标识符(ID)都是唯一的,类似共享内存的ID。
定义消息结构体:
typedef struct msgbuf { long mtype; //消息类型,必须放在第一个 char mtext[128]; //消息正文,可有多个 //..... } MSG;
消息队列相关的Linux命令:
- 查看消息队列:ipcs -q
- 删除指定的消息队列:ipcrm -q 队列号
其它选项:①-m:共享内存;②-q:消息队列;③-s:信号量;④-a:全部。
2. 创建消息队列
调用msgget函数:
【1】头文件:#include <sys/types.h>、#include <sys/ipc.h>、#include <sys/msg.h>
【2】函数原型:int msgget(key_t key, int msgflg);
【3】参数说明:
- key:是IPC键值,可以通过ftok函数来创建(与共享内存类似)
- msgflg:创建消息队列指定的属性。
- IPC_CREAT:创建消息队列,需要或上消息队列的权限
- IPC_EXCL:检测消息队列是否存在,若存在函数返回-1
【4】返回值:成功返回消息队列ID,失败返回-1.
程序实例:创建消息队列并查看其信息。
#include <stdio.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> int main(int argc, char **argv) { //创建key key_t key = ftok("./", 222); printf("key = %#x\n", key); //创建消息队列 int id = msgget(key, IPC_CREAT | 0664); if (id < 0) { perror("msgget error"); return -1; } printf("msgQueue ID: %d\n", id); return 0; }
3. 添加新消息到消息队列
调用msgsnd函数:
【1】头文件:#include <sys/types.h>、#include <sys/ipc.h>、#include <sys/msg.h>
【2】函数原型:int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
【3】参数说明:
- msqid:消息队列的id
- msgp:待发送消息结构体地址
- msgsz:消息正文字数(结构体大小减去结构体中成员大小)
- msgflg:函数的控制属性
- 0:msgsnd调用阻塞直到满足条件为止
- IPC_NOWAIT:若消息没有立即发送,调用该函数的进程会立即返回
【4】返回值:成功返回0,失败返回-1.
程序实例:创建消息队列,并添加一个新消息,其中新消息正文为"Hello, Can!"
#include <stdio.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> #include <string.h> typedef struct msgbuf { long mtype; char mtext[128]; } MSG; int main(int argc, char **argv) { //创建key key_t key = ftok("./", 222); printf("key = %#x\n", key); //创建消息队列 int id = msgget(key, IPC_CREAT | 0664); if (id < 0) { perror("msgget error"); return -1; } printf("msgQueue ID: %d\n", id); //添加新消息到消息队列 MSG msg; memset(&msg, 0, sizeof(msg)); msg.mtype = 20; strcpy(msg.mtext, "Hello, Can!"); msgsnd(id, &msg, sizeof(msg) - sizeof(long), 0); return 0; }
4. 获取消息队列中的消息
调用msgrcv函数:从指定ID的消息队列中接收信息,一旦接受成功,从消息队列中删除该信息。
【1】头文件:#include <sys/types.h>、#include <sys/ipc.h>、#include <sys/msg.h>
【2】函数原型:ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
【3】参数说明:
- msqid:消息队列的id
- msgp:保存消息信息的结构体地址
- msgsz:消息正文字节数
- msgtyp:
- 0:接收消息队列中的第一个消息。
- >0:接收等于msgtyp的信息。
- <0:返回队列中消息类型值小于或者等于msgtyp的绝对值的值,若有多个则取最小值。
- msgflg:函数的控制属性
- 0:阻塞直到满足条件为止
- MSG NOERROR:若返回的消息字节数比nbytes字节数多,则消息就会截短到nbytes字节,且不通知消息发送进程
- IPC_NOWAIT:若消息没有立即发送,调用该函数的进程会立即返回
【4】返回值:成功返回读取的字节数,失败返回-1.
5. 删除消息队列
调用msgctl函数:
【1】头文件:#include <sys/types.h>、#include <sys/ipc.h>、#include <sys/msg.h>
【2】函数原型:int msgctl(int msqid, int cmd, struct msqid_ds *buf);
【3】参数说明:
- msqid:消息队列的id
- cmd:控制命令参数
- IPC_RMID:删除该消息队列标识符的队列,从系统删除并破坏相关结构。
- IPC_STAT:将该消息队列标识符表示的的消息队列各个元素值存到buf结构体中。
- IPC_SET:将buf结构体中的数据设置到消息队列中
- buf:msqid_ds数据类型地址,用来存放或者更改消息队列属性。
【4】返回值:成功返回0,失败返回-1.
【5】删除示例:msgctl(id, IPC_RMID, NULL);
综合实例:写两个程序,一个程序作为发送消息方,一个程序作为接收消息方。发送消息方往消息队列中添加消息,接收消息方从消息队列中获取对应的消息。
发送消息方:
#include <stdio.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> #include <string.h> typedef struct msgbuf { long mtype; char mtext[128]; } MSG; int main(int argc, char **argv) { //创建key key_t key = ftok("./", 222); printf("发送方: key = %#x\n", key); //创建消息队列 int id = msgget(key, IPC_CREAT | 0664); if (id < 0) { perror("msgget error"); return -1; } printf("发送方: msgQueue ID: %d\n", id); //添加新消息到消息队列 MSG msg; memset(&msg, 0, sizeof(msg)); msg.mtype = 20; strcpy(msg.mtext, "Hello, Can!"); msgsnd(id, &msg, sizeof(msg) - sizeof(long), 0); printf("发送方: 消息内容 = %s\n", msg.mtext); return 0; }
接收消息方:
#include <stdio.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> #include <string.h> typedef struct msgbuf { long mtype; char mtext[128]; } MSG; int main(int argc, char **argv) { //创建key key_t key = ftok("./", 222); printf("接收方: key = %#x\n", key); //创建消息队列 int id = msgget(key, IPC_CREAT | 0664); if (id < 0) { perror("msgget error"); return -1; } printf("接收方: msgQueue ID: %d\n", id); //从消息队列中获取消息 MSG msg; msgrcv(id, &msg, sizeof(msg) - sizeof(long), 20, 0); printf("接收方: 消息内容 = %s\n", msg.mtext); return 0; }