鸿蒙内核源码分析(消息封装篇) | 剖析LiteIpc(上)进程通讯内容
基本概念
LiteIPC
是OpenHarmony LiteOS-A
内核提供的一种新型IPC
(Inter-Process Communication,即进程间通信)机制,为轻量级进程间通信组件,为面向服务的系统服务框架提供进程间通信能力,分为内核实现和用户态实现两部分,其中内核实现完成进程间消息收发、IPC内存管理、超时通知和死亡通知等功能;用户态提供序列化和反序列化能力,并完成IPC
回调消息和死亡消息的分发。
我们主要讲解内核态实现部分,本想一篇说完,但发现它远比想象中的复杂和重要,所以分两篇说,通讯内容和通讯机制。通讯的内容就是消息,围绕着消息展开的结构体多达10
几个,捋不清它们之间的关系肯定是搞不懂通讯的机制,所以咱们得先搞清楚关系再说流程。下图是笔者读完LiteIPC
模块后绘制的消息封装图,可以说LiteIPC
是内核涉及结构体最多的模块,请消化理解,本篇将围绕它展开。
[图片上传失败…(image-a29828-1715581843494)]
系列篇多次提过,内核的每个模块都至少围绕着一个重要结构体展开,抓住了它顺瓜摸藤就能把细节抹的清清楚楚,于LiteIPC
,这个结构体就是IpcMsg
。
运行机制
typedef struct {//IPC 消息结构体MsgType type; /**< cmd type, decide the data structure below | 命令类型,决定下面的数据结构*/SvcIdentity target; /**< serviceHandle or targetTaskId, depending on type | 因命令类型不同而异*/UINT32 code; /**< service function code | 服务功能代码*/UINT32 flag; ///< 标签
#if (USE_TIMESTAMP == 1)UINT64 timestamp; ///< 时间戳,用于验证
#endifUINT32 dataSz; /**< size of data | 消息内容大小*/VOID *data; ///< 消息的内容,真正要传递的消息,这个数据内容是指spObjNum个数据的内容,定位就靠offsetsUINT32 spObjNum; ///< 对象数量, 例如 spObjNum = 3时,offsets = [0,35,79],代表从data中读取 0 - 35给第一个对象,依次类推VOID *offsets; ///< 偏移量,注意这里有多少个spObjNum就会有多少个偏移量,详见 CopyDataFromUser 来理解UINT32 processID; /**< filled by kernel, processId of sender/reciever | 由内核提供,发送/接收消息的进程ID*/UINT32 taskID; /**< filled by kernel, taskId of sender/reciever | 由内核提供,发送/接收消息的任务ID*/
#ifdef LOSCFG_SECURITY_CAPABILITY UINT32 userID; ///< 用户IDUINT32 gid; ///< 组ID
#endif
} IpcMsg;
解读
-
第一个
type
,通讯的本质就是你来我往,异常当然也要考虑typedef enum { MT_REQUEST, ///< 请求MT_REPLY, ///< 回复MT_FAILED_REPLY,///< 回复失败MT_DEATH_NOTIFY,///< 通知死亡MT_NUM} MsgType;
-
第二个
target
,LiteIPC
中有两个主要概念,一个是ServiceManager
,另一个是Service
。整个系统只能有一个ServiceManager
,而Service
可以有多个。ServiceManager
有两个主要功能:一是负责Service
的注册和注销,二是负责管理Service
的访问权限(只有有权限的任务Task
可以向对应的Service
发送IPC
消息)。首先将需要接收IPC
消息的任务通过ServiceManager
注册成为一个Service
,然后通过ServiceManager
为该Service
任务配置访问权限,即指定哪些任务可以向该Service
任务发送IPC
消息。LiteIPC
的核心思想就是在内核态为每个Service
任务维护一个IPC
消息队列,该消息队列通过LiteIPC
设备文件向上层用户态程序分别提供代表收取IPC
消息的读操作和代表发送IPC
消息的写操作。
/// SVC(service)服务身份证 typedef struct {UINT32 handle; //service 服务ID, 范围[0,最大任务ID]UINT32 token; //由应用层带入UINT32 cookie; //由应用层带入} SvcIdentity;
code
,timestamp
由应用层设定,用于确保回复正确有效,详见CheckRecievedMsg
dataSz
,data
,spObjNum
,offsets
这四个需连在一起理解,是重中之重。其实消息又分成三种类型(对象)
typedef enum {OBJ_FD, ///< 文件句柄OBJ_PTR, ///< 指针OBJ_SVC ///< 服务,用于设置权限} ObjType;typedef union {UINT32 fd; ///< 文件描述符BuffPtr ptr; ///< 缓存的开始地址,即:指针,消息从用户空间来时,要将内容拷贝到内核空间SvcIdentity svc; ///< 服务,用于设置访问权限} ObjContent;typedef struct { // IpcMsg->data 包含三种子消息,也要将它们读到内核空间ObjType type; ///< 类型ObjContent content;///< 内容} SpecialObj;
这三种对象都打包在data
中,总长度是dataSz
,spObjNum
表示个数,offsets
是个整型数组,标记了对应第几个对象在data
中的位置,这样就很容易从data
读到对象的数据。
UINT32 fd
类型对象通讯的实现是通过两个进程间共享同一个fd
来实现通讯,具体实现函数为HandleFd
。
/// 按句柄方式处理, 参数 processID 往往不是当前进程LITE_OS_SEC_TEXT STATIC UINT32 HandleFd(UINT32 processID, SpecialObj *obj, BOOL isRollback){int ret;if (isRollback == FALSE) { // 不回滚ret = CopyFdToProc(obj->content.fd, processID);//目的是将两个不同进程fd都指向同一个系统fd,共享FD的感觉if (ret < 0) {//返回 processID 的 新 fdreturn ret;}obj->content.fd = ret; // 记录 processID 的新FD, 可用于回滚} else {// 回滚时关闭进程FDret = CloseProcFd(obj->content.fd, processID);if (ret < 0) {return ret;}}
SvcIdentity svc
用于设置进程<->任务之间彼此访问权限,具体实现函数为HandleSvc
。
/// 按服务的方式处理,此处推断 Svc 应该是 service 的简写 @note_thinkingLITE_OS_SEC_TEXT STATIC UINT32 HandleSvc(UINT32 dstTid, const SpecialObj *obj, BOOL isRollback){UINT32 taskID = 0;if (isRollback == FALSE) {if (IsTaskAlive(obj->content.svc.handle) == FALSE) {PRINT_ERR("Liteipc HandleSvc wrong svctid\n");return -EINVAL;}if (HasServiceAccess(obj->content.svc.handle) == FALSE) {PRINT_ERR("Liteipc %s, %d\n", __FUNCTION__, __LINE__);return -EACCES;}if (GetTid(obj->content.svc.handle, &taskID) == 0) {//获取参数消息服务ID所属任务if (taskID == OS_PCB_FROM_PID(OS_TCB_FROM_TID(taskID)->processID)->ipcInfo->ipcTaskID) {//如果任务ID一样,即任务ID为ServiceManagerAddServiceAccess(dstTid, obj->content.svc.handle);}}}return LOS_OK;}
BuffPtr ptr
是通过指针传值,具体实现函数为HandlePtr
,对应结构体为BuffPtr
。
typedef struct {UINT32 buffSz; ///< 大小VOID *buff; ///< 内容 内核需要将内容从用户空间拷贝到内核空间的动作 } BuffPtr;/// 按指针方式处理LITE_OS_SEC_TEXT STATIC UINT32 HandlePtr(UINT32 processID, SpecialObj *obj, BOOL isRollback){VOID *buf = NULL;UINT32 ret;if ((obj->content.ptr.buff == NULL) || (obj->content.ptr.buffSz == 0)) {return -EINVAL;}if (isRollback == FALSE) {if (LOS_IsUserAddress((vaddr_t)(UINTPTR)(obj->content.ptr.buff)) == FALSE) { // 判断是否为用户空间地址PRINT_ERR("Liteipc Bad ptr address\n"); //不在用户空间时return -EINVAL;}buf = LiteIpcNodeAlloc(processID, obj->content.ptr.buffSz);//在内核空间分配内存接受来自用户空间的数据if (buf == NULL) {PRINT_ERR("Liteipc DealPtr alloc mem failed\n");return -EINVAL;}ret = copy_from_user(buf, obj->content.ptr.buff, obj->content.ptr.buffSz);//从用户空间拷贝数据到内核空间if (ret != LOS_OK) {LiteIpcNodeFree(processID, buf);return ret;}//这里要说明下 obj->content.ptr.buff的变化,虽然都是用户空间的地址,但第二次已经意义变了,虽然数据一样,但指向的是申请经过拷贝后的内核空间obj->content.ptr.buff = (VOID *)GetIpcUserAddr(processID, (INTPTR)buf);//获取进程 processID的用户空间地址,如此用户空间操作buf其实操作的是内核空间EnableIpcNodeFreeByUser(processID, (VOID *)buf);//创建一个IPC节点,挂到可使用链表上,供读取} else {(VOID)LiteIpcNodeFree(processID, (VOID *)GetIpcKernelAddr(processID, (INTPTR)obj->content.ptr.buff));//在内核空间释放IPC节点}return LOS_OK;}
processID
和taskID
则由内核填充,应用层是感知不到进程和任务的,暴露给它是服务ID,SvcIdentity.handle
,上层使用时只需向服务发送/读取消息,而服务是由内核创建,绑定在任务和进程上。所以只要有服务ID就能查询到对应的进程和任务ID。userID
和gid
涉及用户和组安全模块,请查看系列相关篇。
进程和任务
再说两个结构体 ProcIpcInfo
,IpcTaskInfo
LiteIPC
实现的是进程间的通讯,所以在进程控制块中肯定有它的位置存在,即:ProcIpcInfo
。
typedef struct {IpcPool pool; ///< ipc内存池,IPC操作所有涉及内核空间分配的内存均有此池提供UINT32 ipcTaskID; ///< 指定能ServiceManager的任务IDLOS_DL_LIST ipcUsedNodelist;///< 已使用节点链表,上面挂 IpcUsedNode 节点, 申请IpcUsedNode的内存来自内核堆空间UINT32 access[LOSCFG_BASE_CORE_TSK_LIMIT]; ///< 允许进程通过IPC访问哪些任务
} ProcIpcInfo;
而进程只是管家,真正让内核忙飞的是任务,在任务控制块中也应有LiteIPC
一席之地,即:IpcTaskInfo
。
typedef struct {LOS_DL_LIST msgListHead;///< 上面挂的是一个个的 ipc节点 上面挂 IpcListNode,申请IpcListNode的内存来自进程IPC内存池BOOL accessMap[LOSCFG_BASE_CORE_TSK_LIMIT]; ///< 此处是不是应该用 LOSCFG_BASE_CORE_PROCESS_LIMIT ? @note_thinking ///< 任务是否可以给其他进程发送IPC消息
} IpcTaskInfo;
两个结构体不复杂,把发送/回复的消息挂到对应的链表上,并提供进程<->任务间彼此访问权限功能access
,accessMap
,由谁来设置权限呢 ? 上面已经说过了是 HandleSvc
。
IPC内存池
还有最后一个结构体IpcPool
,
typedef struct {//用户空间和内核空间的消息传递通过偏移量计算VOID *uvaddr; ///< 用户空间地址,由kvaddr映射而来的地址,这两个地址的关系一定要搞清楚,否则无法理解IPC的核心思想VOID *kvaddr; ///< 内核空间地址,IPC申请的是内核空间,但是会通过 DoIpcMmap 将这个地址映射到用户空间UINT32 poolSize; ///< ipc池大小
} IpcPool;
它是LiteIPC
实现通讯机制的基础,是内核设计很巧妙的地方,实现了在用户态读取内核态数据的功能。请想想它是如何做到的 ?
鸿蒙全栈开发全新学习指南
也为了积极培养鸿蒙生态人才,让大家都能学习到鸿蒙开发最新的技术,针对一些在职人员、0基础小白、应届生/计算机专业、鸿蒙爱好者等人群,整理了一套纯血版鸿蒙(HarmonyOS Next)全栈开发技术的学习路线【包含了大厂APP实战项目开发】。
本路线共分为四个阶段:
第一阶段:鸿蒙初中级开发必备技能
第二阶段:鸿蒙南北双向高工技能基础:gitee.com/MNxiaona/733GH
第三阶段:应用开发中高级就业技术
第四阶段:全网首发-工业级南向设备开发就业技术:gitee.com/MNxiaona/733GH
《鸿蒙 (Harmony OS)开发学习手册》(共计892页)
如何快速入门?
1.基本概念
2.构建第一个ArkTS应用
3.……
开发基础知识:gitee.com/MNxiaona/733GH
1.应用基础知识
2.配置文件
3.应用数据管理
4.应用安全管理
5.应用隐私保护
6.三方应用调用管控机制
7.资源分类与访问
8.学习ArkTS语言
9.……
基于ArkTS 开发
1.Ability开发
2.UI开发
3.公共事件与通知
4.窗口管理
5.媒体
6.安全
7.网络与链接
8.电话服务
9.数据管理
10.后台任务(Background Task)管理
11.设备管理
12.设备使用信息统计
13.DFX
14.国际化开发
15.折叠屏系列
16.……
鸿蒙开发面试真题(含参考答案):gitee.com/MNxiaona/733GH
鸿蒙入门教学视频:
美团APP实战开发教学:gitee.com/MNxiaona/733GH
写在最后
- 如果你觉得这篇内容对你还蛮有帮助,我想邀请你帮我三个小忙:
- 点赞,转发,有你们的 『点赞和评论』,才是我创造的动力。
- 关注小编,同时可以期待后续文章ing🚀,不定期分享原创知识。
- 想要获取更多完整鸿蒙最新学习资源,请移步前往小编:
gitee.com/MNxiaona/733GH
相关文章:

鸿蒙内核源码分析(消息封装篇) | 剖析LiteIpc(上)进程通讯内容
基本概念 LiteIPC是OpenHarmony LiteOS-A内核提供的一种新型IPC(Inter-Process Communication,即进程间通信)机制,为轻量级进程间通信组件,为面向服务的系统服务框架提供进程间通信能力,分为内核实现和用户…...

Charger之三动态电源路径管理(DPPM)
-----本文简介----- 主要内容包括: 领资料:点下方↓名片关注回复:粉丝群 硬件之路学习笔记公众号 Charger的动态电源路径管理(DPPM) 前篇内容:①电池管理IC(Charger)了解一下&…...
大数据模型的选择与安装
大数据模型的选择和安装是一个复杂的过程,涉及多个因素,包括模型的通用能力、特定任务的性能、数据效率、评估完整性、成本以及部署的硬件和软件环境。以下是一些关于大数据模型选择与安装的考虑因素和步骤: 选择大数据模型的考虑因素&#…...
React 之 lazy(延迟加载)(十七)
lazy 能够让你在组件第一次被渲染之前延迟加载组件的代码。 在组件外部调用 lazy,以声明一个懒加载的 React 组件: import { lazy } from react;const MarkdownPreview lazy(() > import(./MarkdownPreview.js)); 配合 Suspense 实现懒加载组件 //App.js imp…...
Node.js -- 会话控制
文章目录 1. 会话介绍2. cookie 相关操作2.1 cookie 设置2.2 删除 cookie2.3 获取cookie 3. session 相关操作4. cookie 和session 的区别5. 补充知识 -- CSRF跨站请求伪造6. token 1. 会话介绍 所谓会话控制就是对会话进行控制 HTTP是一种无状态的协议,它没有办法…...

做抖店不能踩的几个坑,新手要照做,老玩家要听劝~
我是王路飞。 很多人都说抖店的运营很简单,选选品、对接一下达人,就可以坐等店铺出单了。 这话骗骗还没开店的小白也就得了,但凡做抖店超过一个月的,都不会相信这句话。 细心耐心是做抖店最基本的态度。 拿到一个好结果的前提…...

【Kibana】快速上手Kibana平台(KQL)
文章目录 快速使用Kibana平台常用查询语句KQL基本查询覆合查询模糊查询 目前市面上大部分的公司的日志系统都是使用ELK系统,因此我们进行工作必须得掌握Kibana平台的基本使用,这里主要说明怎么“快速使用Kibana平台”以及记录一些常用的“KQL语言”。 快…...

全方位入门git-慕课网 笔记
目录 【上传github忽略某些文件】【配置用户名和邮箱】【想要删除不需要的文件时如何进行操作】【想要给文件重命名如何操作】【想要移动文件到其他位置时如何操作】【文件有变化时,如何查看前后变化】【操作失误的情况下如何实现一键还原】【不再追踪时如何实现撤销…...

使用 Docker 部署 TaleBook 私人书籍管理系统
1)项目介绍 GitHub:https://github.com/talebook/talebook Talebook 是一个简洁但强大的私人书籍管理系统。它基于 Calibre 项目构建,具备书籍管理、在线阅读与推送、用户管理、SSO 登录、从百度/豆瓣拉取书籍信息等功能。 友情提醒&#x…...
分布式系统的一致性与共识算法(一)
前言 etcd是线性一致性读,而zk却是顺序一致性读,再加上各种共识、强弱一致的名词,看到欸度时候总会混淆,这里会给出一些例子来帮助理解。 什么是一致性? 在谈到一致性这个词时,你会想到CAP理论的consist…...

创建一个Spring Boot项目
文章目录 一、如何创建一个Spring Boot项目1.1 项目创建:专业版 or 社区版 or 网站创建1.2 数据配置1.3 项目启动1.4 代码编写 二、Spring Boot 项目文件介绍三、Web服务器四、根据HTTP状态码解决bug4.1 4044.2 500 五、Spring VS Spring Boot VS Spring Web MVC5.1…...

ansible -playbook运维工具、语法、数据结构、命令用法、触发器、角色
目录 配置文件 基本语法规则: YAML支持的数据结构 playbook核心元素 ansible-playbook用法: 触发器 特点: 角色: 习题: 配置文件 playbook配置文件使用yaml语法,YAML 是一门标记性语言,专门用来写配…...

web前端之sass中的颜色函数、active按钮激活、hover鼠标悬浮、disabled禁用、scss循环、css
MENU 效果图htmlsassscss编译后的css页面css 效果图 注意查看蓝色按钮。 html <div class"box"><button class"btn type_1">按钮</button><button class"btn type_2">按钮</button><button class"btn ty…...

交通地理信息系统实习教程(二)
这篇文章服务于GIS背景用户以及有志于GIS的朋友 操作源数据位置:【免费】交通地理信息系统实习二源数据资源-CSDN文库 软件安装包位置:【免费】TransCad-交通地理信息系统软件资源-CSDN文库 一、最短路径分析 1.1软件启动说明 这里需要给出一个必要的…...
Shell脚本——批量清理Kubernetes集群中Evicted状态的pod
测试环境有一台宿主机出现了异常,大量的异常日志导致宿主机的磁盘使用率超过了85%,触发了上面的pod驱离策略,该宿主机上的的pod处于Evicted状态。在清理了磁盘之后,得手动处理掉这些Evicted状态的pod。 #!/bin/bash# 获取当前状态…...

(深度估计学习)Win11复现DepthFM
目录 1. 系统配置2. 拉取代码,配置环境3.开始深度预测4.运行结果 论文链接:https://depthfm.github.io/ 讲解链接:https://www.php.cn/faq/734404.html 1. 系统配置 本人系统:Win11 CUDA12.2 python3.11.5 这里附上几个CUDA安装链…...
cocos creator 帧率60 不生效meta50 能刷新到90
环境: cocos creator 2.4.3 华为meta 50 背景: 小游戏 需要在update 里取帧率 发现跟时间对不上 游戏设置60帧 手机上显示 90帧 cc.game.setFrameRate(30) 显示 30帧 cc.game.setFrameRate(60) 显示 90帧 结论: 对于老版本的cocos creator …...

探讨 cs2019 c++ 的STL 库中的模板 conjunction 与 disjunction
(1)在 STL 库源码中这俩模板经常出现,用来给源码编译中的条件选择,模板的版本选择等提供依据。先给出其定义: 以及: 可以得出结论: conj 是为了查找逻辑布尔型模板参数中的第一个 false &#x…...

【核弹】我的第一款IDEA插件
SuperHotSwap 插件名称叫做:SuperHotSwap(超级热更新) 开发初心:旨在做出一款最便捷的IDEA热更新插件,减少用户操作步骤,提供零配置的可视化操作更新。 为什么要写这个插件: 每次改一下Mappe…...

【工作篇】软件工程师的知识基础(持续更新)
目录 1. linux 知识篇 1. linux 知识篇 1. Linux API 是什么 Linux API 是指 Linux 操作系统 提供的应用程序接口,用于与操作系统进行交互。它包含了一系列的函数、系统调用、库函数和数据结构,用于实现各种系统级的操作,如文件操作、进程…...

Linux应用开发之网络套接字编程(实例篇)
服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …...

业务系统对接大模型的基础方案:架构设计与关键步骤
业务系统对接大模型:架构设计与关键步骤 在当今数字化转型的浪潮中,大语言模型(LLM)已成为企业提升业务效率和创新能力的关键技术之一。将大模型集成到业务系统中,不仅可以优化用户体验,还能为业务决策提供…...

日语AI面试高效通关秘籍:专业解读与青柚面试智能助攻
在如今就业市场竞争日益激烈的背景下,越来越多的求职者将目光投向了日本及中日双语岗位。但是,一场日语面试往往让许多人感到步履维艰。你是否也曾因为面试官抛出的“刁钻问题”而心生畏惧?面对生疏的日语交流环境,即便提前恶补了…...

C++初阶-list的底层
目录 1.std::list实现的所有代码 2.list的简单介绍 2.1实现list的类 2.2_list_iterator的实现 2.2.1_list_iterator实现的原因和好处 2.2.2_list_iterator实现 2.3_list_node的实现 2.3.1. 避免递归的模板依赖 2.3.2. 内存布局一致性 2.3.3. 类型安全的替代方案 2.3.…...

CMake基础:构建流程详解
目录 1.CMake构建过程的基本流程 2.CMake构建的具体步骤 2.1.创建构建目录 2.2.使用 CMake 生成构建文件 2.3.编译和构建 2.4.清理构建文件 2.5.重新配置和构建 3.跨平台构建示例 4.工具链与交叉编译 5.CMake构建后的项目结构解析 5.1.CMake构建后的目录结构 5.2.构…...

什么是库存周转?如何用进销存系统提高库存周转率?
你可能听说过这样一句话: “利润不是赚出来的,是管出来的。” 尤其是在制造业、批发零售、电商这类“货堆成山”的行业,很多企业看着销售不错,账上却没钱、利润也不见了,一翻库存才发现: 一堆卖不动的旧货…...

Mac软件卸载指南,简单易懂!
刚和Adobe分手,它却总在Library里给你写"回忆录"?卸载的Final Cut Pro像电子幽灵般阴魂不散?总是会有残留文件,别慌!这份Mac软件卸载指南,将用最硬核的方式教你"数字分手术"࿰…...
精益数据分析(97/126):邮件营销与用户参与度的关键指标优化指南
精益数据分析(97/126):邮件营销与用户参与度的关键指标优化指南 在数字化营销时代,邮件列表效度、用户参与度和网站性能等指标往往决定着创业公司的增长成败。今天,我们将深入解析邮件打开率、网站可用性、页面参与时…...
Hive 存储格式深度解析:从 TextFile 到 ORC,如何选对数据存储方案?
在大数据处理领域,Hive 作为 Hadoop 生态中重要的数据仓库工具,其存储格式的选择直接影响数据存储成本、查询效率和计算资源消耗。面对 TextFile、SequenceFile、Parquet、RCFile、ORC 等多种存储格式,很多开发者常常陷入选择困境。本文将从底…...
React---day11
14.4 react-redux第三方库 提供connect、thunk之类的函数 以获取一个banner数据为例子 store: 我们在使用异步的时候理应是要使用中间件的,但是configureStore 已经自动集成了 redux-thunk,注意action里面要返回函数 import { configureS…...