首页 > 编程语言 >2024-02-27-物联网系统编程(6-消息队列)

2024-02-27-物联网系统编程(6-消息队列)

时间:2024-02-27 10:55:18浏览次数:24  
标签:02 IPC 27 队列 msqid 2024 int 消息 include

6.消息队列

6.1 IPC对象

​ 除了最原始的进程间通信方式信号、无名管道和有名管道外,还有三种进程间通信方式,这三种方式称之为IPC对象: 消息队列、共享内存、信号灯集

​ IPC对象也是在内核空间开辟区域,每一种IPC对象创建好之后都会将其设置为全局,并且会给其分配一个编号,只要找到唯一的这个编号就可以进行通信,所以不相关的进程可以通过IPC对象通信。

IPC对象创建好之后,会在当前系统中可见,只要不删除或者不关闭系统,就会一直存在

查看系统中已经创建的IPC

ipcs    # 查看当前系统中所有创建的IPC对象
ipcs -q # 查看消息队列
ipcs -m # 查看创建的共享内存
ipcs -s # 查看信号量

image-20240227084001931

ipcrm # 删除ipc对象
如
ipcrm -q msqid # 删除标号为msqid的消息队列

6.2 消息队列概述

  1. 消息队列中的消息是有类型的
  2. 消息队列中的消息是有格式的
  3. 消息队列可以实现消息的随机查询,消息不一定要以先进先出的次序读取,编程时可以按消息的类型读取
  4. 消息队列允许一个或多个进程向它写入或者读取消息
  5. 与无名管道、命名管道一样,从消息队列中读出消息,消息队列中对应的数据都会被删除
  6. 每个消息队列都有消息队列标识符,消息队列的标识符在整个系统中是唯一的。
  7. 只有内核重启或人工删除消息队列时,该消息队列才会被删除。若不人工删除消息队列,消息队列会一直存在于系统中。

SystemV提供的 IPC 通信机制需要一个key 值,通过key植就可在系统内获得一个唯一的消息队列标识符

key 值可以人为指定,也可以通过 ftok 函数获得。

6.2.1 ftok函数

#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok(const char *pathname, int proj id);
功能:获得项目相关的唯一的 IPC 键值;
参数:
	路径名pathname:
	prej_id:项目 ID,非0整数(只有低 8 位有效),范围 0 ~ 127;
返回值:
	成功:返回 key 值;
	失败:返回 -1
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include  <stdlib.h>

int main(int argc, char const *argv[])
{
    key_t mykey;
    if ((mykey = ftok(".", 100))== -1)
    {
        perror("fail to ftok");
        exit(1);
    }
    printf("mykey = %#x\n",mykey);
    
    return 0;
}

输出结果

mykey = 0x640516aa

6.3 消息队列的操作

6.3.1 创建消息队列 - msgget() 函数

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

int msgget(key_t key, int msgflg);
功能:创建一个消息队列,得到消息队列的id;
参数:
	key:键值,唯一的键值确定唯一的消息队列方法
        1:任意指定一个数
        2:使用ftok函数获取键值;
msgflg: 消息队列的访问权限,一般设置为 IPC_CREAT|IPC_EXCL|0777 或者 IPC_CREAT  0777
返回值:
	成功:消息队列的id;
    失败:返回 -1;
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char const *argv[])
{
    key_t mykey;
    if ((mykey = ftok(".", 100)) == -1)
    {
        perror("fail to ftok");
        exit(1);
    }
    printf("mykey = %#x\n", mykey);

    int msqid;
    if ((msqid = msgget(mykey, IPC_CREAT | 0666)) == -1)
    {
        perror("fail to msgget");
        exit(1);
    }

    printf("msqid = %d\n", msqid);

    return 0;
}

输出结果

mykey = 0x640516aa
msqid = 0

image-20240227090946435

6.3.2 发送信息 - msgsnd() 函数

#include<sys/msg. h>
int msgsnd(int msgid, const void *msgp,size_t msgsz, int msgflg);
功能:
	将新消息添加到消息队列;
参数:
	msgid:消息队列的标识符;
	msgp:待发送消息结构体的地址,需要写入的数据,自己定义结构体;
		struct struct_name{
            long mtype; // 消息编号,必须大于0
            char mtext[128]; // 消息正文,可以定义多个成员
        }
	msgsz: 消息正文的字节数,不包括消息编号的长度;
	msgflg: 函数的控制属性
		0: msgsnd,调用阻塞直到条件满足为止。
		IPC_NOWAIT: 非阻塞,若消息没有立即发送则调用该函数的进程会立即返回。
返回值:
	成功: 0;
	失败: 返回-1。
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <stdlib.h>

typedef struct
{
    // 第一个位置必须是long,代表消息类型
    long msg_type;
    char msg_text[128]; // 消息正文,可以多个成员并且类型也可以随意
} MSG;

// 定义消息正文的大小
#define MSGTEXT_SIZE (sizeof(MSG) - sizeof(long))

int main(int argc, char const *argv[])
{
    key_t mykey;
    if ((mykey = ftok(".", 100)) == -1)
    {
        perror("fail to ftok");
        exit(1);
    }
    printf("mykey = %#x\n", mykey);

    int msqid;
    if ((msqid = msgget(mykey, IPC_CREAT | 0666)) == -1)
    {
        perror("fail to msgget");
        exit(1);
    }

    printf("msqid = %d\n", msqid);
    system("ipcs -q");

    // 使用msgsnd向消息队列中写入数据
    MSG msg1 = {1, "hello world"};
    MSG msg2 = {1, "i love c"};
    MSG msg3 = {1, "i love hello kitty"};
    if ((msgsnd(msqid, &msg1, MSGTEXT_SIZE, 0)) == -1)
    {
        perror("fail to send message");
    }
    system("ipcs -q");
    if ((msgsnd(msqid, &msg2, MSGTEXT_SIZE, 0)) == -1)
    {
        perror("fail to send message");
    }
    if ((msgsnd(msqid, &msg3, MSGTEXT_SIZE, 0)) == -1)
    {
        perror("fail to send message");
    }
    system("ipcs -q");
    return 0;
}

输出结果

mykey = 0x640516aa
msqid = 2

--------- 消息队列 -----------
键        msqid      拥有者  权限     已用字节数 消息      
0x640516aa 2          spider     666        0            0           


--------- 消息队列 -----------
键        msqid      拥有者  权限     已用字节数 消息      
0x640516aa 2          spider     666        128          1           


--------- 消息队列 -----------
键        msqid      拥有者  权限     已用字节数 消息      
0x640516aa 2          spider     666        384          3  

image-20240227093933369

注意:消息队列结束后,消息仍然存在于消息队列中,而如果是管道,就会消失。

6.3.3 接收数据 - msgrev() 函数

#include <sys/msg.h>
ssize_t msgrcv(int msgid, void *msgp,size_t msgsz, long msgtyp, int msgflg)
功能: 从标识符为msgid的消息队列中接收一个消息。一旦接收消息成功,则消息在消息队列中被删除;
参数:
	msgid: 消息队列的标识符,代表要从哪个消息列中获取消息。
	msgp: 存放消息结构体的地址。
	msgsz: 消息正文的字节数。
msgtyp: 消息的类型、可以有以下几种类型
	msgtyp = 0: 返回队列中的第一个消息
	msgtyp > 0: 返回队列中消息类型为msgtyp的消息
	msgtyp < 0: 返回队列中消息类型值小于或等于 msgtyp绝对值的消息,如果这种消息有若干个,则取类型值最小的消息。
注意:
	若消息队列中有多种类型的消息,msscv,获取消息的时候按消息类型获取,不是先进先出的。
	在获取某类型消息的时候,若队列中有多条此类型的消息,则获取最先添加的消息,即先进先出原则。
msgflg:函数的控制属性
	0: msgrev 调用阻塞直到接收消息成功为止。
	MSG_NOERROR:若返回的消息字节数比 nbytes字节数多,则消息就会截短到nbytes字节,且不通知消息发送进程。
	IPC NOWAIT:调用进程会立即返回。若没有收到消息则立即返回-1.
返回值:
	成功: 返回读取消息的长度;
	失败: 返回-1 
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <stdlib.h>

typedef struct
{
    // 第一个位置必须是long,代表消息类型
    long msg_type;
    char msg_text[128]; // 消息正文,可以多个成员并且类型也可以随意
} MSG;

// 定义消息正文的大小
#define MSGTEXT_SIZE (sizeof(MSG) - sizeof(long))

int main(int argc, char const *argv[])
{
    key_t mykey;
    if ((mykey = ftok(".", 100)) == -1)
    {
        perror("fail to ftok");
        exit(1);
    }
    printf("mykey = %#x\n", mykey);

    int msqid;
    if ((msqid = msgget(mykey, IPC_CREAT | 0666)) == -1)
    {
        perror("fail to msgget");
        exit(1);
    }

    printf("msqid = %d\n", msqid);
    system("ipcs -q");

    // // 使用msgsnd向消息队列中写入数据
    // MSG msg1 = {1, "hello world"};
    // MSG msg2 = {1, "i love c"};
    // MSG msg3 = {1, "i love hello kitty"};
    // if ((msgsnd(msqid, &msg1, MSGTEXT_SIZE, 0)) == -1)
    // {
    //     perror("fail to send message");
    // }
    // system("ipcs -q");
    // if ((msgsnd(msqid, &msg2, MSGTEXT_SIZE, 0)) == -1)
    // {
    //     perror("fail to send message");
    // }
    // if ((msgsnd(msqid, &msg3, MSGTEXT_SIZE, 0)) == -1)
    // {
    //     perror("fail to send message");
    // }
    MSG msg;
    // 如果第四个数据位0,则按照先进先出的方式读取数据
    // 如果第四个数据大于0,则获取当前值的消息类型相同的数据
    // 如果第四个参数小于0,则获取当前值的绝对值内消息类型最小的数据
    if (msgrcv(msqid, &msg, MSGTEXT_SIZE, 1, 0) == -1)
    {
        perror("fail to msgrcv");
        exit(1);
    }
    printf("recv_msg = %s\n", msg.msg_text);
    system("ipcs -q");

    return 0;
}

输出结果

mykey = 0x640516aa
msqid = 2

--------- 消息队列 -----------
键        msqid      拥有者  权限     已用字节数 消息      
0x640516aa 2          spider     666        384          3           

recv_msg = hello world

--------- 消息队列 -----------
键        msqid      拥有者  权限     已用字节数 消息      
0x640516aa 2          spider     666        256          2     

6.3.4 消息队列的控制 - msgctl() 函数

#include<sys/msg. h>
int msgetl(int msqid int cmd, struct msqid_ds *buf)
功能: 对消息队列进行各种控制,如修改消息队列的属性,或删除消息消息队列;
参数:
	msqid: 消息队列的标识符
	cmd: 函数功能的控制
        IPC_RMID: 删除由 msqid 指示的消息队列,将它从系统中删除并破坏相关数据结构;
        IPC_STAT: 将 msqid 相关的数据结构中各个元素的当前值存入到由buf指向的结构中;
        IPC_SET:  将 msqid 相关的数据结构中的元素设置为由 buf指向的结构中的对应值。
	buf: msqid_ds,数据类型的地址,用来存放或更改消息队列的属性

返回值:
       成功:返回0
       失败:返回-1
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char const *argv[])
{
    key_t key;
    if ((key = ftok(".", 20)) == -1)
    {
        perror("fail to ftok");
        exit(1);
    }
    printf("key = %#x\n", key);

    int msgid;
    if ((msgid = msgget(key, IPC_CREAT | 0777)) == -1)
    {
        perror("fail to msgget");
        exit(1);
    }

    printf("msgid = %d", msgid);
    system("ipcs -q");

    //  通过msgctl函数删除消息队列
    if (msgctl(msgid, IPC_RMID, NULL) == -1)
    {
        perror("fail to msgctl");
        exit(1);
    }

    system("ipcs -q");
    return 0;
}

输出结果

key = 0x140516aa

--------- 消息队列 -----------
键        msqid      拥有者  权限     已用字节数 消息      
0x640516aa 2          spider     666        256          2           
0x140516aa 3          spider     777        0            0           


--------- 消息队列 -----------
键        msqid      拥有者  权限     已用字节数 消息      
0x640516aa 2          spider     666        256          2    

标签:02,IPC,27,队列,msqid,2024,int,消息,include
From: https://www.cnblogs.com/hasaki-yasuo/p/18036428

相关文章

  • 2.27每日总结
    今天课上学习了软件测试技术,对软件测试技术有了简单的了解。软件测试技术是一门涉及软件质量保证和验证的课程,旨在教授学生如何设计、执行和评估软件测试方案以确保软件系统的质量。这门课程通常涵盖以下内容:软件测试基础知识:介绍软件测试的基本概念、原则和方法论,包括白盒测......
  • 2024 省选复习 (updating)
    前言快省选了,在复习,但是不知道干什么。所以就写点东西吧。就是瞎写写,所以可能有很多错误,如果发现了欢迎指出。常见错误&注意事项数组不能开大,也不能开小题目要求什么千万不能读错,最好手算一下样例算法复习树状数组进阶P6619原本是树状数组二分的模板题,但是用......
  • 2023 csp游记
    省流:XJ小丑,挂分笑话初赛前一天晚上2023年9月15日学了一会数学,学傻了,还是题题不会,希望明天初赛能有个好成绩。看到了一些与我年龄相同但爆杀我的人,觉得自己太菜了。初赛当天2023年9月16日早上6:30起来了,觉得稍微有点困,稍微复习了一会,吃了个早饭就出发去海创了。海创很大,门......
  • 蒟蒻的2023NOIP游记(非正式)
    前言:这是篇blog这周集中打模拟赛的记录,后会和NOIP场外游记并在一起。11/11双十一,打了两场共同体NOIP模拟赛157:55左右开题t1,t2,t3,t4看了一眼,觉得t1,t2可做想t1,到8:40想出了做法(赛后看来离正解挺近的),9:30左右写好。对于一张n个点的图,由菊花图可想到,应该是对半开。......
  • 20240226
    非常意识流的日记,精神状态极度不佳下打出来的。模拟赛垫底,不过是意料之中的,没造成太大影响。下午也很正常,一直在硬刚Border,不过有些微疲倦。晚自习就开始颓废了,不想学习。然后下去散步的时候唐了,成小丑了,破防了。当时看到青蛙的博客时真正体会到了什么是「整个人都麻了」的......
  • 【专题】2023年金融、保险、银行行业报告汇总PDF合集分享(附原数据表)
    原文链接:https://tecdat.cn/?p=35149原文出处:拓端数据部落公众号自中国提出双碳目标以来,可持续金融市场呈现出蓬勃发展的态势。这一发展趋势在多年来得到可持续金融战略咨询团队的支持和推动。同时,数字化转型的深入推进推动了新客户的增长,而中国的碳金融创新也成为市场关注的焦......
  • 2024.2.26
    前言还有\(4\)天就结束了呜呜呜,我还不想走,我还没打过国赛呜呜呜。博弈以为是个吊题,结果真是签到题啊QAQ。首先我们要读明白题,我们一个点可以放多个棋子,所以可以得出一个结论:每个点是互不影响的。所以我们可以每个点分开来算。正如题解所说:“因为在自己所属点上的棋子是完......
  • 近期总结 2024.2.26
    dp专场*2。CF1608FMEXCounting题意:给出\(n,m,b_{1...n}\),求出有多少个长度为\(n\)的序列\(a\)满足\(\foralli\in[1,n],\space0\lea_i\len\)且\(|\operatorname{mex}\{a_1,a_2,...,a_i\}-b_i|\lem\)。\(1\len\le2000,\space1\lek\le50\)很简单的......
  • NOI 2024省选OIFC模拟21 蒲巴巴 超繁做法
    题目描述一年一度的PuBaBa杯开始了!今年的PuBaBa杯总共有\(n\)个选手来参加,编号分别为\(1,2,\cdots,n\),他们的水平按编号依次递增,所以他们过的题目数量单调不降。作为本场比赛的出题人,PuBaBa总共出了\(m\)道题,他希望第\(i\)道题至少有\(l_i\)个选手通过,至多有\(......
  • 2024.2.25模拟赛T3题解
    题目推出dp柿子之后,枚举\(i\)的时候用线段树维护\(1-i\)的\(mex\)段,对于每一段,分别使用线段树套李超树维护,对于每个\(mex\)再次使用线段树套李超树维护即可code#include<bits/stdc++.h>usingnamespacestd;#defineN600005#defineintlonglongintn,m;consti......