IPC(Inter-Process Communication,进程间通信)消息队列是一种在不同进程之间传递数据的机制。它允许多个进程通过共享的消息队列进行异步通信。
消息队列是在内核中创建和维护的,用于存储和传递消息。多个进程可以通过使用相同的消息队列标识符来访问同一个消息队列。每个消息都有一个类型和数据部分,进程可以按照类型从消息队列中读取特定类型的消息。
使用IPC消息队列进行进程间通信的一般步骤如下:
-
创建或获取消息队列:使用
msgget
函数创建或获取一个消息队列。该函数返回一个唯一的消息队列标识符。 -
发送消息到消息队列:使用
msgsnd
函数向消息队列发送消息。需要指定消息队列标识符、消息数据和消息类型。 -
接收消息:使用
msgrcv
函数从消息队列接收消息。可以根据消息类型选择接收特定类型的消息。 -
删除消息队列:当不再需要消息队列时,可以使用
msgctl
函数删除消息队列。需要提供消息队列标识符和删除命令。
与消息队列相关的常用 API 包括以下几个:
msgget
:创建或获取一个消息队列。
int msgget(key_t key, int msgflg);
key
:表示消息队列的键值,可以通过ftok
函数生成。它是一个唯一的标识符,用于标识特定的消息队列。多个进程可以通过相同的key
值来获取同一个消息队列。注意,在不同的进程中使用相同的key
来获取消息队列时需要保证权限和存在性。-
msgflg
:用于指定操作的行为选项,是一个标志参数。可以通过以下标志进行组合:IPC_CREAT
:如果指定的消息队列不存在,则创建一个新的消息队列。如果已存在,则返回该消息队列的标识符。IPC_EXCL
:与IPC_CREAT
一同使用,只有在消息队列不存在的情况下才创建新的消息队列,否则会返回错误。- 还可以与文件的读写权限进行按位或运算,例如
IPC_CREAT | 0666
表示创建对所有用户可读可写的消息队列。
- 返回值为消息队列的标识符,即一个正整数。如果成功创建或获取消息队列,则返回一个非负整数表示消息队列的标识符。如果出现错误,则返回 -1,并相应地设置
errno
变量来指示错误的原因。
补充:
ftok
是一个用于生成唯一标识符(key)的函数,通常用于创建共享内存和消息队列等进程间通信机制。
函数原型:
key_t ftok(const char *pathname, int proj_id);
参数解释:
pathname
:一个存在的文件路径名,用于生成唯一的 key。可以是任意有效的文件路径。proj_id
:一个用户定义的整数,用于区分不同的标识符。通常为正整数。
返回值:
- 如果成功,返回一个唯一的 key 值。
- 如果出错,返回 -1,并设置
errno
错误码来指示具体错误。
ftok
函数通过将给定的 pathname
转化为一个无符号的整数标识符,并与 proj_id
进行组合,生成一个唯一的 key。这个 key 可以用于创建或获取共享内存或消息队列等进程间通信机制。
需要注意的是,ftok
函数生成的 key 可能在不同系统上有不同的表示方式,因此在跨平台的程序中需要小心使用。另外,ftok
生成的 key 并不是全局唯一的,仅在相同的 pathname
和 proj_id
下才能保证唯一性。
2.msgsnd
:向消息队列发送消息。
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
返回值为操作的结果,即一个非负整数。如果成功发送消息,则返回0。如果出现错误,返回-1,并设置 errno
变量来指示错误的原因。
-
msqid
:表示消息队列的标识符,即通过msgget
函数获取到的值。 -
msgp
:指向要发送的消息的指针,通常是一个自定义的结构体指针。 -
msgsz
:表示要发送消息的大小,以字节为单位。注意,该大小必须至少大于等于msgp
指向的消息结构体的大小。 -
msgflg
:用于指定发送消息的行为选项,是一个标志参数。可以通过以下标志进行组合:
- 0:阻塞模式,如果发送队列已满,则调用进程被阻塞,直到有空间可以发送消息。
IPC_NOWAIT
:非阻塞模式,如果发送队列已满,则返回错误而不等待。- 其他标志可以与上述标志进行按位或运算,例如
IPC_NOWAIT | <其他标志>
。
3.msgrcv
:从消息队列接收消息。
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
-
msqid
:表示消息队列的标识符,即通过msgget
函数获取到的值。 -
msgp
:指向消息缓冲区的指针,用于接收接收到的消息内容。通常是一个自定义的结构体指针。 -
msgsz
:表示消息缓冲区的大小,以字节为单位。注意,该大小必须至少大于等于消息结构体的大小,以确保能够容纳接收到的消息内容。 -
msgtyp
:表示接收消息的类型。可以通过不同的msgtyp
值来选择接收具有不同类型的消息。常见的取值如下:0
:接收队列中的第一个消息。>0
:接收队列中消息类型等于msgtyp
的第一个消息。<0
:接收队列中消息类型小于等于msgtyp
绝对值的最小消息。 注意,如果指定了非零的msgtyp
值,则只接收匹配类型的消息;如果指定了0
,则接收队列中的第一个消息。
-
msgflg
:用于指定接收消息的行为选项,是一个标志参数。可以通过以下标志进行组合:
- 0:阻塞模式,如果接收队列中没有符合条件的消息,则调用进程被阻塞,直到有可接收的消息。
IPC_NOWAIT
:非阻塞模式,如果接收队列中没有符合条件的消息,则返回错误而不等待。- 其他标志可以与上述标志进行按位或运算,例如
IPC_NOWAIT | <其他标志>
。
- 。
4.msgctl
:对消息队列进行控制操作。
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
- 参数
msqid
是消息队列的标识符。 - 参数
cmd
是控制命令,用于指定要执行的操作,如删除队列、获取或设置属性等。 - 参数
buf
是指向struct msqid_ds
结构体的指针,用于传递或接收消息队列的状态信息。 - 返回值表示函数执行成功与否。
下面是通过创建消息队列来实现不同进程的通信:
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdlib.h>
struct msgbuf {
long mtype; /* message type, must be > 0 */
char mtext[256]; /* message data */
};
int main() {
// Generate a unique key for the message queue using ftok function
key_t key;
key = ftok(".", 2);
if (key == -1) {
perror("ftok error");
exit(1);
}
printf("key id = %d\n", key);
// Create or get the message queue using msgget function
struct msgbuf recbuf = {.mtext = {0}};
int qid = msgget(key, IPC_CREAT | 0666);
if (qid == -1) {
perror("Error creating or getting message queue:");
exit(1);
}
// Receive a message from the message queue using msgrcv function
msgrcv(qid, &recbuf, sizeof(recbuf.mtext), 666, 0);
printf("Received data from queue %d: %s\n", qid, recbuf.mtext);
// Prepare a message to send back
struct msgbuf send = {999, "Received, thank you!"};
// Send a message to the message queue using msgsnd function
msgsnd(qid, &send, strlen(send.mtext), 0);
printf("Sent message back to the queue\n");
// Remove the message queue using msgctl function
msgctl(qid, IPC_RMID, NULL);
return 0;
}
结果:
20230820