POSIX消息队列与System V消息队列

POSIX消息队列与System V消息队列

1.主要函数

System V消息队列

System V消息队列是Unix和类Unix操作系统中的一种进程间通信(IPC)机制,允许进程之间通过消息进行通信。与POSIX消息队列不同,System V消息队列提供了更底层的控制方式,适用于需要更精细控制的传统IPC场景。

消息队列的主要特点

  1. 消息类型:每条消息都有一个类型标识,类型用整数表示且必须大于0。这允许进程根据类型选择性地接收消息。
  2. 优先级:消息可以有优先级,优先级高的消息将优先被处理。
  3. 内核维护:消息队列由内核维护,即使发送进程退出,消息仍然保留在队列中,直到被接收。
  4. 生命周期:消息队列的生命周期不随进程结束而结束,而是随操作系统的运行而持续存在,需要显式地调用接口或命令才能删除
#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标准兼容,使得跨平台的代码更加容易编写。

主要特点

  1. 标准化:POSIX消息队列遵循POSIX标准,具有良好的跨平台兼容性。
  2. 优先级支持:支持消息优先级,优先级高的消息会优先被接收。
  3. 非阻塞和阻塞模式:支持阻塞和非阻塞两种模式,可以根据需要选择。
  4. 信号通知:可以配置信号通知,当消息队列中有新消息时,会发送信号通知接收进程。
  5. 文件描述符操作:消息队列可以像文件描述符一样操作,支持readwritefcntl等系统调用。
#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 sigeventsigval_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_notifySIGEV_SIGNAL时,指定要发送的信号。
  • sigev_value:通知时传递的数据,类型为union sigval
  • sigev_notify_function:当sigev_notifySIGEV_THREAD时,指定的线程函数。
  • sigev_notify_attributes:当sigev_notifySIGEV_THREAD时,指定线程的属性。
  • sigev_notify_thread_id:当sigev_notifySIGEV_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 sigeventmq_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消息队列的区别

  1. 标准化
  • POSIX消息队列:遵循POSIX标准,具有良好的跨平台兼容性。
  • System V消息队列:特定于Unix和类Unix系统,不遵循POSIX标准,跨平台兼容性较差。
  1. 接口设计
  • POSIX消息队列:提供了更现代、更简洁的接口,使用文件描述符操作,支持readwritefcntl等系统调用。
  • System V消息队列:使用更底层的系统调用,如msggetmsgsndmsgrcvmsgctl,接口相对复杂。
  1. 消息优先级
  • POSIX消息队列:支持消息优先级,优先级高的消息会优先被接收。
  • System V消息队列:也支持消息优先级,但优先级的处理方式与POSIX有所不同。
  1. 非阻塞和阻塞模式
  • POSIX消息队列:支持阻塞和非阻塞两种模式,可以通过fcntl设置。
  • System V消息队列:支持阻塞和非阻塞两种模式,通过msgsndmsgrcvmsgflg参数设置。
  1. 信号通知
  • POSIX消息队列:可以配置信号通知,当消息队列中有新消息时,会发送信号通知接收进程。
  • System V消息队列:不支持信号通知,需要轮询或使用其他机制检测新消息。
  1. 文件描述符操作
  • POSIX消息队列:消息队列可以像文件描述符一样操作,支持readwritefcntl等系统调用。
  • System V消息队列:不支持文件描述符操作,只能通过特定的系统调用操作。
  1. 性能
  • POSIX消息队列:在某些无竞争条件下,性能可能会稍低于System V消息队列。
  • System V消息队列:通常性能较好,尤其是在高竞争条件下。
  1. 限制
  • POSIX消息队列:存在系统能够创建的消息队列个数有限、单条消息大小受限等限制。
  • System V消息队列:也存在类似的限制,但具体限制可能因系统而异。

推荐使用POSIX消息队列

  • 跨平台兼容性:POSIX消息队列遵循POSIX标准,具有良好的跨平台兼容性,适用于需要在不同操作系统上运行的代码。
  • 现代接口:POSIX消息队列提供了更现代、更简洁的接口,使用文件描述符操作,支持readwritefcntl等系统调用,代码更易于理解和维护。
  • 灵活性:支持优先级、非阻塞模式、信号通知等,提供了更多的灵活性和功能。
  • 文件描述符操作:可以像文件描述符一样操作,支持selectpoll等多路复用机制,便于与其他文件描述符一起管理。

适用场景

  • 跨平台开发:如果你的代码需要在不同操作系统上运行,POSIX消息队列是更好的选择。
  • 现代接口:如果你更倾向于使用现代的接口和文件描述符操作,POSIX消息队列会更方便。
  • 灵活性:如果你需要使用优先级、非阻塞模式、信号通知等高级功能,POSIX消息队列提供了更多的灵活性。

总结

虽然System V消息队列在某些情况下可能具有更好的性能,但POSIX消息队列的标准化、现代接口和灵活性使其成为更推荐的选择,尤其是在跨平台开发和现代系统编程中。