Linux进程间通信——探索共享内存—— 剖析原理, 学习接口应用

        前言:本节内容主要讲解进程间通信的, systemV版本下的共享内存。 共享内存,顾名思义, 其实就是一块内存, 它不同于管道是一个文件。 所以它的传输速度是很快的。 因为管道是文件,有缓冲区, 而共享内存只是一块内存, 可以直接与进程挂接, 直接拷贝。

        ps:本节内容适合了解一些管道知识的友友们进行观看哦

目录

进程间通信的本质

共享内存的原理

共享内存相关接口

shmget——创建共享内存

shmflg

key

shmat——进程挂接

 ​编辑

shmdt——进程去关联

 指令

ipcs -m

应用

准备文件

准备makefile

Com.hpp

GetKey——封装获取key值的函数

CreatShareMemory——获取共享内存

CreatShm和GetShm——获取共享内存

processa.cpp

processb.cpp

运行程序

key和shimd的区别

共享内存的生命周期


进程间通信的本质

        进程间通信的本质就是——先让不同的进程, 看到同一份资源。(这是为了给通信奠定前提条件)

共享内存的原理

        上面的这一张图, 其实就是叫做共享内存。 因为共享内存的本质, 就是两个进程能够看到同一份资源, 而上面, 就做到了两个进程看到同一份资源。  其中左边是一个进程的PCB, 然后右边是一个进程的PCB。 同时这两个PCB都有它们对应的虚拟地址空间然后物理内存中有一块共享资源, 两个进程都可以通过页表的映射看到这份共享资源。—— 这就是共享内存。 

        对于上面这一张图来说, 我们的第一步就是申请内存。 第二步然后就是将申请好的内存分别挂接到两个进程的进程地址空间, 然后返回我们的地址空间的地址即可。但是这是申请共享内存的流程。 如果我们要释放共享内存呢?

        我们申请是由操作系统在内存中开辟一段地址空间, 然后把虚拟地址到物理地址之间的映射关系在页表中一填, 最后再挂接。 那么共享内存就创建好了。 ——而释放空间内存其实就是反过来, 一定是如何创建的, 那么就如何释放的。 ——所以第一步就是进程和共享内存去关联, 就是将页表之中虚拟地址和物理地址的映射关系去掉 然后就释放共享内存。 

        现在, 有一个问题就是——上面的操作是由进程直接做的吗? 答案是不是的, 进程没有资格直接访问共享内存的, 必须由操作系统来做。 

        那么我们就能看到一个典型的现象。 ——我们的进程需要通信, 然后操作系统就帮进程建立信道。 ——这里进程就是需求方, 而操作系统就是执行方。 进程解决为什么我们要建立共享内存操作系统解决的是如何建立共享内存。 那么就意味着我们的操作系统是必须要给进程提供系统调用!!!

        那么,我们的操作系统当中有着各种通信, 所以一定有着非常多的共享内存, 所以操作系统要不要将共享内存管理起来呢?——答案是需要的, 而管理的方式就是先描述再组织。 也就是说, 一定会有内核数据结构来描述共享内存

共享内存相关接口

shmget——创建共享内存

        上面的shmget函数, 就是创建共享内存的接口。 使用它需要包含sys/ipc.h、sys/shm.h头文件。 同时三个参数, 一个返回值int类型。 ——其中, size和返回值比较好说, size就是我们要创建的共享内存的大小, 返回值int就是共享内存标识符。 key和shmflg是没有办法一下子说清的, 下面我们来谈一谈这两个参数的相关问题:

shmflg

        shmflag是创建的共享内存的模式, 这里说两种常用的使用选项:

        IPC_CREAT(单独使用)如果你申请的共享内存, 不存在, 就创建, 存在就获取并返回。

        IPC_CREAT | IPC_EXCL如果你申请的共享内存不存在, 就创建, 存在就出错返回。 ——这个选项就可以确保如果我们申请内存成功了, 这个内存一定是新的!!!(IPC_EXCL不单独使用!

key

        在谈key之前我们先思考一个问题, 就是对于一块共享内存, 我们如何知道他存不存在呢? 或者说我们怎么保证让不同的进程看到同一个共享内存呢?——带着这两个问题, 我们来谈key:

        1、key是一个数字, 这个数字是几, 不重要, 关键是它必须在内核种具有唯一性, 能够让不同进程进行唯一性标识。

        2、第一个进程可以根据key创建共享内存, 第二个之后的进程, 他们只要拿着同一个key, 就可以和第一个进程看到同一个共享内存了!!!

        3、对于一个创建好的共享内存, key在哪?——这个可以直接挑明, 其实就在共享内存的描述对象中!!!

        4、命名管道是通过路径 + 文件名来确定唯一的管道的!!!而我们的key, key类似于路径, 同样具有唯一性。共享内存就是通过key来确定唯一性!!!

         现在有一个问题, 第一次创建共享内存的时候的key, 是如何有的呢?

        这个函数, 就是用来在第一次创建共享内存时, 用来生成key的函数。 所以,key不是直接定义的!!!

        上面的图中, key是如何创建出来的呢? 我们通过上面的蓝色框框其实可以看到, 这个函数是通过转化路径名和一个项目标识符来生成一个key

        那么问题来了, 我们的key能不能随便定义一个出来呢?——理论上是可以的, 因为我们两个进程看到同一个资源, 本质上就是需要拿着同一把钥匙, 而key就是这一把钥匙。 ——所以我们不需要把ftok想的太复杂, 这个函数当生成key的时候不需要去系统中查找哪个key使用过, 哪个没有使用过。 而是类似于哈希函数, 使用了一套算法, pathname和proj_id进行数值计算即可!!

        那么, 为什么不能让操作系统自己去生成呢?为什么要让用户自己去设置呢?——虽然操作系统做这些工作很简单, 但是如果操作系统给我们生成了这么一个key, 我们进行通信的时候, 我们怎么把这个key交给另一个进程呢? ——这里可不可以直接将返回的key交给另一个进程呢这个不可以的, 因为我们现在要解决的就是进程间通信的问题, 而想要将key交给另一个进程, 需要进程通信, 这就陷入了一个死循环。 所以, 我们的key, 就势必不能由操作系统自己决定。 ——这个也就说明, 我们的ftok, 与其说是用户自己指定的key, 不如说是用户之间约定的key!!!只要有两个用户, 他们同时使用进程, 同时使用进程, 使用同一个路径, 同一个id, 那么他们就能够进行通信!!!

shmat——进程挂接

 

        什么是进程挂接?我们要访问一个共享内存, 虽然共享内存是操作系统的, 虽然我们使用shmid只是获得了共享内存的编号。 但是既然是内存, 那么如果我们操作系统提供一个系统接口, 通过shmid找到目标的共享内存, 然后让这个共享内存和进程的地址空间通过页表建立了联系。 那么我们的进程, 是不是就相当于能够访问这块共享内存了? 而这个过程, 就叫做挂接!!!

        而一个共享内存可以有多个进程和它挂接起来, 这个数量, 就是nattch, 我们如何观察到这个nattch, 可以使用ipcs -m指令。ps:讲解在指令部分

shmdt——进程去关联

        有进程挂接, 那么就有进程去关联, 也就是下面的shmdt, 传送的参数是shmat返回的地址空间的地址。 

 

        这里我们思考的是我们只有这个共享内存的起始地址, 这个函数是如何知道我们要取消关联的共享内存有多大呢? ——这个问题和realloc, free等函数类似, 他们都是只需要传送首地址, 不需要传送大小或者末尾地址, 这就说明了一定有我们在用户层看不到的东西, 在管理着我们的内存!!!

        并且, 我们的去关联的流程就是——根据页表找到我们的物理内存, 把页表清掉, 根据inode属性, 让物理内存减减, 如果要释放, 就按照属性里面的大小进行释放即可!

 指令

ipcs -m

        ipcs -m可以查看一个共享内存的各个属性:

  • shimd:共享内存的编号。
  • perms:共享内存的权限。
  • bytes:共享内存的大小, 这个大小是以4096唯一个单位的, 也就是说, 虽然我们上面显示我们创建了4097个大小的共享内存, 但其实操作系统给我们开辟的是8k多字节。 从c语言的角度, 这部分多申请的空间叫做cookie。 ——实际上, 我们c语言在申请堆空间的时候,要不要管理所谓的堆空间呢?——答案是肯定要的, 所以就要先描述再组织, 所以我们c语言多申请的那部分空间, 也有对应的属性!!!
  • nattch:代表了该共享内存的挂接数量。

应用

准备文件

ps:Log.hpp我们在之前的文章已经实现过了, 所以这里直接拿来用了, 不会再实现一遍, 不会的友友可以去前面的文章看一看:linux进程间通信——学习与应用命名管道, 日志程序的使用与实现-CSDN博客

准备makefile

有两个cpp文件需要编译。所以需要.PHONY, 然后编译两个文件。 最后clean, 删除两个文件。

.PHONY:all
all:processa.exe processb.exe 

processa.exe:processa.cpp
	g++ -o $@ $^ -std=c++11
processb.exe:processb.cpp 
	g++ -o $@ $^ -std=c++11

.PHONY:clean
clean: 
		rm -f processa.exe processb.exe

Com.hpp

GetKey——封装获取key值的函数

我们知道ftok函数的第一个参数是路径, 第二个参数是项目id。 我们就可以先定义两个常量——pathname和proj_id;然后, 我们又可以创建一个log函数, 让log函数来打印日志; ftok需要包含头文件sys/ipc.h以及sys/types.h。 同时其他的string, cstring, cerrno, 等等都包含进来需要用到。——然后其中的pathname其实就是传给ftok的路径函数, 其中的proj_id就是项目id。 通过这两个我们就可以获得一个key值。 用来创建共享内存。 

#pragma once

#include"Log.hpp"
#include<cerrno>
#include<cstring>
#include<string>
#include<iostream>

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

using namespace std;

const string pathname = "/home/_mian_yang";
const int proj_id = 0x88881; 

Log log;

//获取key
key_t GetKey()
{
	key_t k = ftok(pathname.c_str(), proj_id);   //ftok拿到k函数
	if (k == -1)  //如果k == -1, 那么就拿到k失败。
	{
		log(Fatal, "ftok error: %s", strerror(errno));  //打印错误信息
		exit(1);
	}
	log(Info, "ftok success! Key is: #d", k);  //创建k成功, 返回k
	return k; 

}

CreatShareMemory——获取共享内存

        这是第二层封装, 封装了GetKey函数。 用来获取共享内存。 外层函数传递flag, 也就是贡献内存的打开方式——这个打开方式就是用来区分共享内存共享内存是已经创建好的还是第一次创建的。在本次实验中, 我们prcessa进程用来第一次创建共享内存, 而processb用来获取已经创建好的共享内存。 那么这两个进程内部调用CreatShareMemory的时候就要传递不同的flag: processa进程传递IPC_CREAT | IPC_EXCL | 权限, processb传递IPC_CREAT 

另外, 因为shmget创建共享内存的函数需要传递一个size用来规定共享内存的大小, 所以, 我们可以在上面创建一个size常量。 另外使用shmget也需要包含一下sys/shm.h头文件

//
#pragma once

#include"Log.hpp"
#include<cerrno>
#include<cstring>
#include<iostream>
#include<sys/ipc.h>
#include<sys/types.h>
#include<string>
#include<sys/shm.h>
using namespace std;

const int size = 4096;   //4kb
const string pathname = "/home/_mian_yang";
const int proj_id = 0x88881; 
Log log;


//获取key
key_t GetKey()
{
	key_t k = ftok(pathname.c_str(), proj_id);   //ftok拿到k函数
	if (k == -1)  //如果k == -1, 那么就拿到k失败。
	{
		log(Fatal, "ftok error: %s", strerror(errno));  //打印错误信息
		exit(1);
	}
	log(Info, "ftok success! Key is: #d", k);  //创建k成功, 返回k
	return k; 

}


int CreatShareMemory(int flag)
{
	key_t key = GetKey(); //获取key
	int shmid = shmget(key, size, flag);
	if (shmid == -1)
	{
		log(Fatal, "creat share memory error: %s", strerror(errno)); //创建共享内存失败, 打印错误消息
		exit(1);
	}
	log(Info, "creat share memory is success! shmget is: %d", shmid); //创建共享内存成功, 打印共享内存的的shmid
	return shmid;
}

CreatShm和GetShm——获取共享内存


int GreatShm()
{
	return GreatShareMemory(IPC_CREAT | IPC_EXCL | 0666); //第一次创建需要判断是否存在
	
}

int GetShm()
{
	return GreatShareMemory(IPC_CREAT); //获取的时候不需要判断是否存在
}

processa.cpp

        我们在本次实验中要实现a来打印, b来输入的实验效果。所以我们的a要从内存中读到数据。 在b中把数据写入内存。 

        另外, 我们设计让a来创建共享内存, 创建好共享内存后, 要知道, 我们平时向内存中读数据, 必须要获取这个内存的地址,但是我们现在只有共享内存的编号shmid,  那么如何获取这个地址? ——这就用到了挂接, 我们将共享内存挂接到当前进程的虚拟地址上, 那么进程访问虚拟地址就是在访问共享内存!!!(注:这里面使用了shmctl, 这个函数的使用难度很大, 这里博主使用了IPC_STAT选项, 目的是为了获取shmid对应共享内存中的数据, 那么获取到那里呢, 就需要提前创建一个描述共享内存的结构体, 博主定义的是shmds, 所以就有了下面的代码)

#include"Com.hpp"
#include"Log.hpp"


extern Log log; //拿到Com.hpp里面创建的哪个全局的静态对象, 


int main()
{
    //先创建共享内存
    int shmid = GreatShm();//函数内部自动判断, 无需手动判断
    //挂接共享内存到到vm_area_struct
    char* shmaddr = (char*)shmat(shmid, nullptr, 0); //这个返回值, 就是
    //共享内存的虚拟地址, 我们利用虚拟地址, 就可以访问共享内存

    struct shmid_ds shmds; //创建一个结构体, 用来获取共享内存的数据

    while(true)
    {
        cout << "client say@ " << shmaddr << endl;
        sleep(1);
        shmctl(shmid, IPC_STAT, &shmds); //读取共享内存的各个属性
        //将各个数据打出来!
        cout << "shm size: " << shmds.shm_segsz << endl;
        cout << "shm nattch: " << shmds.shm_nattch << endl;
        cout << "shm key: " << shmds.shm_perm.__key << endl;
    }    
    shmdt(shmaddr);
    shmctl(shmid, IPC_RMID, nullptr); //删除
   
    return 0;
}

processb.cpp

b进程我们用来读取数据, 同样需要挂接。 不同的是我们要创建一个缓冲区buffer, 来接收我们要打印的数据,然后向内存中将缓冲区的数据写进去。 

#include"Com.hpp"
#include"Log.hpp"

int main()
{
    int shmid = GetShm();//获取共享内存
    //向里面写东西, 写东西就需要获得这个共享内存的地址, 那么就需要挂接
    char* shmaddr = (char*)shmat(shmid, nullptr, 0); //挂接地址随机, 模式为0
    while (true)
    {
        //先创建一个缓冲区
        string str;
        cin >> str;  //向缓冲区种写入数据

        snprintf(shmaddr, str.size(), "%s", str.c_str()); //将缓冲区的数据写入
        //共享内存
    }
    return 0;
}

运行程序

如下图是我们甘冈打开两个进程:可以看到, 此时的挂接个数是2.

然后我们在b进程输入一个你好啊, 观察一下——

我们就会发现数据已经打印进去了。 然后我们如果退出进程b, 挂接数会减到1:

——以上就是共享内存接口的相关应用。 

经过上面的实验, 我们可以很明显的看出来, 共享内存是没有同步机制的!!!!——而想要让共享内存拥有同步机制的效果, 可以使用管道!!!
 

key和shimd的区别

        了解了应用后, 我们就可以深究一下内部的原理了。

        首先我们谈的就是请问key和shmid有什么区别呢?我们知道, key是在操作系统标定唯一性的, shmid是在我们的进程里面的,用来标识资源的唯一性的。也就是说,key是操作系统层面的, 只有在创建共享内存的时候会用到key, 其他时候用不到。 

共享内存的生命周期

        我们第一次运行程序processa.exe的时候, key_t会创建成功, 并且共享内存被创建出来。但是当我们第二次运行processa.exe的时候, 我们会看到下面这种情况:

        也就是说, 我们的共享内存并没有随进程的退出而退出。 这里我们可以使用ipcs -m查看当前系统中所有的共享资源

        我们输入上面ipcs -m的指令可以看到我们刚刚创建的共享内存, 也就是说, 即便我们进程推出了, 但是我们的ipc资源, 还是存在的。 这说明如果我们不主动把共享内存关掉, 操作系统也不会给我们关。——这就是共享内存的特性。 ——也就是说, 贡献内存的生命周期是随着内核的!!!(管道文件的生命周期是随着进程的!!!)用户不主动关闭, 共享内存会一直存在, 除非内核重启!!!

        释放的方式分为两种——一种是使用指令进行释放, 另一种就是上面程序中使用的进程内函数调用shmctl进行控制释放共享内存。 

  •         指令释放:ipcrm -m 共享资源的shmid
  •         内部调用:shmctl(shmid, IPC_RMID, nullptr);

 

——————以上就是本节全部内容哦, 如果对友友们有帮助的话可以关注博主, 方便学习更多知识哦!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/879431.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【新手上路】衡石分析平台使用手册-租户管理

租户管理​ 衡石系统支持服务一个平台方和多个企业客户的租户模式&#xff0c;平台方管理租户&#xff0c;为租户提供数据&#xff0c;租户在系统内进行数据分析。 衡石系统增加工作空间的设计&#xff0c;在平台方和租户之间提供单向的传递通道&#xff0c;平台厂商可以轻松…

时序数据库 TDengine 的入门体验和操作记录

时序数据库 TDengine 的学习和使用经验 什么是 TDengine &#xff1f;什么是时序数据 &#xff1f;使用RPM安装包部署默认的网络端口 TDengine 使用TDengine 命令行&#xff08;CLI&#xff09;taosBenchmark服务器内存需求删库跑路测试 使用体验文档纠错 什么是 TDengine &…

C# 使用代码清理 以及禁用某个代码清理

初级代码游戏的专栏介绍与文章目录-CSDN博客 我的github&#xff1a;codetoys&#xff0c;所有代码都将会位于ctfc库中。已经放入库中我会指出在库中的位置。 这些代码大部分以Linux为目标但部分代码是纯C的&#xff0c;可以在任何平台上使用。 源码指引&#xff1a;github源…

下载chromedriver驱动

首先进入关于ChromeDriver最新下载地址&#xff1a;Chrome for Testing availability 进入之后找到与自己所匹配的&#xff0c;在浏览器中查看版本号&#xff0c;下载版本号需要一致。 下载即可&#xff0c;解压&#xff0c;找到 直接放在pycharm下即可 因为在环境变量中早已配…

python3GUI--字符串加密方案(附源码)

文章目录 一&#xff0e;前言二&#xff0e;展示1.AES 加密1.介绍优点缺点2.代码3.结果 2.RSA 加密1.介绍优点缺点2.代码3.结果 3.基于 HMAC 的 URL 签名1.介绍优点缺点2.代码3.结果 4.JWT&#xff08;JSON Web Token&#xff09;加密1.介绍优点缺点2.安装3.代码4.结果 三&…

孙怡带你深度学习(2)--PyTorch框架认识

文章目录 PyTorch框架认识1. Tensor张量定义与特性创建方式 2. 下载数据集下载测试展现下载内容 3. 创建DataLoader&#xff08;数据加载器&#xff09;4. 选择处理器5. 神经网络模型构建模型 6. 训练数据训练集数据测试集数据 7. 提高模型学习率 总结 PyTorch框架认识 PyTorc…

1×1卷积核【super star 卷积核】

一、11卷积的作用 我们先来给出11卷积的一般作用&#xff0c;如下所示&#xff1a; • 跨通道的特征整合 • 特征通道的升维与降维 • 减少权重参数&#xff08;卷积核参数&#xff09; 【 简化模型 】 1.1 特征通道的升维与降维/跨通道的特征整合/简化模型 输入数据&…

《深度学习》—— PyTorch的神经网络模块中常用的损失函数

文章目录 前言一、回归模型中常用的损失函数1、平均绝对误差损失&#xff08;L1Loss&#xff09;2、均方误差损失&#xff08;MSELoss也称L2Loss&#xff09;3、SmoothL1Loss 二、分类模型中常用的损失函数1、负对数似然损失&#xff08;NLLLoss&#xff09;2、二元交叉熵损失&…

网络封装分用

目录 1,交换机 2,IP 3,接口号 4,协议 分层协议的好处: 5,OSI七层网络模型. 6,TCP/IP五层网络模型(主流): [站在发送方视角] [接收方视角] 1,交换机 交换机和IP没有关系,相当于是对路由器接口的扩充,这时相当于主机都与路由器相连处于局域网中,把越来越多的路由器连接起…

8-----手机机型维修工具助手 功能较全 涵盖解锁 刷机 修复等选项 维修推荐

上图是一款功能较全的维修加密狗。目前可以无限制 任何人使用。看图片可以了解其中涵盖刷机 解锁 修复分区 查看短接图 安装驱动 修复基带等等选项。而且其中有针对各个机型型号的对应功能操作。以及一些rec5.0相关的操作选项。 通过此博文了解 ★★★★★此工具涵盖的一些…

论文解读《NewsBench:一个评估中文新闻大型语言模型编辑能力的系统评估框架》

引言&#xff1a;感觉这篇文章&#xff0c;对 LLMs 的新闻编辑能力做了一个详细的实验和分析&#xff0c;而且还贡献了一个宝贵的中文新闻数据集&#xff0c;蛮不错的&#xff0c;后面或许可以用起来&#xff0c;就拜读了一下。 这篇博客的题目说是解读&#xff0c;其实大部分…

Java发邮件:如何配置SMTP服务器实现发信?

Java发邮件功能实现的教程&#xff1f;Java发邮件的方式有哪些&#xff1f; 无论是用于用户注册确认、密码重置&#xff0c;还是系统通知&#xff0c;Java发邮件都是不可或缺的一部分。AokSend将详细介绍如何配置SMTP服务器&#xff0c;以便在Java发邮件时能够顺利发送邮件。 …

如何创建和编辑抖音百科词条,不会的找我们代创建!

如何创建和编辑抖音百科词条&#xff0c;不会的找我们代创建&#xff01; 如何创建抖音百科个人词条&#xff0c;个人抖音百科的创建 #抖音百科 #百科 #推广 做过百度百科的老板们注意了&#xff0c;等一下别划走。 2024 年品宣新风口出现了&#xff0c;抖音百科正在替代百度…

Flask-JWT-Extended登录验证

1. 介绍 """安装:pip install Flask-JWT-Extended创建对象 初始化与app绑定jwt JWTManager(app) # 初始化JWTManager设置 Cookie 的选项:除了设置 cookie 的名称和值之外&#xff0c;你还可以指定其他的选项&#xff0c;例如&#xff1a;过期时间 (max_age)&…

erlang学习:mnesia数据库与ets表1

Mnesia 和 ETS 都是 Erlang 提供的表管理工具&#xff0c;用于存储和检索数据&#xff0c;但它们之间有一些重要的区别和共同点。 共同点 都是Erlang提供的表存储机制&#xff1a;ETS 和 Mnesia 都允许你在内存中创建表&#xff0c;并且可以用来存储键值对或者更复杂的数据结…

VC++以资源方式打开可执行文件

刚看一个资料说可以在VC中&#xff0c;以资源方式打开可执行文件&#xff0c;然后它如果包含对话框一些资源&#xff0c;会呈现出来&#xff0c;可以把其他程序界面上的控件直接拷贝到自己程序&#xff1b; 但是操作了一下没有成功&#xff0c; 先新建一个空对话框准备拷贝东…

Modbus_tcp

目录 一&#xff1a;modbus起源 1.起源 2. 分类&#xff1a; 3. 优势&#xff1a; 4. 应用场景&#xff1a; 5.ModbusTCP特点&#xff08;掌握&#xff09;&#xff1a; 二、 ModbusTCP的协议 1. 报文头 2. 寄存器 1. 线圈&#xff08;Coils&#xff09; 2. 离…

【无标题】rocket

rocketMQ集群双主双从同步模式(2m-2s-sync)搭建-CSDN博客 集群架构概念 在部署的时候首先要将nameserver启动起来&#xff0c;之后就是将broker启动起来&#xff0c;broker启动起来会将自己的信息注册到nameserver上面。之后再去创建topic&#xff0c;因为发消息的逻辑和收消…

虹科方案 | 精准零部件测试!多路汽车开关按键功能检测系统

欢迎关注虹科&#xff0c;为您提供最新资讯&#xff01; #LIN/CAN总线 #零部件测试 #CAN数据 导读 在汽车制造业中&#xff0c;零部件的安全性、功能性和可靠性是确保车辆整体性能的关键。虹科针对车辆零部件的LIN/CAN总线仿真测试&#xff0c;提出了基于虹科Baby-LIN系列产…

【教程】鸿蒙ARKTS 打造数据驾驶舱---前序

鸿蒙ARKTS 打造数据驾驶舱 ​ 前面2章我介绍了如何通过定义View绘制箭头以及圆形进度&#xff0c;初步了解了鸿蒙如何进行自定义View。接下来我将通过我最近在带的一个VUE的项目&#xff0c;简单实现了几个鸿蒙原生页面。帮助大家快速上手纯血鸿蒙开发. 本项目基于Api11Stage模…