#星光计划1.0# v56.05 鸿蒙内核源码分析(进程映像) 原创 精华
子畏于匡,颜渊后。子曰:“吾以女为死矣。”曰:“子在,回何敢死?” 《论语》:先进篇
百篇博客分析.本篇为: (进程映像篇) | 程序是如何被加载运行的
加载运行相关篇为:
- v51.04 鸿蒙内核源码分析(ELF格式) | 应用程序入口并非main
- v53.03 鸿蒙内核源码分析(ELF解析) | 敢忘了她姐俩你就不是银
- v54.04 鸿蒙内核源码分析(静态链接) | 一个小项目看中间过程
- v55.04 鸿蒙内核源码分析(重定位) | 与国际接轨的对外发言人
- v56.05 鸿蒙内核源码分析(进程映像) | 程序是如何被加载运行的
可执行文件和共享目标文件(动态连接库)是程序的静态存储形式.要执行一个程序,系统要先把相应的可执行文件和动态连接库装载到进程空间中,这样形成一个可运行的进程的内存空间布局,也可以称它为"进程映像".
本篇结合源码介绍鸿蒙加载和运行shell进程的整个过程,因本篇涉及代码较多,所以删减了一些不相干的代码. 鸿蒙加载和运行ELF的函数为 LOS_DoExecveFile
LOS_DoExecveFile
根文件系统已经提供shell,fileName为 “/bin/shell”
解读
- 创建了一个新的用户进程空间,每个应用进程都有自己独立的进程空间,也称虚拟空间.这个空间和内核空间是隔离的,用户空间的虚拟地址范围为 0x00000000 ~ 0x3FFFFFFF,内核空间是0x3FFFFFFF ~ 0xFFFFFFFF
- 加载ELF文件,注意 SysExecve -> LOS_DoExecveFile,而
SysExecve
是个系统调用,所以LOS_DoExecveFile
是运行在内核空间.加载过程由内核完成,包括申请的动态内存都是由内核空间提供. - 加载成功后,当前进程会被腾龙换鸟,把原有内脏挖空后留给新的
shell
使用,原用进程空间和文件都会被保存下来. - 运行shell,代码段,数据段装载完成后,设置好运行栈,运行就变得很简单,将用户栈保存到内核栈中,程序就会切到shell入口地址
0x1000
执行,正式开始了 shell 之旅
如何加载?
ELF一体两面,面对不同的场景扮演不同的角色,这是理解ELF的关键,链接器只关注1(ELF头信息),3(区),4(区头表) 的内容,加载器只关注1(ELF头信息),2(段头表),3(段)的内容,本篇说加载过程,所以不会出现区(sections)这个概念.
先看shell
1,2,3(段)的内容,这些内容看过
- v53.xx 鸿蒙内核源码分析(ELF解析篇)
- v51.xx 鸿蒙内核源码分析(ELF格式篇)
的不会陌生,对照着代码去看很容易理解.
解读
- INTERP 段,说明ELF需要加载另一个动态链接库
/lib/ld-musl-arm.so.1
. - GNU_STACK 段,指的就是栈,没有它内核无法构建栈,而且必须是RW
- LOAD 段,指加载段,即.bss,.data,.text都属于加载段,加载它们到指定位置就是加载器的工作,而ELF本身已经提供了指令/数据的相对位置.加载器只需提供一个加载开始地址就能计算出指令/数据在虚拟空间中的最终地址.
ELFLoadInfo
理解ELFLoadInfo
是理解鸿蒙加载ELF运行的关键.代码都已经注释.
解读
-
一个程序要运行需要两个必不可少的硬性条件.
-
- 指令在哪里,由
elfEntry
,它是.text
的开始位置,直接在 elf头中可以读到.
- 指令在哪里,由
-
- 拿到指令后在哪里运行,即栈在哪里,
ELFLoadInfo
有7个变量在描述栈信息.足以说明栈的重要性.栈的构建对应的是ELF的GNU_STACK
段,权限必须是(R + W)
- 拿到指令后在哪里运行,即栈在哪里,
-
-
interpInfo
对应的是ELF的INTERP
段,不是所有的ELF都会有INTERP
段,如下:这个段的意思就是需要加载动态链接库,
/lib/ld-musl-arm.so.1
是libc.so
的一个软链,具体位置在根文件系统 /rootfs/lib/libc.so 位置. -
argv
,envc
命令行参数和环境变量内核会专门开辟4K空间,保存在栈底位置,一起保存的还有ELF的辅助向量表auxVector
. -
loadAddr
通过LOS_MMap
将各LOAD
段并做好的虚拟地址和物理地址的映射关系保存在了映射区.- 从代码看对
.bss
区做了匿名映射,见于OsSetBss()
,不清楚为何内核要区别对待.bss
区. - 其余各区做了文件映射.
- 从代码看对
加载过程(OsLoadELFFile)
源码位置: …\kernel\extended\dynload\src\los_load_elf.c
解读
-
OsReadPhdrs
读取程序头(段头),共11个段头. -
OsReadInterpInfo
读取动态链接库lib/libc.so
段头信息. -
OsSetArgParams
将外部参数(命令行和环境变量)保存在栈底位置 -
OsFlushAspace
切换进程空间,新进程空间重置堆区,映射区,MMU切换.映射区一旦变化意味着MMU的L1,L2表的变化. -
OsLoadELFSegment
加载ELF.bss,.data,.text
区,这些区统一叫LOAD
段,建立新的虚拟地址和物理地址映射关系 -
经过以上操作, shell在虚拟内存中真实样子如下:
但注意:其中不包含 /lib/libc.so的信息,动态链接部分会单独一篇去说明.
-
用户地址空间在 mmap处 一切为二, 堆区独占1/4, 所有区(.bbs,.text,…)共占1/4,映射区和栈区共占1/2,二者相立而行,向中间靠拢.
如何运行?
由 …\kernel\extended\dynload\src\los_exec_elf.c 提供,很简单.
解读
- 运行shell出奇的简单,设置好执行指令的入口地址(PC)寄出器和栈指针(SP)就可以了,这些内容在系列篇中已经反复说过,请自行翻看.
- 因shell为用户态进程,所以会有内核态和用户态两个栈,初始化内核栈
OsTaskStackInit
和用户栈OsUserTaskStackInit
过程在线程概念篇中也已有描述.
百篇博客分析.深挖内核地基
- 给鸿蒙内核源码加注释过程中,整理出以下文章。内容立足源码,常以生活场景打比方尽可能多的将内核知识点置入某种场景,具有画面感,容易理解记忆。说别人能听得懂的话很重要! 百篇博客绝不是百度教条式的在说一堆诘屈聱牙的概念,那没什么意思。更希望让内核变得栩栩如生,倍感亲切.确实有难度,自不量力,但已经出发,回头已是不可能的了。 😛
- 与代码有bug需不断debug一样,文章和注解内容会存在不少错漏之处,请多包涵,但会反复修正,持续更新,v**.xx 代表文章序号和修改的次数,精雕细琢,言简意赅,力求打造精品内容。
按功能模块:
- 前因后果 >> 总目录 | 调度故事 | 内存主奴 | 源码注释 | 源码结构 | 静态站点 |
- 基础工具 >> 双向链表 | 位图管理 | 用栈方式 | 定时器 | 原子操作 | 时间管理 |
- 加载运行 >> ELF格式 | ELF解析 | 静态链接 | 重定位 | 进程映像 |
- 进程管理 >> 进程管理 | 进程概念 | Fork | 特殊进程 | 进程回收 | 信号生产 | 信号消费 | Shell编辑 | Shell解析 |
- 编译构建 >> 编译环境 | 编译过程 | 环境脚本 | 构建工具 | gn应用 | 忍者ninja |
- 进程通讯 >> 自旋锁 | 互斥锁 | 进程通讯 | 信号量 | 事件控制 | 消息队列 |
- 内存管理 >> 内存分配 | 内存管理 | 内存汇编 | 内存映射 | 内存规则 | 物理内存 |
- 任务管理 >> 时钟任务 | 任务调度 | 任务管理 | 调度队列 | 调度机制 | 线程概念 | 并发并行 | CPU | 系统调用 | 任务切换 |
- 文件系统 >> 文件概念 | 文件系统 | 索引节点 | 挂载目录 | 根文件系统 | 字符设备 | VFS | 文件句柄 | 管道文件 |
- 硬件架构 >> 汇编基础 | 汇编传参 | 工作模式 | 寄存器 | 异常接管 | 汇编汇总 | 中断切换 | 中断概念 | 中断管理 |
百万汉字注解.精读内核源码
四大码仓中文注解 . 定期同步官方代码
鸿蒙研究站( weharmonyos ) | 每天死磕一点点,原创不易,欢迎转载,请注明出处。若能支持点赞则更佳,感谢每一份支持。
很详细~