进程通信——消息队列

https://zhuanlan.zhihu.com/p/268389190

1.概念

IPC的意思是“ 进程间通信机制”。
Linux内核有三种常用IPC对象可以拿来做进程间通信:

  • 消息队列
  • 共享内存
  • 信号量

这三种IPC对象在Linux内核中都以链表的形式存储,它们都有特定的ID来标识(消息队列标识符msqid、共享内存标识符shmid,信号量标识符semid)。

每个 IPC 对象都对应一个ipc_perm结构体, ipc_perm结构体中保留了该ipc对象对应的一些权限信息:

struct ipc_perm {
  key_t __key;                 /* Key supplied to semget(2) */
  uid_t uid;                   /* Effective UID of owner */
  gid_t gid;                   /* Effective GID of owner */
  uid_t cuid;                  /* Effective UID of creator */
  gid_t cgid;                  /* Effective GID of creator */
  unsignedshort mode;          /* Read/write permission.*/
  unsignedshort __seq;         /* Sequence number */
};

1.0 IPC

1.1 什么是消息队列

消息队列称为报文队列也叫做信箱,是linux的一种通信机制这种通信机制传递的数据具有某种结构,而不是简单的字节流。

  • 消息队列的本质是内核提供的链表,内核基于这个链表,实现了一个数据结构。内核又基于此提供了从一个进程向另一个进程发送一块数据的方法。
  • 同时消息队列也适用在同一进程的不同线程间通信。
  • 向消息队列写数据,实际上是向这个数据结构中插入一个新节点;从消息队列读数据,实际上是从这个数据结构中删除一个节点。
  • 消息队列也有管道一样的不足,就是每个数据块的最大长度是有限的,系统上全体队列的最大值也有一个上限。

1.2 消息队列工作机制

在这里插入图片描述

1.3 消息队列与其他进程通信机制的比较:

  • 与信号量相比,消息队列可以承载更多的通信数据。
  • 与管道的默认接收相比,消息队列可以让接收进程有选择地接收通信数据,还可以设置接收的优先级。当使用消息队列的进程终止时,消息队列不会自动删除。但所有引用管道的进程终止时,管道会自动删除。
  • 与共享内存相比,共享内存的速度更快,因为对共享内存的处理不经过内核调用,而消息队列需要经过内核调用。但是在多核系统上,为了避免产生高速缓存一致性问题,更推荐使用消息队列。

1.4 消息队列和其他线程通信机制的比较

  1. 线程安全:使用消息队列可以实现线程间的安全通信,避免了直接共享内存可能引起的问题,如数据竞争和同步问题.
  2. 解耦性:生产者(发送消息的线程)和消费者(接收消息的线程)之间通过消息队列解耦,使得系统更加模块化,便于维护和扩展。
  3. 灵活性:消息队列可以承载多种类型的数据,包括简单的字符串、复杂的数据结构等,且易于扩展新的消息类型。
  4. 可扩展性:在需要增加处理能力时,可以轻松增加消费者线程的数量,而不会影响生产者的性能。
  5. 可靠性:通过适当的错误处理和消息确认机制,可以提高消息传输的可靠性。

2.使用System-V版

2.1 用户消息缓冲区

无论是发送进程还是接收进程,都需要在进程空间中用消息缓冲区来暂存消息,该消息缓冲区的结构定义如下

struct msgbuf{
long mtype;
char mtext[1];
};

  • 可以通过mtype来区分数据类型,通过判断mtype,是否为需要接收的数据
  • mtext[1]为存放消息的正文数组,可以根据消息的大小定义该数组的长度

2.2 创建消息队列msgget

函数原型如下

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

int msgget(key_t key, int msgflg);

参数
key: 某个消息队列的名字,通过宏值定义,或者使用ftok函数生成。
msgflg:由九个权限标志构成,用法和创建文件时使用的mode模式标志是一样的,这里举两个来说明

IPC_CREAT
	如果消息队列对象不存在,则创建之,否则则进行打开操作
IPC_EXCL
	如果消息对象不存在则创建之,否则产生一个错误并返回

返回值:
成功返回一个非负整数,即该消息队列的标识码;
失败则返回-1


2.3 添加消息到消息队列msgsend

函数原型如下:

int  msgsnd(int msgid, const void *msg_ptr, size_t msg_sz, int msgflg);

参数
msgid: 由msgget函数返回的消息队列标识码
msg_ptr:是一个指针,指针指向准备发送的消息缓冲区
msg_sz:是msg_ptr指向的消息长度,消息缓冲区结构体中mtext的大小,不包括数据的类型
msgflg:控制着当前消息队列满或到达系统上限时将要发生的事情
如:
msgflg = IPC_NOWAIT 表示队列满不等待,返回EAGAIN错误

返回值:
成功返回0
失败则返回-1


2.4 从消息队列读取消息、

函数原型如下:

int  msgrcv(int msgid, void *msg_ptr, size_t msgsz,
		 long int msgtype, int msgflg);

参数:
msgid: 由msgget函数返回的消息队列标识码
msg_ptr:是一个指针,指针指向准备接收的消息,
msgsz:是msg_ptr指向的消息长度,消息缓冲区结构体中mtext的大小,不包括数据的类型
msgtype:它可以实现接收优先级的简单形式
msgtype=0返回队列第一条信息
msgtype>0返回队列第一条类型等于msgtype的消息 
msgtype<0返回队列第一条类型小于等于msgtype绝对值的消息
msgflg:控制着队列中没有相应类型的消息可供接收时将要发生的事
msgflg=IPC_NOWAIT,队列没有可读消息不等待,返回ENOMSG错误。
msgflg=MSG_NOERROR,消息大小超过msgsz时被截断

注意
msgtype>0且msgflg=MSC_EXCEPT,接收类型不等于msgtype的第一条消息

返回值:
成功返回实际放到接收缓冲区里去的字符个数
失败,则返回-1


2.5 消息队列的控制函数msgctrl

函数原型:

int  msgctl(int msqid, int command, strcut msqid_ds *buf);

参数:

  • msqid: 由msgget函数返回的消息队列标识码
  • command:是将要采取的动作,(有三个可取值)分别如下:
命令说明
IPC_STAT获取该消息队列的信息,获取到的信息会储存在结构体msqid_ds类型的buf中
IPC_SET在进程有足够权限时,把消息队列的属性值设置为msqid_ds数据结构中给出的值
IPC_RMID删除消息队列
IPC_INFO获得系统对消息队列做的限制

注意若选择删除队列,第三个参数传NULL;
返回值:
如果操作成功,返回“0”;如果失败,则返回“-1”


2.6 msqid_ds

常用于SYSTEM_V版的函数,这个结构体用于控制一个消息队列

struct msqid_ds{
    struct ipc_perm msg_perm;
    time_t msg_stime;               //发送到队列的最后一个消息的时间戳
    time_t msg_rtime;               //从队列中获取的最后一个消息的时间戳
    time_t msg_ctime;               //对队列进行最后一次变动的时间戳
    unsigned long __msg_cbytes;     //在队列上所驻留的字节总数
    msgqnum_t msg_qnum;             //当前队列中消息的数量
    msglen_t msg_qbytes;            //当前队列允许的最大bytes数
    pid_t msg_lspid;                //发送最后一个消息的进程PID
    pid_t msg_lrpid;                //接收最后一个消息的进程PID
};

3. 使用POSIX版本

3.1 mq_open

创建或打开消息队列

#include <mqueue.h>
typedef int mqd_t;
mqd_t mq_open(const char *name, int oflag); 
mqd_t mq_open(const char *name, int oflag, mode_t mode, struct mq_attr *attr);

//链接librt.so动态库
gcc -a.cpp -o a.exe -lrt

参数:
name:消息队列名字
oflag:指定打开消息队列的方式,包括读写方式,是否创建、是否非阻塞等
常见取值:

  • O_RDONLY–以只接收消息的形式打开消息队列
  • O_WRONLY–以只发送消息的形式打开消息队列
  • O_RDWR–以可接收可发送的形式打开消息队列
  • 还有O_CREAT, O_EXCL, O_NONBLOCK

mode:指定了新创建的消息队列的权限模式,类似于open函数中的mode参数,只有在oflag包含O_CREAT时, 才需要指定mode参数,包含:

  • S_IRUSR(4):用户读取权限
  • S_IWUSR(2):用户写入权限
  • S_IXUSR(1):用户执行权限
  • S_IWGGRP(4):组写入权限
  • S_IWOTH(1):其他用户写入权限

attr:指定新队列的属性,若attr为NULL,则使用默认属性

返回:
若成功,返回消息队列描述符mqdes。若失败,返回-1。


3.2 mq_send

向消息队列发送消息

#include <mqueue.h>
int mq_send(mqd_t mqdes, const char *msg_ptr, size_t msg_len, unsigned msg_prio);

参数:
–msg_ptr: 指向需要发送的消息的指针
–msg_len: 消息长度
–msg_prio: 消息的优先级

返回:
若成功,返回0。若失败,返回-1

此处的优先级可以作为不同进程识别消息类型的依据


3.3 mq_receive

从消息队列接受消息

#include <mqueue.h>
int mq_receive(mqd_t mqdes, char *msg_ptr, size_t msg_len, unsigned *msg_prio);

参数:
–msg_ptr: 指向接收到的消息的指针
–msg_len: 消息长度
–msg_prio: 消息的优先级
返回值:
若成功,返回接收到消息的字节数。若失败,返回-1

如果消息队列已满,mq_send()函数将阻塞,直到队列有可用空间或该调用被信号打断。
如果消息队列为空,> mq_receive()函数将阻塞,直到队列有新的消息被放进来。
如果创建消息队列时,oflag传入了O_NONBLOCK,则两个函数不会阻塞,而是会立马报错返回。

如果有多个消费进程,如何保证自己能获取到想要的


3.4 mq_notify

mq_notify用于注册消息队列的通知,当消息队列收到新的消息时,能够及时向指定的线程发送信号。

#include <mqueue.h>
int mq_notify(mqd_t mqdes, const struct sigevent *sevp);

参数说明
mqdes:消息队列描述符,由mq_open或mq_timedsend等函数返回。
sevp:指向sigevent结构体的指针,该结构体定义了如何处理信号。sigevent
返回值
如果成功,mq_notify返回0。
如果失败,mq_notify返回-1,并设置errno以指示错误。


3.5 mq_close

关闭消息队列的访问,该消息队列仍然存在,其他进程仍然可以访问

#include <mqueue.h>
int mq_close(mqd_t mqdes);

参数说明
–mqdes:要关闭消息队列的描述符
返回值
若成功,返回0。若失败,返回-1。


3.6 mq_unlink

销毁消息队列,任何在使用该消息队列的进程都将失败。

#include <mqueue.h>
int mq_unlink(const char *name);

参数说明
–mqdes:要关闭消息队列的名字
返回值
若成功,返回0。若失败,返回-1。


3.7 mq_getattr/mq_setattr

#include <mqueue.h>
int mq_setattr(mqd_t mqdes, const struct mq_attr *newattr, struct mq_attr *oldattr);
int mq_getattr(mqd_t mqdes, struct mq_attr *attr);

返回值
若成功,返回0。若失败,返回-1


4. 代码示例

4.1 不同进程间通信

生产者

//A.cpp
// g++ A.cpp -o A.exe -lrt
#include <mqueue.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>

typedef int mqd_t;

int main()
{
    // 设置消息队列属性
    struct mq_attr attr;
    attr.mq_flags = 0;
    attr.mq_maxmsg = 10;
    attr.mq_msgsize = 100;
    attr.mq_curmsgs = 0;
    mq_unlink("/test");//先删除
    mqd_t mqd = mq_open("/test", O_CREAT|O_RDWR, S_IRWXU, &attr);
    if(mqd == -1)
    {
        printf("mq_open fail! Error: %s\n", strerror(errno));
        return -1;

    }

    for(int i = 0; i < 5; i++)
    {
        char buf[] = "HELLO";
        if(-1 == mq_send(mqd, buf, sizeof(buf), i))
        {
            printf("mq_send() fail!\n");
        }
        else
        {
            printf("mq_send() succ!\n");
        }
    }

    sleep(10);
    // 发送最后一条消息
    char buf[] = "last notice";
    mq_send(mqd, buf, sizeof(buf), 0);

    if(-1 == mq_close(mqd))
    {
        return -1;
    }
    sleep(10);
    if(-1 == mq_unlink("/test"))
    {
        return -1;
    }
    return 0;
}

使用者

//B.cpp
#include <mqueue.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>

typedef int mqd_t;

int main()
{
    struct mq_attr attr;
    attr.mq_flags = 0;
    attr.mq_maxmsg = 10;
    attr.mq_msgsize = 100;
    attr.mq_curmsgs = 0;
    mqd_t mqd = mq_open("/test", O_RDWR, S_IRWXU, &attr);
    if(mqd == -1)
    {
        printf("mq_open fail! Error: %s\n", strerror(errno));
        return -1;
    }
    char buf[128];

    for(int i = 0; i < 6; i++)
    {
        memset(buf, 0, sizeof(buf));
        if(-1 == mq_receive(mqd, buf, sizeof(buf), NULL))
        {
            printf("mq_receive fail! Error:%s\n", strerror(errno));
        }
        else
        {
            printf("mq_receive succ:%s\n", buf);
        }
    }

    if(-1 == mq_close(mqd))
    {
        return -1;
    }
    return 0;
}

4.2 同一进程不同线程间通知

同一进程内的线程‌共享文件描述符表‌,包括消息队列描述符(mqd_t)。任意线程调用 mq_open 打开队列后,其他线程可直接使用该描述符进行读写操作,无需重复打开。

#include <stdio.h>
#include <stdlib.h>
#include <mqueue.h>
#include <string.h>
#include <pthread.h>
 
#define QUEUE_NAME "/my_queue"
#define MAX_MSG 1024
 
void* thread_function(void* arg) {
    mqd_t mq;
    char buffer[MAX_MSG];
    struct mq_attr attr;
    attr.mq_flags = 0;
    attr.mq_maxmsg = 10;  // 最大消息数
    attr.mq_msgsize = MAX_MSG;  // 每个消息的最大长度
    attr.mq_curmsgs = 0;
 
    // 打开或创建消息队列
    mq = mq_open(QUEUE_NAME, O_CREAT | O_RDWR, 0644, &attr);
    if (mq == (mqd_t)-1) {
        perror("mq_open");
        exit(1);
    }
 
    // 发送消息
    const char* message = "Hello from thread!";
    if (mq_send(mq, message, strlen(message) + 1, 0) == -1) {
        perror("mq_send");
        exit(1);
    }
    printf("Thread sent message: %s\n", message);
 
    // 关闭消息队列(但不删除)
    mq_close(mq);
    return NULL;
}
 
int main() {
    pthread_t thread_id;
    int ret;
    mqd_t mq;
    char buffer[MAX_MSG];
    struct mq_attr attr;
    attr.mq_flags = 0;
    attr.mq_maxmsg = 10;  // 最大消息数
    attr.mq_msgsize = MAX_MSG;  // 每个消息的最大长度
    attr.mq_curmsgs = 0;
 
    // 创建或打开消息队列(与线程中使用相同的名称)
    mq = mq_open(QUEUE_NAME, O_CREAT | O_RDWR, 0644, &attr);
    if (mq == (mqd_t)-1) {
        perror("mq_open");
        exit(1);
    }
 
    // 创建线程
    ret = pthread_create(&thread_id, NULL, thread_function, NULL);
    if (ret != 0) {
        perror("pthread_create");
        exit(1);
    }
 
    // 在主线程中接收消息
    if (mq_receive(mq, buffer, MAX_MSG, NULL) == -1) {
        perror("mq_receive");
        exit(1);
    } else {
        printf("Main thread received message: %s\n", buffer);
    }
 
    // 等待线程结束
    pthread_join(thread_id, NULL);
    // 关闭并删除消息队列(只在主线程中操作)
    mq_close(mq);
    mq_unlink(QUEUE_NAME);
    return 0;
}

5. 接收方如何被动接收消息

5.1 循环读

5.2 select

5.3 mq_notify

5.3.1 使用新线程的方式

#include <stdio.h>
#include <mqueue.h>
#include <signal.h>
#include <string.h>
#include <fcntl.h>
#include <pthread.h>
#include <sys/stat.h>
 
#define QUEUE_NAME "/test_queue"
#define MAX_MSG_NUM 10 //受系统变量影响,不可超过系统变量值
#define MAX_MSG_LEN 1024
 
// 信号处理函数
void signal_handler(union sigval sv) {
    mqd_t mq = (mqd_t)sv.sival_int;
    char buffer[MAX_MSG];
    ssize_t bytes_read;
    struct mq_attr attr;
 
    // 获取队列属性以确定消息大小
    if (mq_getattr(mq, &attr) == -1) {
        perror("mq_getattr");
        mq_close(mq);
        return;
    }
 	for(size_t i =0; i < attr.mq_curmsgs; ++i)
 	{
	    // 读取消息
	    bytes_read = mq_receive(mq, buffer, attr.mq_msgsize, NULL);
	    if (bytes_read == -1) {
	        perror("mq_receive");
	    } else {
	        printf("Received: %s\n", buffer);
	    }
	}
 	struct sigevent sev;
 	sev.sigev_notify = SIG_THREAD;
 	sev.sigev_value.sival_int = mq;
 	sev.sigev_notify_function = sigal_handler;
    // 重新注册通知,以便下次消息到达时能够再次收到通知
    if (mq_notify(mq, &sev) == -1) {
        perror("mq_notify");
        return;
    }
}

void* producer_thread(void *args)
{
	mqd_t mq;
	struct mq_attr attr;
    attr.mq_flags = 0;
    attr.mq_maxmsg = MAX_MSG_NUM;  // 最大消息数
    attr.mq_msgsize = MAX_MSG_LEN;  // 每个消息的最大长度
    attr.mq_curmsgs = 0;  // 当前消息数,不关心此属性在创建时设置什么值

	mq = mq_open(QUEUE_NAME, O_WAONLY|OWONLY|O_NONBLOCK, &attr)
	if(mq < 0)
	{
		perror("mq_open error!");
	}
	char message[MAX_MSG_LEN];
	for(int i=0;i<6;++i)
	{
		sprintf(message, "message%d", i);
		if(mq_send(mq,message,strlen(message)+1, 0) == -1)
		{
			perror("mq_send");
			continue;
		}
		printf("son thread send message:%s", message);
	}
	mq_close(mq);
}
 
int main() {
    struct sigevent ev;
    mqd_t mq;
    struct mq_attr attr;
 
    // 创建或打开消息队列
    attr.mq_flags = 0;
    attr.mq_maxmsg = MAX_MSG_NUM;  // 最大消息数
    attr.mq_msgsize = MAX_MSG_LEN;  // 每个消息的最大长度
    attr.mq_curmsgs = 0;  // 当前消息数,不关心此属性在创建时设置什么值
    mq = mq_open(QUEUE_NAME, O_CREAT | O_RDONLY, 0644, &attr);
    if (mq == (mqd_t)-1) {
        perror("mq_open");
        return 1;
    }
 
    // 设置信号处理函数和信号事件,以便在收到通知时调用该函数处理消息
    ev.sigev_notify = SIGEV_THREAD;  // 使用信号通知方式
    ev.sigev_value.sival_int = mq;  // 传递消息队列描述符到信号处理函数中(非必须)
    ev.sigev_notify_function = signal_handler; // 使用信号而非回调函数,因为我们已经在信号处理函数中处理了队列操作。
    ev.sigev_notify_attributes = NULL; // 使用默认线程属性(非必须)
  
    // 注册通知
    if (mq_notify(mq, &ev) == -1) {
        perror("mq_notify");
        mq_close(mq);
        return 1;
    }
  	pthread_t pthread_id;
  	if(0 != pthread_create(&pthread_id, NULL, producer_thread_func, NULL))
  	{
  		perror("pthread_create");
  	}
  	pthread_join(thread_id, NULL);
    
  
    // 主线程可以做其他事情,例如等待或执行其他任务。信号处理函数将负责读取和处理消息。  
  getchar(); // 让程序持续运行,直到用户输入。实际使用时
  mq_close(mq);
  mq_unlink(QUEUE_NAME)
  return 0;
  }

5.3.2 使用信号的方式

有关信号基础请看

#include <stdio.h>
#include <mqueue.h>
#include <signal.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
 
#define QUEUE_NAME "/test_queue"
#define MAX_MSG 1024
 
// 信号处理函数
void signal_handler(int signum) {
    mqd_t mq;
    char buffer[MAX_MSG];
    ssize_t bytes_read;
    struct mq_attr attr;
 
    mq = mq_open(QUEUE_NAME, O_RDONLY);
    if (mq == (mqd_t)-1) {
        perror("mq_open");
        return;
    }
 
    // 获取队列属性以确定消息大小
    if (mq_getattr(mq, &attr) == -1) {
        perror("mq_getattr");
        mq_close(mq);
        return;
    }
 
    // 读取消息
    bytes_read = mq_receive(mq, buffer, attr.mq_msgsize, NULL);
    if (bytes_read == -1) {
        perror("mq_receive");
    } else {
        printf("Received: %s\n", buffer);
    }
 
    // 重新注册通知,以便下次消息到达时能够再次收到通知
    if (mq_notify(mq, NULL) == -1) {
        perror("mq_notify");
    }
 
    mq_close(mq);
}
 
int main() {
    struct sigevent ev;
    mqd_t mq;
    struct mq_attr attr;
 
    // 创建或打开消息队列
    attr.mq_flags = 0;
    attr.mq_maxmsg = 10;  // 最大消息数
    attr.mq_msgsize = MAX_MSG;  // 每个消息的最大长度
    attr.mq_curmsgs = 0;  // 当前消息数,不关心此属性在创建时设置什么值
    mq = mq_open(QUEUE_NAME, O_CREAT | O_RDONLY, 0644, &attr);
    if (mq == (mqd_t)-1) {
        perror("mq_open");
        return 1;
    }
 
    // 设置信号处理函数和信号事件,以便在收到通知时调用该函数处理消息
    ev.sigev_notify = SIGEV_SIGNAL;  // 使用信号通知方式
    ev.sigev_signo = SIGUSR1;        // 使用SIGUSR1信号进行通知
    ev.sigev_value.sival_ptr = &mq;  // 传递消息队列描述符到信号处理函数中(非必须)
    ev.sigev_notify_function = NULL; // 使用信号而非回调函数,因为我们已经在信号处理函数中处理了队列操作。
    ev.sigev_notify_attributes = NULL; // 使用默认线程属性(非必须)
    if (sigaction(SIGUSR1, &(struct sigaction){ .sa_handler = signal_handler }, NULL) == -1) {
        perror("sigaction");
        mq_close(mq);
        return 1;
    }
  
    // 注册通知,以便在队列中有消息时收到SIGUSR1信号。这里传递NULL表示使用默认的SIGEV_SIGNAL。 
    if (mq_notify(mq, &ev) == -1) {
        perror("mq_notify");
        mq_close(mq);
        return 1;
    }
  
    printf("Waiting for messages...\n");  // 主线程等待接收和处理消息,实际上通过信号处理函数处理。
  
    // 主线程可以做其他事情,例如等待或执行其他任务。信号处理函数将负责读取和处理消息。  
  getchar(); // 让程序持续运行,直到用户输入。实际使用时

6. SYSTEM_V和POSIX的差异

‌System V和POSIX的主要差异在于它们的设计目标、实现方式、性能、可靠性、以及应用场景。‌

  1. 设计目标和实现方式‌:
  • System V IPC(Inter-Process Communication,进程间通信)主要由贝尔实验室和BSD开发,侧重于进程之间的通信手段的改进,基于内核实现。它提供了信号量、消息队列和共享内存等机制,用于在进程间进行通信和同步‌1。
  • POSIX(Portable Operating System Interface,可移植操作系统接口)则是由IEEE制定的标准,旨在为运行在不同操作系统上的软件提供统一的接口。POSIX的实现由不同的操作系统内核开发人员完成,旨在提高系统的可移植性‌1。
  1. 性能和可靠性‌:
  • 在性能方面,POSIX在无竞争条件下不会陷入内核,而System V无论何时都要陷入内核,因此在性能上稍逊一筹‌1。
  • 在可靠性方面,POSIX的sem_wait函数成功获取信号量后,如果进程意外终止,将无法释放信号量。而System V提供了SEM_UNDO选项来解决这个问题,因此后者更加可靠‌1。
  1. 应用场景‌:
  • System V的应用更为广泛,尤其是在一些较旧的操作系统或未实现POSIX标准的系统中。然而,考虑到可移植性,POSIX成为了一个趋势,尤其在需要跨平台兼容的应用中更为常见‌1。
  • 在进程间通信和同步方面,POSIX的使用似乎更为普遍,尽管在共享内存方面,System V仍然占据主流‌12。

总的来说,System V和POSIX各有优势和适用场景。System V提供了更广泛的兼容性和对旧系统的支持,而POSIX则以其标准化的接口和高可移植性受到青睐。选择使用哪种机制取决于具体的应用需求和目标平台的特性‌12。

7. 遇到的错误

遇到错误可使用strerror打印

#include <errno.h>
#include <string.h>

5.1 Message too long

可能原因

  1. 接收缓存buf比实际消息队列内容短
  2. 接收缓存buf比消息队列属性中的mq_msgsize小,即使缓冲区长度>实际消息长度
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值