进程间通信之消息队列msg queue

消息message是一个格式化的可变长信息单位。消息机制允许一个进程向任何其他进程发送一个消息。本质上,消息队列很像是内核维护的一个信箱,任何进程都可以向内核发送一封信,这个信包括消息的类型(用long型表示),和消息的内容(char型数组)。任何进程通过IPC对象标识符,就可以向指定的信箱发送消息。类似的,任何进程可以通过IPC对象标识符,来从信箱中收取指定类型的消息。这里的任何进程,是指具有访问权限的任何进程。
下面通过一个例子来了解消息队列。
程序server创建消息队列,其他进程可以通过server进程创建的消息队列来进行通信。这时写消息队列的程序send可以发送消息,任何时候,读消息队列的程序receive可以读取消息。
程序server的源代码msgq.c

#include <sys/types.h>
#include <sys/msg.h>
#include <unistd.h>
#include <stdio.h>

int main()
{
    key_t key;
    int msgid;
    int retval;

    printf("my pid is %d\n", getpid());

    key = ftok(".", 'q');
    printf("IPC key = 0x%x\n", key);

    /* create a message queue with the IPC key */
    msgid = msgget(key, IPC_CREAT | 00666);
    if(-1 == msgid)
    {
        perror("msg creat error");
        return;
    }
    else
    {
        printf("Message Queue msqid=%d\n", msgid);
    }

    printf("use \"id\", \"ipcs -q [-i msgid]\" to see more details\n");
    printf("press Enter to remove the message queue...\n");
    getchar();

    /* delete the msg queue */
    retval = msgctl(msgid, IPC_RMID, NULL);
    if(-1 == retval)
    {
        perror("unlink msg queue error");
    }
    else
    {
        printf("msg queue [%d] unlinked OK\n", msgid);
    }
    return;
}

receive.c

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/msg.h>
#include <sys/types.h>
#define LEN 512

int main(int argc, char *argv[])
{
    int sflag, ret;
    struct msgbuf
    {
        int mtype;
        char mtext[LEN];
    }msg_buf;

    /*receive message from msg queue */
    if(2 != argc)
    {
        printf("Usage: %s msqID\n", argv[0]);
        return;
    }

    sflag = IPC_NOWAIT | MSG_NOERROR;
    do
    {
        ret = msgrcv(atoi(argv[1]), &msg_buf, sizeof(msg_buf.mtext), 0, sflag);
        if(-1 == ret)
        {
            perror("exit when type=100. msgrcv error");
            sleep(1);
        }
        else
        {
            printf("pid:%d received [%d] Byte from msqid [%d]\n", \
                    getpid(), ret, atoi(argv[1]));
            printf("content:[%s]\n", msg_buf.mtext);
        }
        if(100 == msg_buf.mtype)
            break;
    }
    while(1);

    return ;
}

send.c

#include <stdio.h>
#include <string.h>
#include <sys/msg.h>
#include <sys/types.h>
#define LEN 512

int main(int argc, char *argv[])
{
    int sflag, ret;
    struct msgbuf
    {
        int mtype;
        char mtext[LEN];
    }msg_buf;


    /*send id message*/
    if(4 != argc)
    {
        /*             0  1     2    3 */
        printf("Usage: %s msqID type message\n", argv[0]);
        return;
    }

    sflag = IPC_NOWAIT;
    msg_buf.mtype = atoi(argv[2]);
    strcpy(&msg_buf.mtext[0], argv[3]);

    ret = msgsnd(atoi(argv[1]), &msg_buf, strlen(msg_buf.mtext), sflag);
    if(-1 == ret)
    {
        perror("msgsnd error");
        return ;
    }
    else
    {
        printf("pid:%d sent msg to msqid [%d] OK\n", getpid(), atoi(argv[1]));
    }

    return ;
}

对应的Makefile

SERVER = server
SEND = s
RECEIVE = r


all:server send receive

server: msgq.c
    gcc msgq.c -o $(SERVER)

send: send.c
    gcc send.c -o $(SEND)

receive: receive.c
    gcc receive.c -o $(RECEIVE)

clean:
    rm -f $(SERVER) $(SEND) $(RECEIVE)

随时的,可以使用命令ipcs -q来查看系统所有的消息队列。
先运行server:

test@test:~$ ./server 
my pid is 11609
IPC key = 0x710400db
Message Queue msqid=163840
use "id", "ipcs -q [-i msgid]" to see more details
press Enter to remove the message queue...

进程server会建立消息队列后会停止运行,其他进程就可以通过导出的msgq id来进行通信。
然后从另一个终端中运行send,就可以发送消息到id为163840的这个消息队列

test@test:~$ ./s 163840 10 hello

这时通过ipcs -q -i 163840就可以看到163840这个消息队列的很多信息,比如现在消息队列中有多少byte数据,最后一个发送消息的进程进程号是多少。
可以再从第三个终端中运行receive,指定从163840这个消息队列中收取消息。

test@test:~$ ./r 163840
pid:11616 received [5] Byte from msqid [163840]
content:[hello]
exit when type=100. message receive error: No message of desired type
exit when type=100. message receive error: No message of desired type
exit when type=100. message receive error: No message of desired type
exit when type=100. message receive error: No message of desired type
exit when type=100. message receive error: No message of desired type
exit when type=100. message receive error: No message of desired type

r进程收不到类型为100的消息就会循环读取。这时可从第二个终端中查看下消息队列的信息,也可以继续发送其他消息。
当发送类型100的消息时,收消息进程满足退出条件就退出了。
再返回第一个终端中敲下回车,释放申请的IPC资源。

<think>好的,我需要给用户介绍一下消息队列实现进程间通信。首先,我得回想一下消息队列的基本概念,然后整理出它的工作原理和特点。接下来,应该分步骤解释它是如何工作的,比如消息的发送、接收以及队列的管理。还要提到它的优势,比如解耦、异步通信、缓冲作用等。可能还需要对比其他进程间通信方式,比如管道、共享内存,说明消息队列的不同之处。要注意用户可能对技术细节不太熟悉,所以需要用简单易懂的语言,避免太专业的术语。同时,按照系统指令,数学公式需要用正确的LaTeX格式,不过这里可能涉及的不多,但如果有涉及计算或算法的地方要正确标注。最后,确保结构清晰,分点说明,让用户能够一步步理解。可能需要检查一下是否有遗漏的重要点,比如消息队列的典型应用场景或者潜在的问题,比如消息丢失的可能性或者性能考虑。总之,要全面但简洁地覆盖消息队列进程间通信中的角色和实现机制。</think>消息队列(Message Queue)是一种实现进程间通信(IPC)的核心技术。它通过**异步通信**和**数据暂存**机制,帮助不同进程(可能位于不同主机)高效、可靠地交换数据。以下是分步解析: --- ### 一、消息队列的核心原理 1. **队列结构** 消息队列本质是一个**先进先出(FIFO)**的缓冲区,由操作系统内核维护。发送方(生产者)将数据封装为消息并存入队列,接收方(消费者)按顺序取出处理。 $$ \text{Producer} \xrightarrow{\text{send(msg)}} \text{Queue} \xrightarrow{\text{receive()}} \text{Consumer} $$ 2. **消息格式** 每条消息包含两部分: - **类型标识**:用于区分不同用途的消息(例如:$type=1$ 表示日志,$type=2$ 表示控制命令) - **数据内容**:可以是文本、二进制数据或结构化数据(如JSON) 3. **通信模式** - **点对点**:一个消息仅被一个消费者处理 - **发布/订阅**:一个消息可被多个订阅者接收 --- ### 二、实现进程间通信的关键步骤 1. **创建或连接队列** 进程通过系统调用(如`msgget()`)创建新队列或连接已有队列,需指定唯一标识符(例如:`key=0x1234`)。 2. **发送消息** ```c msgsnd(queue_id, &message, msg_size, IPC_NOWAIT); ``` - `message`需包含类型和内容 - 若队列满,可选择阻塞等待或立即返回错误 3. **接收消息** ```c msgrcv(queue_id, &buffer, max_size, target_type, 0); ``` - 可按类型精准获取消息(如$target\_type=3$) - 若队列空,进程可阻塞等待 4. **权限与销毁** - 通过`msgctl()`设置访问权限(例如:`0666`) - 显式调用删除接口释放内核资源 --- ### 三、优势与典型场景 1. **核心优势** - **解耦**:生产者和消费者无需同时在线 - **削峰填谷**:突发流量时缓冲压力,避免系统崩溃 - **可靠性**:消息持久化(如RabbitMQ)可防止数据丢失 2. **应用场景** - 微服务间异步调用(如订单支付成功后通知物流系统) - 日志收集(多个服务将日志发送到统一队列) - 任务分发(主节点通过队列分配计算任务) --- ### 四、对比其他IPC方式 | 方式 | 数据格式 | 同步性 | 跨主机支持 | |-------------|----------|----------|--------| | 管道(Pipe) | 字节流 | 同步阻塞 | 不支持 | | 共享内存(SHM) | 原始内存 | 需同步机制 | 不支持 | | 消息队列 | 结构化消息 | 异步非阻塞 | 支持 | --- ### 五、潜在问题与解决方案 1. **消息堆积** - 增加消费者数量 - 设置消息过期时间(TTL) 2. **顺序保证** - 单消费者场景天然有序 - 多消费者时需业务层处理(如版本号) 3. **安全性** - 使用TLS加密通信 - 身份验证机制(如Token) --- 通过消息队列进程间通信变得更灵活可靠,尤其适合分布式系统和高并发场景。实际应用中可选用RabbitMQ、Kafka等成熟中间件简化开发。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

yilonglucky

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值