二、消息队列
1.什是消息队列
在 Linux 中,进程间通信(IPC)的消息队列是一种在进程之间传递数据的机制。它允许不同的进程以异步的方式发送和接收消息。
2.消息队列的特点
- 消息队列可以实现多个进程之间的通信,一个进程可以向消息队列发送消息,而另一个进程可以从消息队列中接收消息。
- 消息队列中的消息是有类型的,可以根据消息的类型进行选择性接收。
- 消息队列具有一定的缓存能力,可以在发送方和接收方处理速度不匹配时起到缓冲作用。
3.使用消息队列的步骤
(1)包含头文件
代码示例
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
(2)创建或获取消息队列
ftok
函数
key_t ftok(const char *pathname, int proj_id);
- 作用:生成一个唯一的键值,通常用于创建消息队列、共享内存等 IPC 对象的标识。
- 参数:
pathname
:一个已存在的文件路径名。proj_id
:一个字符,用于生成唯一的键值。
- 返回值:成功时返回一个唯一的键值,失败时返回 -1。
msgget
函数
int msgget(key_t key, int msgflg);
- 作用:创建或获取一个消息队列。
- 参数:
key
:由ftok
函数生成的键值。msgflg
:标志位,可以是IPC_CREAT
(如果消息队列不存在则创建)、IPC_EXCL
(如果消息队列已存在则出错)以及权限标志(如0666
)的组合。
- 返回值:成功时返回消息队列的标识符,失败时返回 -1。
代码示例
key_t key = ftok(".", 'a');
int msgid = msgget(key, IPC_CREAT | 0666);
if (msgid == -1) {
perror("msgget");
return -1;
}
(3)发送消息
定义一个消息结构体,然后使用msgsnd
函数向消息队列发送消息
代码示例
struct msgbuf {
long mtype;
char mtext[1024];
};
msgsnd
函数
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
- 作用:向消息队列发送消息。
- 参数:
msgid
:消息队列的标识符。msgp
:指向要发送的消息结构体的指针。msgsz
:消息正文的长度。msgflg
:标志位,通常为 0 或IPC_NOWAIT
(如果消息队列已满则不阻塞立即返回错误)。
- 返回值:成功时返回 0,失败时返回 -1。
代码示例
//定义消息结构体
struct msgbuf msg;
msg.mtype = 1;
strcpy(msg.mtext, "Hello, message queue!");
if (msgsnd(msgid, &msg, sizeof(msg.mtext), 0) == -1) {
perror("msgsnd");
return -1;
}
(4)接收消息
msgrcv
函数
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
- 作用:从消息队列中接收消息。
- 参数:
msgid
:消息队列的标识符。msgp
:指向接收消息的缓冲区结构体的指针。msgsz
:消息正文的最大长度。msgtyp
:要接收的消息类型。msgflg
:标志位,可以是IPC_NOWAIT
(如果没有满足条件的消息则不阻塞立即返回错误)等。
- 返回值:成功时返回实际读取的字节数,失败时返回 -1。
代码示例
struct msgbuf rcv_msg;
if (msgrcv(msgid, &rcv_msg, sizeof(rcv_msg.mtext), 1, 0) == -1) {
perror("msgrcv");
return -1;
}
printf("Received message: %s\n", rcv_msg.mtext);
(5)删除消息队列
msgctl
函数
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
- 作用:对消息队列进行控制操作,如删除消息队列、获取消息队列的状态信息等。
- 参数:
msgid
:消息队列的标识符。cmd
:控制命令,可以是IPC_STAT
(获取消息队列的状态信息并存入buf
指向的结构体中)、IPC_SET
(设置消息队列的属性)、IPC_RMID
(删除消息队列)等。buf
:指向一个缓冲区,用于存储或设置消息队列的状态信息,具体取决于cmd
的值。
- 返回值:成功时返回相应的状态信息或 0,失败时返回 -1。
代码示例
if (msgctl(msgid, IPC_RMID, NULL) == -1) {
perror("msgctl");
return -1;
4.用消息队列的方式完成进线程间的简单通信:
(1)发送消息
代码示例
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
//创建消息结构体
typedef struct msgbuf{
long mtype;
char mtext[32];
}MSG;
int main(int argc, char *argv[])
{
//创建key值
key_t mykey = ftok(".",'b');
if(mykey < 0)
{
perror("ftok");
return -1;
}
//msgget()--创建或获取消息队列
int msgfd = msgget(mykey, 0664 |IPC_CREAT);
if(msgfd < 0)
{
perror("msgget");
return -1;
}
//准备好需要发送的消息,初始化结构体
MSG message1 = {1,"hello"};
MSG message2 = {2,"world"};
MSG message3 = {3,"niaho"};
//magsnd()--发送消息
msgsnd(msgfd,&message2,sizeof(MSG)-8,0);
return 0;
}
(2)接收消息
代码示例
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
//创建消息结构体
typedef struct msgbuf{
long mtype;
char mtext[32];
}MSG;
int main(int argc, char *argv[])
{
//创建key值
key_t mykey = ftok(".",'b');
if(mykey < 0)
{
perror("ftok");
return -1;
}
//msgget()--创建或获取消息队列
int msgfd = msgget(mykey, 0664 |IPC_CREAT);
if(msgfd < 0)
{
perror("msgget");
return -1;
}
//magrcv()--接受消息
MSG message2;
int ret = msgrcv(msgfd,&message2,sizeof(MSG)-8,2,0);
if(ret < 0)
{
perror("read");
return -1;
}
printf("message = %s\n",message2.mtext);
return 0;
}