linux进程间通信-消息队列_linux

消息队列(Message Queue)

消息队列允许不同的进程通过发送和接收消息来交换数据,从而实现进程间的通信。消息队列在系统中对应一个由内核维护的内存空间,本质上是一个先进先出(FIFO)的数据结构。

具体来说,发送进程可以将消息添加到消息队列的尾部,而接收进程则可以从队列的头部获取消息。这种通信方式是异步的,也就是说发送进程和接收进程不需要同时在线或同步操作。发送进程可以在任何时间将消息发送到队列,而接收进程则可以在其方便的时候从队列中检索消息。

消息队列的特点:

  1. 独立于发送和接收进程:消息队列是一种独立于发送和接收进程的数据结构,它在内核中以队列的形式存在。
  2. 消息封装:消息队列中的数据是封装为消息的形式进行传输的,每个消息都有明确的边界。
  3. 固定或可变长度:消息队列支持固定长度或可变长度的消息,每个消息都是独立的数据单元。
  4. 竞争消费与多接收:消息队列默认是“竞争消费”模型,即消息一旦被接收,就会从队列中移除;也可以通过配置消息队列实现消息的“多接收”(multi-receive)或“复制”(copy)模式。在这种模式下,消息在被接收后仍然保留在队列中,其他进程也可以读取这个消息。这允许多个进程可以独立地处理同一份消息,常见于发布/订阅模型。
  5. 消息顺序:消息队列保证了消息的顺序性,即先发送的消息先被接收。
  6. 消息标识:每个消息都有一个与之关联的类型,接收消息时可以根据类型有选择地接收。
  7. 系统资源限制:系统对消息队列的数量和消息的大小有一定的限制。
  8. 复杂的控制:消息队列提供了复杂的控制机制,如消息类型、优先级和消息的非阻塞访问。
  9. 适用场景:适用于需要可靠消息传递、消息顺序重要、或需要异步通信的场景。它们也适用于分布式系统或复杂的应用程序,其中消息的顺序和类型很重要。

使用消息队列的关键函数:

  1. mq_open:打开或创建一个消息队列。
mqd_t mq_open(const char *name, int oflag);

消息队列的名称在整个系统中必须是唯一的。使用 mq_open 时,如果消息队列已经存在,则不会重复创建,但可以修改其属性。使用 O_CREAT 标志创建消息队列时,需要提供权限和初始属性。消息队列的属性包括消息队列的最大消息数、每个消息的最大的值等。

  1. mq_close:关闭一个打开的消息队列描述符。
int mq_close(mqd_t mqdes);
  1. mq_unlink:删除一个消息队列。
int mq_unlink(const char *name);
  1. mq_send:向消息队列发送一个消息。
int mq_send(mqd_t mqdes, const char *msg_ptr, size_t msg_len, unsigned int msg_prio);
  1. mq_receive:从消息队列接收一个消息。
int mq_receive(mqd_t mqdes, char *msg_ptr, size_t msg_len, unsigned int *msg_prio);
  1. mq_getattr:获取消息队列的属性。
int mq_getattr(mqd_t mqdes, struct mq_attr *__restrict attr);
  1. mq_notify:注册一个通知,当消息队列中有消息到达时触发。
int mq_notify(mqd_t mqdes, const struct sigevent *__restrict notification);

Linux消息队列的使用通常涉及几个关键步骤:

  1. 创建或打开队列 :使用 mq_open 函数打开一个现有的消息队列,或者创建一个新的消息队列。
  2. 发送消息 :使用 mq_send 函数向队列发送消息。
  3. 接收消息 :使用 mq_receive 函数从队列接收消息。
  4. 关闭队列 :使用 mq_close 函数关闭消息队列。
  5. 删除队列 (如果需要):使用 mq_unlink 函数删除消息队列。

此外,Linux消息队列在多种场景下都有广泛的应用,例如:

  • 任务调度系统 :主控进程可以将任务信息发送到消息队列,工作者进程从队列中获取并执行任务。
  • 实时数据处理 :数据生产者发送实时数据到消息队列,数据消费者从队列中获取并处理这些数据。
  • 日志记录系统 :日志产生者发送日志信息到消息队列,日志消费者则从队列中获取并记录这些信息。
  • 分布式计算 :在分布式环境中,节点之间可以通过消息队列来通信,实现任务的分发和结果的收集。

总的来说,Linux消息队列提供了一种灵活且高效的进程间通信机制,特别适用于需要在不同进程之间异步传递数据的场景。

消息队列实验:

#include <stdio.h>  
#include <stdlib.h>  
#include <string.h>  
#include <unistd.h>  
#include <fcntl.h>  
#include <sys/stat.h>  
#include <sys/types.h>  
#include <sys/wait.h>
#include <mqueue.h>  
  
#define QUEUE_NAME "/my_message_queue"  
#define MAX_MSG_SIZE 256  
#define MSG_PRIORITY 1

mqd_t create_message_queue() {  
    mqd_t mq;  
    struct mq_attr attr;  
  
    attr.mq_flags = 0;  
    attr.mq_maxmsg = 10;  // 队列中最大的消息数  
    attr.mq_msgsize = MAX_MSG_SIZE;  // 消息的最大长度  
    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);  
    }  
    return mq;  
}

void send_message(mqd_t mq, const char *msg) {  
    if (mq_send(mq, msg, strlen(msg) + 1, MSG_PRIORITY) == -1) {  
        perror("mq_send");  
        exit(1);  
    }  
}

void receive_message(mqd_t mq) {  
    char buffer[MAX_MSG_SIZE];  
    ssize_t bytes_read;  
    unsigned int priority;  
  
    bytes_read = mq_receive(mq, buffer, MAX_MSG_SIZE, &priority);  
    if (bytes_read == -1) {  
        perror("mq_receive");  
        exit(1);  
    }  
    buffer[bytes_read] = '\0';  // 确保字符串正确终止  
    printf("Received message: %s\n", buffer);
}

int main() {  
    pid_t pid1, pid2; 
    mqd_t mq;  
  
    mq = create_message_queue();  
  
    pid1 = fork();  
    if (pid1 == -1) {  
        perror("fork");  
        exit(1);  
    } else if (pid1 == 0) {  
        // 子进程(发送者)  
        send_message(mq, "Hello from sender!");  
        exit(0);  
    }  
  
    pid2 = fork();  
    if (pid2 == -1) {  
        perror("fork");  
        exit(1);  
    } else if (pid2 == 0) {  
        // 另一个子进程(接收者)  
        sleep(1);  // 等待发送者发送消息  
        receive_message(mq);  
        exit(0);  
    }  
  
    // 父进程等待子进程结束  
    waitpid(pid1, NULL, 0);  
    waitpid(pid2, NULL, 0);  
    while(1);
    // 关闭并删除消息队列  
    mq_close(mq);  
    mq_unlink(QUEUE_NAME);  
  
    return 0;  
}


实验结果:

linux进程间通信-消息队列_消息队列_02