Linux多进程通信(4)——消息队列从入门到实战!

Linux多进程通信总结——进程间通信看这一篇足够啦!

1.基本介绍

1)消息队列的本质其实是一个内核提供的链表,内核基于这个链表,实现了一个数据结构,向消息队列中写数据,实际上是向这个数据结构中插入一个新结点;从消息队列汇总读数据,实际上是从这个数据结构中删除一个结点
2)消息队列独立于发送与接收进程,进程终止时,消息队列中的内容不会被删除,所以要记得删除消息队列
3)消息队列可以实现消息的
随机查询
消息不一定要以先进先出的次序读取,也可以按照消息的类型读取
4)Linux环境中,最多有256个消息队列,每个消息最大为8K字节,总大小不能超过16K,否则在send时会阻塞,可以通过更改内核设置的方式更改大小。
image.png

2.API介绍

1)获取消息队列键值

#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok(char *pathname, char proj);

pathname:路径名,是必须存在的,ftok只是根据文件inode在系统内的唯一性来取一个数值,和文件的权限无关。
proj:1-255之间的数字
返回值: 生成一个独有的数,失败则返回-1
key 31-24 proj_id 低8位
key 23-16 pathname的st_dev属性的低8位
key 15-0 pathname的st_ino属性的低16位32位组合而成一个int值,就是我们的ftok的返回值了
根据路径名以及数字,合成系统中唯一的Key值

2)创建/获取消息队列

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgget(key_t key, int msgflg);

key:使用ftok获取到的唯一Key值
msgflag: 和其他的IPC通信一样,IPC_CREAT,如果消息队列对象不存在,则创建之,否则则进行打开操作。IPC_EXCL,如果消息对象不存在则创建之,否则产生一个错误并返回,可同时用IPC_CREATE | 0666
返回值:成功返回消息队列ID,失败则返回-1

3)msgsend发送消息

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

msgid :消息队列ID
msgptr:消息体,消息体的类型是一个结构体

msg_sz: 消息长度 消息体中的消息长度(不是一整个结构体的长度,只是mtext消息数据的长度
msgflag :发送flag 一般设置为0,阻塞式等待(消息队列满),也可以设置为 IPC_NOWAIT,则立刻返回
返回值:成功返回0,失败返回-1
调用成功后,发送内容的一个备份,会被放到消息队列中(这里会有个硬拷贝的过程)

4)msgrecv接收消息

ssize_t msgrcv(int msgid, const void *msgp, size_t msgsz, 
                                       int msgtype, int msgflag)

msgid:消息队列ID
msgp: 接收到的消息体
msgsz: 接收消息的长度
msgtype :消息类型 ,对应发送时的m_type。
值为0:代表接收一个任意类型的消息
值大于0:获取消息类型为msgtype的第一个消息
值小于0:获取消息类型小于等于msgtype的第一个消息
msgflag :接收的flag一般设置为0,阻塞式等待
msgflg=IPC_NOWAIT,队列没有可读消息不等待,返回ENOMSG错误。
msgflg=MSG_NOERROR,消息大小超过msgsz时被截断
msgtype>0且msgflg=MSC_EXCEPT,接收类型不等于msgtype的第一条消息。
返回值:调用成功时,返回接收到消息的字节数,失败返回-1

调用成功时消息被复制到由msg_ptr指向的用户分配的缓存区中,然后删除消息队列中的对应消息(这里会有个硬拷贝的过程)

5)控制消息队列

int msgctl(int msqid, int cmd, struct msqid_ds *buf);

msgid:消息队列id
cmd:控制消息队列的命令选项
IPC_STAT:把msgid_ds结构中的数据设置为消息队列的当前关联值,即用消息队列的当前关联值覆盖msgid_ds的值。
IPC_SET:如果进程有足够的权限,就把消息列队的当前关联值设置为msgid_ds结构中给出的值
IPC_RMID:删除消息队列

其实对于IPC通信来说,这些基本都是通用的~

3.例程

1)服务端

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <pthread.h>

#define MSGQ_PATH "./"
#define MSGQ_PRI 100

typedef struct {
    long mtype;
    char mtext[256];
} MSGQ_T;

int iMsgId = 0;

int main(int argc, char **argv)
{
    key_t msgkey = 0;
    msgkey = ftok(MSGQ_PATH, MSGQ_PRI);
    if (msgkey == -1)
    {
        printf("ftok error, pid:%d\n", getpid());
        return -1;
    }

    iMsgId = msgget(msgkey, IPC_CREAT | 0644);
    if (iMsgId == -1)
    {
        printf("msgget error, pid:%d\n", getpid());
        return -1;
    }
    printf("msgget success, pid:%d, key:%d, msgid:%d\n", getpid(), msgkey, iMsgId);
    
    while (1)
    {
        MSGQ_T msgBuf = {0};

        int iMsgType = 0;
        printf("input message type:\n");//输入消息类型
        scanf("%d", &iMsgType);
        if (iMsgType == 0)
        {
            printf("message type 0, break\n");
            break;
        }
    
        char acBuf[256] = {0};
        printf("input message to be send:\n");//输入消息信息
        scanf("%s", acBuf);

        msgBuf.mtype = iMsgType;
        strncpy(msgBuf.mtext, acBuf, sizeof(msgBuf.mtext) - 1);

        msgsnd(iMsgId, &msgBuf, sizeof(msgBuf), IPC_NOWAIT);

        printf("msgq:%d, send msgtype:%d, msgtext:%s \n", iMsgId, msgBuf.mtype, msgBuf.mtext);
    }

    msgctl(iMsgId, IPC_RMID, NULL);

    return 0;
}

2)客户端

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <pthread.h>

#define MSGQ_PATH "./"
#define MSGQ_PRI 100

typedef struct {
    long mtype;
    char mtext[256];
} MSGQ_T;

int iMsgId = 0;
pthread_t msgqRecvThread = 0; 

void *msgqRecvThreadTask(void *arg)
{
    while (1)
    {
        MSGQ_T msgBuf = {0};
        int ret = msgrcv(iMsgId, &msgBuf, sizeof(msgBuf), 0, 0);
        if (ret < 0)
        {
            return NULL;
        }

        printf("msgq:%d, recv msgtype:%d, msgtext:%s, ret:%d \n", iMsgId, msgBuf.mtype, msgBuf.mtext, ret);
    }

    return NULL;
}

int main(int argc, char **argv)
{
    key_t msgkey = 0;
    msgkey = ftok(MSGQ_PATH, MSGQ_PRI);
    if (msgkey == -1)
    {
        printf("ftok error, pid:%d\n", getpid());
        perror("....");
        return -1;
    }

    iMsgId = msgget(msgkey, IPC_CREAT | 0644);
    if (iMsgId == -1)
    {
        printf("msgget error, pid:%d\n", getpid());
        perror("....");
        return -1;
    }
    printf("msgget success, pid:%d, key:%d, msgid:%d\n", getpid(), msgkey, iMsgId);

    
    pthread_create(&msgqRecvThread, NULL, msgqRecvThreadTask, NULL);
    pthread_join(msgqRecvThread, NULL);

    return 0;
}

可以通过指令分别编译客户端和服务端并执行

gcc server.c -o server -lpthread
gcc client.c -o client -lpthread
./server
./client

在输入框输入消息后,便可以在client观察到消息接收啦
image.png
此时输入ipcs -q后能看到创建的消息队列
image.png
这里我显示有两个消息队列,因为上一个没有用msgctl删除。

当服务端和客户端程序都正常退出时,调用msgctl则会正常删除消息队列,在例程中的server端输入0即可!

  • 20
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值