POSIX消息队列与System V消息队列
文章目录
1.主要函数
System V消息队列
System V消息队列是Unix和类Unix操作系统中的一种进程间通信(IPC)机制,允许进程之间通过消息进行通信。与POSIX消息队列不同,System V消息队列提供了更底层的控制方式,适用于需要更精细控制的传统IPC场景。
消息队列的主要特点
- 消息类型:每条消息都有一个类型标识,类型用整数表示且必须大于0。这允许进程根据类型选择性地接收消息。
- 优先级:消息可以有优先级,优先级高的消息将优先被处理。
- 内核维护:消息队列由内核维护,即使发送进程退出,消息仍然保留在队列中,直到被接收。
- 生命周期:消息队列的生命周期不随进程结束而结束,而是随操作系统的运行而持续存在,需要显式地调用接口或命令才能删除
#include <sys/msg.h>
int msgget(key_t key, int oflag)
int msgsnd(int msqid, const void * ptr, size_tlength, int flag)
ssize_t msgrcv (int msqid, void *ptr, size_t length, long type, int flag)
int msgctl(int msqid, int cmd, struct msqid_ds *buf)
POSIX 消息队列
POSIX消息队列是一种标准化的进程间通信(IPC)机制,它提供了一种在不同进程之间传递消息的方法。与System V消息队列相比,POSIX消息队列提供了更现代、更灵活的接口,并且与POSIX标准兼容,使得跨平台的代码更加容易编写。
主要特点
- 标准化:POSIX消息队列遵循POSIX标准,具有良好的跨平台兼容性。
- 优先级支持:支持消息优先级,优先级高的消息会优先被接收。
- 非阻塞和阻塞模式:支持阻塞和非阻塞两种模式,可以根据需要选择。
- 信号通知:可以配置信号通知,当消息队列中有新消息时,会发送信号通知接收进程。
- 文件描述符操作:消息队列可以像文件描述符一样操作,支持
read
、write
、fcntl
等系统调用。
#include <mqueue.h>
mqd_t mq_open(const char *name, int oflag, mode_t mode, struct mq_attr attr );
int mq_close(mqd_t mqdes);//
int mq_unlink(const char *name);
int mq_getattr(mqd_t mqdes, struct mq_attr *attr);
int mq_setattr(mqd_t mqdes, struct mq_attr *attr, struct mq_attr *oattr);
int mq_send(mqd_t mqdes, const char *ptr, size_t len, unsigned int prio);
ssize_t mq_receive(mqd_t mqdes, char *ptr, size_t len, unsigned int *prio);
int mq_notify(mqd_t mqdes, const struct sigevent *notification);
2.System V消息队列
2.1 msgget
用途:创建一个新的消息队列或获取现有消息队列的ID。
函数原型:
int msgget(key_t key, int msgflg);
参数:
key
:消息队列的键值,通常由ftok
函数生成。msgflg
:标志位,用于指定消息队列的权限和操作方式。常用标志位包括:IPC_CREAT
:如果消息队列不存在,则创建一个新的消息队列。IPC_EXCL
:如果消息队列已存在,则返回错误。- 权限位(如
0666
):指定消息队列的读写权限。
返回值:
- 成功时返回消息队列的ID。
- 失败时返回-1,并设置
errno
。
示例:
key_t key = ftok("path", 1234);
int msgid = msgget(key, 0666 | IPC_CREAT);
if (msgid == -1) {
perror("msgget");
exit(1);
}
2. 2 msgsnd
用途:向消息队列发送一条消息。
函数原型:
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
参数:
msqid
:消息队列的ID,由msgget
返回。msgp
:指向消息缓冲区的指针,消息缓冲区的第一个字段必须是long
类型,表示消息类型。msgsz
:消息的大小(不包括消息类型字段)。msgflg
:标志位,常用标志位包括:0
:阻塞模式,如果队列已满,则等待。IPC_NOWAIT
:非阻塞模式,如果队列已满,则立即返回。
返回值:
- 成功时返回0。
- 失败时返回-1,并设置
errno
。
示例:
struct msgbuf {
long mtype;
char mtext[80];
};
struct msgbuf msg;
msg.mtype = 1;
strcpy(msg.mtext, "Hello, World!");
if (msgsnd(msgid, &msg, sizeof(msg.mtext), 0) == -1) {
perror("msgsnd");
exit(1);
}
2.3 msgrcv
用途:从消息队列接收一条消息。
函数原型:
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
参数:
msqid
:消息队列的ID,由msgget
返回。msgp
:指向消息缓冲区的指针,消息缓冲区的第一个字段必须是long
类型,表示消息类型。msgsz
:消息的大小(不包括消息类型字段)。msgtyp
:消息类型,用于选择性地接收消息。常用值包括:0
:接收队列中的第一条消息。- 正数:接收指定类型的消息。
- 负数:接收类型大于等于
|msgtyp|
的最小类型的消息。
msgflg
:标志位,常用标志位包括:0
:阻塞模式,如果队列为空,则等待。IPC_NOWAIT
:非阻塞模式,如果队列为空,则立即返回。
返回值:
- 成功时返回接收到的消息的大小。
- 失败时返回-1,并设置
errno
。
示例:
struct msgbuf msg;
if (msgrcv(msgid, &msg, sizeof(msg.mtext), 1, 0) == -1) {
perror("msgrcv");
exit(1);
}
printf("Message received: %s\n", msg.mtext);
2.4 msgctl
用途:对消息队列进行控制操作,如删除队列或设置队列属性。
函数原型:
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
参数:
msqid
:消息队列的ID,由msgget
返回。cmd
:控制命令,常用命令包括:IPC_RMID
:删除消息队列。IPC_SET
:设置消息队列的属性。IPC_STAT
:获取消息队列的属性。
buf
:指向struct msqid_ds
结构的指针,用于存储或修改消息队列的属性。
返回值:
- 成功时返回0。
- 失败时返回-1,并设置
errno
。
示例:
// 删除消息队列
if (msgctl(msgid, IPC_RMID, NULL) == -1) {
perror("msgctl");
exit(1);
}
示例代码
msgget
:创建或获取消息队列。msgsnd
:向消息队列发送消息。msgrcv
:从消息队列接收消息。msgctl
:对消息队列进行控制操作。
这些函数共同构成了System V消息队列的完整功能,通过合理使用这些函数,可以实现进程间的高效通信。
发送消息
#include <stdio.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/msg.h>
struct msgbuf {
long mtype;
char mtext[80];
};
int main() {
key_t key = 1234;
int msgid = msgget(key, 0666 | IPC_CREAT);
struct msgbuf msg;
msg.mtype = 1;
strcpy(msg.mtext, "Hello, World!");
if (msgsnd(msgid, &msg, sizeof(msg.mtext), 0) == -1) {
perror("msgsnd");
exit(1);
}
printf("Message sent: %s\n", msg.mtext);
msgctl(msgid, IPC_RMID, NULL);
return 0;
}
接收消息
#include <stdio.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/msg.h>
struct msgbuf {
long mtype;
char mtext[80];
};
int main() {
key_t key = 1234;
int msgid = msgget(key, 0666 | IPC_CREAT);
struct msgbuf msg;
if (msgrcv(msgid, &msg, sizeof(msg.mtext), 1, 0) == -1) {
perror("msgrcv");
exit(1);
}
printf("Message received: %s\n", msg.mtext);
msgctl(msgid, IPC_RMID, NULL);
return 0;
}
3.POSIX消息队列
3.1 mq_open
用途:打开或创建一个消息队列。
函数原型:
mqd_t mq_open(const char *name, int oflag, mode_t mode, struct mq_attr *attr);
参数:
name
:消息队列的名称,通常以/
开头。oflag
:打开标志,常用标志包括:O_CREAT
:如果消息队列不存在,则创建一个新的消息队列。O_EXCL
:如果消息队列已存在,则返回错误。O_RDWR
:以读写模式打开。O_WRONLY
:以只写模式打开。O_RDONLY
:以只读模式打开。
mode
:权限位,指定消息队列的读写权限。attr
:指向struct mq_attr
结构的指针,用于设置消息队列的属性。
struct mq_attr
关于 struct mq_attr属性结构:
struct mq_attr { long mq_flags;//阻塞标志, 0(阻塞)或O_NONBLOCK long mq_maxmsg;//最大消息数 long mq_msgsize;//每个消息最大大小 long mq_curmsgs;//当前消息数 };
返回值:
- 成功时返回消息队列的描述符。
- 失败时返回-1,并设置
errno
。
示例:
mqd_t mq;
struct mq_attr attr;
attr.mq_flags = 0;
attr.mq_maxmsg = 10;
attr.mq_msgsize = 1024;
attr.mq_curmsgs = 0;
mq = mq_open("/my_mq", O_CREAT | O_RDWR, 0644, &attr);
if (mq == (mqd_t)-1) {
perror("mq_open");
exit(1);
}
3.2 mq_send
用途:向消息队列发送一条消息。
函数原型:
int mq_send(mqd_t mqdes, const char *msg_ptr, size_t msg_len, unsigned msg_prio);
参数:
mqdes
:消息队列的描述符,由mq_open
返回。msg_ptr
:指向消息内容的指针。msg_len
:消息的长度。msg_prio
:消息的优先级。
返回值:
- 成功时返回0。
- 失败时返回-1,并设置
errno
。
示例:
char *msg = "Hello, World!";
if (mq_send(mq, msg, strlen(msg) + 1, 1) == -1) {
perror("mq_send");
exit(1);
}
2.3 mq_receive
用途:从消息队列接收一条消息。
函数原型:
ssize_t mq_receive(mqd_t mqdes, char *msg_ptr, size_t msg_len, unsigned *msg_prio);
参数:
mqdes
:消息队列的描述符,由mq_open
返回。msg_ptr
:指向接收消息内容的缓冲区。msg_len
:缓冲区的大小。msg_prio
:指向消息优先级的指针,可选。
返回值:
- 成功时返回接收到的消息的长度。
- 失败时返回-1,并设置
errno
。
示例:
char buffer[1024];
unsigned msg_prio;
ssize_t bytes = mq_receive(mq, buffer, sizeof(buffer), &msg_prio);
if (bytes == -1) {
perror("mq_receive");
exit(1);
}
printf("Message received: %s\n", buffer);
2.4 mq_close
用途:关闭消息队列描述符。
函数原型:
int mq_close(mqd_t mqdes);
参数:
mqdes
:消息队列的描述符,由mq_open
返回。
返回值:
- 成功时返回0。
- 失败时返回-1,并设置
errno
。
示例:
if (mq_close(mq) == -1) {
perror("mq_close");
exit(1);
}
2.5 mq_unlink
用途:删除消息队列。
函数原型:
int mq_unlink(const char *name);
参数:
name
:消息队列的名称。
返回值:
- 成功时返回0。
- 失败时返回-1,并设置
errno
。
示例:
if (mq_unlink("/my_mq") == -1) {
perror("mq_unlink");
exit(1);
}
2.6 mq_notify
用途:设置消息队列的通知机制,当消息队列中有新消息时,会触发通知。
函数原型:
int mq_notify(mqd_t mqdes, const struct sigevent *notification);
参数:
mqdes
:消息队列的描述符,由mq_open
返回。notification
:指向struct sigevent
结构的指针,用于指定通知机制。如果为NULL
,则取消当前的通知设置。
struct sigevent
struct sigevent
和sigval_t sigev_val
的定义
struct sigevent
struct sigevent
是一个结构体,用于描述当消息队列中有新消息时,如何通知进程。它的定义如下:struct sigevent { int sigev_notify; /* Notification method */ int sigev_signo; /* Notification signal */ union sigval sigev_value; /* Data passed with notification */ void (*sigev_notify_function)(union sigval); /* Function used for thread notification (SIGEV_THREAD) */ void *sigev_notify_attributes; /* Attributes for notification thread (SIGEV_THREAD) */ pid_t sigev_notify_thread_id; /* ID of thread to signal (SIGEV_THREAD_ID); Linux-specific */ };
sigev_notify
:通知方法,取值如下:SIGEV_NONE
:不发送任何通知。SIGEV_SIGNAL
:发送一个信号事件发生时,将sigev_signo指定的信号发送给指定的进程;SIGEV_THREAD
:事件发生时,内核会(在此进程内)以sigev_notify_attributes为线程属性创建一个线程,并让其执行
sigev_notify_function,并以sigev_value为其参数sigev_signo
:当sigev_notify
为SIGEV_SIGNAL
时,指定要发送的信号。sigev_value
:通知时传递的数据,类型为union sigval
。sigev_notify_function
:当sigev_notify
为SIGEV_THREAD
时,指定的线程函数。sigev_notify_attributes
:当sigev_notify
为SIGEV_THREAD
时,指定线程的属性。sigev_notify_thread_id
:当sigev_notify
为SIGEV_THREAD_ID
时,指定要发送信号的线程ID(Linux特有)。
union sigval
union sigval
是一个联合体,用于在通知时传递数据。它的定义如下:union sigval { int sival_int; /* Integer value */ void *sival_ptr; /* Pointer value */ };
sival_int
:整数值。sival_ptr
:指针值。使用示例
以下是一个使用
struct sigevent
和mq_notify
的示例代码:#include <stdio.h> #include <mqueue.h> #include <pthread.h> #include <string.h> #include <errno.h> #include <signal.h> #include <stdlib.h> #include <unistd.h> #define MYQUEUE "/test_queue" #define MESSAGE "queue test message" void *sender_thread(void *arg) { mqd_t mqdes = *(mqd_t *)arg; char message[] = MESSAGE; printf("sender thread message=%s, mqd=%d\n", message, mqdes); int ret = mq_send(mqdes, message, strlen(message) + 1, 0); // 发送消息,优先级为0 if (-1 == ret) { if (errno == EAGAIN) { printf("The queue was full\n"); // 消息队列已满,继续发送 } else { perror("mq_send"); return NULL; } } printf("send thread end\n"); return NULL; } void notify_thread(union sigval sval) { mqd_t mqdes = *((mqd_t *)sval.sival_ptr); char buffer[256]; size_t bytes_read; struct sigevent sev; printf("notify is already running\n"); while (1) { bytes_read = mq_receive(mqdes, buffer, 256, NULL); if (bytes_read == -1) { if (errno == EAGAIN) { printf("the queue is empty\n"); break; } else { perror("mq_receive"); exit(-1); } } printf("receive message:%s\n", buffer); } sev.sigev_notify = SIGEV_THREAD; sev.sigev_notify_function = notify_thread; sev.sigev_notify_attributes = NULL; sev.sigev_value.sival_ptr = &mqdes; if (mq_notify(mqdes, &sev) == -1) { perror("mq_notify"); exit(-1); } printf("notify is end\n"); } int main(int argc, char *argv[]) { pthread_t sender; struct mq_attr attr; attr.mq_flags = 0; // 阻塞模式 attr.mq_maxmsg = 10; // 最大消息数量 attr.mq_msgsize = 256; // 每个消息最大大小 attr.mq_curmsgs = 0; // 当前0个消息 mqd_t mqdes = mq_open(MYQUEUE, O_CREAT | O_RDWR, 0666, &attr); // 创建一个消息队列 if (mqdes == -1) { perror("mq_open"); return -1; } struct sigevent sev; sev.sigev_notify = SIGEV_THREAD; sev.sigev_notify_function = notify_thread; sev.sigev_notify_attributes = NULL; sev.sigev_value.sival_ptr = &mqdes; int ret = mq_notify(mqdes, &sev); printf("mq_notify return:%d\n", ret); if (ret == -1) { perror("mq_notify"); return -1; } if (-1 == pthread_create(&sender, NULL, sender_thread, &mqdes)) { perror("sender pthread_create"); return -1; } pthread_join(sender, NULL); sleep(10); mq_close(mqdes); mq_unlink(MYQUEUE); return 0; }
在这个示例中,
struct sigevent
被用来设置消息队列的通知机制,当消息队列中有新消息时,会触发notify_thread
函数。
返回值:
- 成功时返回0。
- 失败时返回-1,并设置
errno
。
示例:
struct sigevent sev;
sev.sigev_notify = SIGEV_SIGNAL;
sev.sigev_signo = SIGUSR1;
sev.sigev_value.sival_ptr = &mq;
if (mq_notify(mq, &sev) == -1) {
perror("mq_notify");
exit(1);
}
2.7 mq_getattr
用途:获取消息队列的属性。
函数原型:
int mq_getattr(mqd_t mqdes, struct mq_attr *attr);
参数:
mqdes
:消息队列的描述符,由mq_open
返回。attr
:指向struct mq_attr
结构的指针,用于存储消息队列的属性。
返回值:
- 成功时返回0。
- 失败时返回-1,并设置
errno
。
示例:
struct mq_attr attr;
if (mq_getattr(mq, &attr) == -1) {
perror("mq_getattr");
exit(1);
}
printf("Max messages: %ld\n", attr.mq_maxmsg);
printf("Message size: %ld\n", attr.mq_msgsize);
printf("Current messages: %ld\n", attr.mq_curmsgs);
2.8 mq_setattr
用途:设置消息队列的属性。
函数原型:
int mq_setattr(mqd_t mqdes, const struct mq_attr *attr, struct mq_attr *oattr);
参数:
mqdes
:消息队列的描述符,由mq_open
返回。attr
:指向struct mq_attr
结构的指针,用于指定新的属性。oattr
:指向struct mq_attr
结构的指针,用于存储旧的属性。如果为NULL
,则不存储旧的属性。
返回值:
- 成功时返回0。
- 失败时返回-1,并设置
errno
。
示例:
struct mq_attr new_attr, old_attr;
new_attr.mq_flags = O_NONBLOCK;
new_attr.mq_maxmsg = 10;
new_attr.mq_msgsize = 1024;
new_attr.mq_curmsgs = 0;
if (mq_setattr(mq, &new_attr, &old_attr) == -1) {
perror("mq_setattr");
exit(1);
}
printf("Old max messages: %ld\n", old_attr.mq_maxmsg);
printf("Old message size: %ld\n", old_attr.mq_msgsize);
printf("Old current messages: %ld\n", old_attr.mq_curmsgs);
mq_notify
:设置消息队列的通知机制,当有新消息时触发通知。mq_getattr
:获取消息队列的属性。mq_setattr
:设置消息队列的属性,并可选择获取旧的属性。
这些函数提供了对POSIX消息队列的更细粒度的控制,使得开发者可以更好地管理消息队列的行为和性能。
示例代码
发送消息
#include <stdio.h>
#include <stdlib.h>
#include <mqueue.h>
int main() {
mqd_t mq;
struct mq_attr attr;
attr.mq_flags = 0;
attr.mq_maxmsg = 10;
attr.mq_msgsize = 1024;
attr.mq_curmsgs = 0;
mq = mq_open("/my_mq", O_CREAT | O_RDWR, 0644, &attr);
if (mq == (mqd_t)-1) {
perror("mq_open");
exit(1);
}
char *msg = "Hello, World!";
if (mq_send(mq, msg, strlen(msg) + 1, 1) == -1) {
perror("mq_send");
exit(1);
}
printf("Message sent: %s\n", msg);
if (mq_close(mq) == -1) {
perror("mq_close");
exit(1);
}
if (mq_unlink("/my_mq") == -1) {
perror("mq_unlink");
exit(1);
}
return 0;
}
接收消息
#include <stdio.h>
#include <stdlib.h>
#include <mqueue.h>
int main() {
mqd_t mq;
struct mq_attr attr;
mq = mq_open("/my_mq", O_CREAT | O_RDWR, 0644, &attr);
if (mq == (mqd_t)-1) {
perror("mq_open");
exit(1);
}
char buffer[1024];
unsigned msg_prio;
ssize_t bytes = mq_receive(mq, buffer, sizeof(buffer), &msg_prio);
if (bytes == -1) {
perror("mq_receive");
exit(1);
}
printf("Message received: %s\n", buffer);
if (mq_close(mq) == -1) {
perror("mq_close");
exit(1);
}
if (mq_unlink("/my_mq") == -1) {
perror("mq_unlink");
exit(1);
}
return 0;
}
示例1:使用阻塞方式读写
// 包含所需的头文件
#include <pthread.h> // POSIX线程库
#include <stdio.h> // 标准输入输出库
#include <stdlib.h> // 标准库
#include <unistd.h> // UNIX标准库
#include <mqueue.h> // POSIX消息队列库
#include <string.h> // 字符串处理库
// 定义消息队列的名称和要发送的消息
#define QUEUE_NAME "/test_queue" // 消息队列的名称
#define MESSAGE "Hello, World!" // 要发送的消息
// 全局的消息队列描述符
mqd_t mq;
// sender_thread函数:发送线程的主体
void *sender_thread(void *arg) {
char message[] = MESSAGE; // 创建要发送的消息的副本
printf("Sender thread started.\n"); // 打印发送线程开始的消息
mq_send(mq, message, strlen(message) + 1, 0); // 发送消息到消息队列
printf("Message sent.\n"); // 打印消息已发送的消息
return NULL; // 返回NULL,表示线程正常结束
}
// receiver_thread函数:接收线程的主体
void *receiver_thread(void *arg) {
char buffer[256]; // 创建用于接收消息的缓冲区
printf("Receiver thread started.\n"); // 打印接收线程开始的消息
mq_receive(mq, buffer, sizeof(buffer), NULL); // 从消息队列接收消息
printf("Received message: %s\n", buffer); // 打印已接收的消息
return NULL; // 返回NULL,表示线程正常结束
}
// main函数:程序的入口点
int main() {
pthread_t sender, receiver; // 创建发送和接收线程的标识符
struct mq_attr attr; // 创建消息队列属性结构体变量
// 设置消息队列的属性值
attr.mq_flags = 0; // 消息队列的标志位设置为0
attr.mq_maxmsg = 10; // 消息队列的最大消息数设置为10
attr.mq_msgsize = 256; // 消息队列的每个消息的最大大小设置为256字节
attr.mq_curmsgs = 0; // 消息队列的当前消息数设置为0
// 打开或创建名为QUEUE_NAME的消息队列,并设置其属性为attr指定的值
mq = mq_open(QUEUE_NAME, O_CREAT | O_RDWR, 0666, &attr);
if (mq == (mqd_t)-1) {
// 如果打开或创建失败,则打印错误信息并返回1
perror("mq_open");
return 1;
}
// 创建发送线程,如果创建失败,则打印错误信息并返回1
if (pthread_create(&sender, NULL, sender_thread, NULL) != 0) {
perror("pthread_create (sender)");
return 1;
}
// 创建接收线程,如果创建失败,则打印错误信息并返回1
if (pthread_create(&receiver, NULL, receiver_thread, NULL) != 0) {
perror("pthread_create (receiver)");
return 1;
}
// 等待发送线程结束,如果发送线程已经结束,则立即返回,否则阻塞等待其结束
pthread_join(sender, NULL);
// 等待接收线程结束,如果接收线程已经结束,则立即返回,否则阻塞等待其结束
pthread_join(receiver, NULL);
// 关闭已打开的消息队列描述符mq所引用的消息队列,并释放由该描述符占用的所有资源
mq_close(mq);
// 删除名为QUEUE_NAME的消息队列,并将其从系统中删除,如果成功则返回0,否则返回-1并设置errno以指示错误。
mq_unlink(QUEUE_NAME); // 删除消息队列
return 0; // 程序正常退出,返回0
}
示例2: 使用mq_notify sigev_notify = SIGEV_THREAD异步通知的方式实现
#include <mqueue.h>
#include <pthread.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#if 0
mqd_t mq_open(const char *name, int oflag, mode_t mode, struct mq_attr attr );
int mq_send(mqd_t mqdes, const char *ptr, size_t len, unsigned int prio); /tiemou
ssize_t mq_receive(mqd_t mqdes, char *ptr, size_t len, unsigned int *prio);
int mq_close(mqd_t mqdes);
int mq_unlink(const char *name)
struct mq_attr
{
long mq_flags;//阻塞标志, 0(阻塞)或O_NONBLOCK
long mq_maxmsg;//最大消息数
long mq_msgsize;//每个消息最大大小
long mq_curmsgs;//当前消息数
};
union sigval {
/* Data passed with notification */
int sival_int; /* Integer value */
void *sival_ptr; /* Pointer value */
};
struct sigevent {
int sigev_notify; /* Notification method */
int sigev_signo; /* Notification signal */
union sigval sigev_value;
/* Data passed with notification */
void (*sigev_notify_function) (union sigval);
/* Function used for thread
notification (SIGEV_THREAD) */
void *sigev_notify_attributes;
/* Attributes for notification thread
(SIGEV_THREAD) */
pid_t sigev_notify_thread_id;
/* ID of thread to signal
(SIGEV_THREAD_ID); Linux-specific */
};
#endif
#define QUEQUE_NAME "/test_queue"
#define MESSAGE "mqueque,test!"
void *sender_thread(void *arg)
{
// 发送消息
mqd_t mqd = *(mqd_t *)arg;
int send_size = -1;
char message[] = MESSAGE;
printf("sender thread message=%s, mqd=%d\n", message, mqd);
send_size = mq_send(mqd, message, strlen(message) + 1, 0);
if (-1 == send_size)
{
if (errno == EAGAIN)
{
printf("message queque is full\n");
}
else
{
perror("mq_send");
}
}
return NULL;
}
void notify_thread (union sigval sval)
{
// 获取消息队列描述符
mqd_t mqd = -1;
mqd = *((mqd_t *)sval.sival_ptr);
// 定义一个缓冲区,用于存储接收到的消息
char buffer[256];
// 定义一个变量,用于存储接收到的消息的大小
ssize_t bytes_read;
// 定义一个结构体,用于重新注册消息队列的通知
struct sigevent sev;
// 打印提示信息
printf("notify_thread started, mqd=%d\n", mqd);
// 循环接收消息,直到队列为空
while (1)
{
// 从消息队列中接收消息
bytes_read = mq_receive(mqd, buffer, 256, NULL);
// 如果接收失败,检查错误码
if (bytes_read == -1)
{
// 如果错误码是EAGAIN,说明队列为空,跳出循环
if (errno == EAGAIN)
{
printf("queue is empty\n");
break;
}
// 否则,打印错误信息,退出程序
else
{
perror("mq_receive");
exit(1);
}
}
// 如果接收成功,打印接收到的消息的大小和内容
printf("read %ld bytes: %s\n", (long)bytes_read, buffer);
}
// 重新注册消息队列的通知,使用同样的回调函数和参数
sev.sigev_notify = SIGEV_THREAD;
sev.sigev_notify_function = notify_thread;
sev.sigev_notify_attributes = NULL;
sev.sigev_value.sival_ptr = &mqd;
if (mq_notify(mqd, &sev) == -1)
{
perror("mq_notify");
exit(1);
}
}
#if 0
void *receiver_thread(void *arg)
{
mqd_t mqd = *(mqd_t *)arg;
ssize_t receiver_size = -1;
//接收消息
char buffer[256];
printf("Receive trehad start\n");
receiver_size = mq_receive(mqd, buffer, sizeof(buffer), NULL);
printf("receiver thread message=%s, mqd=%d, receiver_size=%ld\n", buffer, mqd, receiver_size);
return NULL;
}
#endif
int main(int argc, char *argv[])
{
pthread_t sender, receiver;
//创建消息队列
mqd_t mqd = -1;
struct mq_attr attr;
attr.mq_flags = 0;
attr.mq_maxmsg = 10;
attr.mq_msgsize = 256;
attr.mq_curmsgs = 0;
mqd = mq_open(QUEQUE_NAME, O_CREAT | O_RDWR, 0666, &attr);
if (mqd == (mqd_t)-1 )
{
perror("mq_open");
return -1;
}
struct sigevent sev;
// 注册消息队列的通知,使用线程模式,指定回调函数和参数
sev.sigev_notify = SIGEV_THREAD;
sev.sigev_notify_function = notify_thread;
sev.sigev_notify_attributes = NULL;
sev.sigev_value.sival_ptr = &mqd;
if (mq_notify(mqd, &sev) == -1)
{
perror("mq_notify");
exit(1);
}
if (pthread_create(&sender, NULL, sender_thread, (void *)&mqd) != 0)
{
perror("pthread_create sender");
return -1;
}
#if 0
if (pthread_create(&receiver, NULL, receiver_thread, (void *)&mqd) != 0)
{
perror("pthread_create receiver");
return -1;
}
#endif
pthread_join(sender, NULL);
sleep(5);//等待触发并把消息读走
//pthread_join(receiver, NULL);
mq_close(mqd);
mq_unlink(QUEQUE_NAME);
//sleep(5);
return 0;
}
4.POSIX消息队列与System V消息队列的区别
- 标准化
- POSIX消息队列:遵循POSIX标准,具有良好的跨平台兼容性。
- System V消息队列:特定于Unix和类Unix系统,不遵循POSIX标准,跨平台兼容性较差。
- 接口设计
- POSIX消息队列:提供了更现代、更简洁的接口,使用文件描述符操作,支持
read
、write
、fcntl
等系统调用。 - System V消息队列:使用更底层的系统调用,如
msgget
、msgsnd
、msgrcv
、msgctl
,接口相对复杂。
- 消息优先级
- POSIX消息队列:支持消息优先级,优先级高的消息会优先被接收。
- System V消息队列:也支持消息优先级,但优先级的处理方式与POSIX有所不同。
- 非阻塞和阻塞模式
- POSIX消息队列:支持阻塞和非阻塞两种模式,可以通过
fcntl
设置。 - System V消息队列:支持阻塞和非阻塞两种模式,通过
msgsnd
和msgrcv
的msgflg
参数设置。
- 信号通知
- POSIX消息队列:可以配置信号通知,当消息队列中有新消息时,会发送信号通知接收进程。
- System V消息队列:不支持信号通知,需要轮询或使用其他机制检测新消息。
- 文件描述符操作
- POSIX消息队列:消息队列可以像文件描述符一样操作,支持
read
、write
、fcntl
等系统调用。 - System V消息队列:不支持文件描述符操作,只能通过特定的系统调用操作。
- 性能
- POSIX消息队列:在某些无竞争条件下,性能可能会稍低于System V消息队列。
- System V消息队列:通常性能较好,尤其是在高竞争条件下。
- 限制
- POSIX消息队列:存在系统能够创建的消息队列个数有限、单条消息大小受限等限制。
- System V消息队列:也存在类似的限制,但具体限制可能因系统而异。
推荐使用POSIX消息队列
- 跨平台兼容性:POSIX消息队列遵循POSIX标准,具有良好的跨平台兼容性,适用于需要在不同操作系统上运行的代码。
- 现代接口:POSIX消息队列提供了更现代、更简洁的接口,使用文件描述符操作,支持
read
、write
、fcntl
等系统调用,代码更易于理解和维护。 - 灵活性:支持优先级、非阻塞模式、信号通知等,提供了更多的灵活性和功能。
- 文件描述符操作:可以像文件描述符一样操作,支持
select
、poll
等多路复用机制,便于与其他文件描述符一起管理。
适用场景
- 跨平台开发:如果你的代码需要在不同操作系统上运行,POSIX消息队列是更好的选择。
- 现代接口:如果你更倾向于使用现代的接口和文件描述符操作,POSIX消息队列会更方便。
- 灵活性:如果你需要使用优先级、非阻塞模式、信号通知等高级功能,POSIX消息队列提供了更多的灵活性。
总结
虽然System V消息队列在某些情况下可能具有更好的性能,但POSIX消息队列的标准化、现代接口和灵活性使其成为更推荐的选择,尤其是在跨平台开发和现代系统编程中。