
v46.05 鸿蒙内核源码分析(特殊进程) | 老鼠生儿会打洞 原创
寝不尸,居不容。见齐衰者,虽狎,必变。见冕者与瞽者,虽亵,必以貌。凶服者式之。式负版者。有盛馔,必变色而作。迅雷风烈,必变 《论语》:乡党篇
百篇博客系列篇.本篇为:
v46.xx 鸿蒙内核源码分析(特殊进程篇) | 龙生龙凤生凤老鼠生儿会打洞
进程管理相关篇为:
- v02.06 鸿蒙内核源码分析(进程管理) | 谁在管理内核资源
- v24.03 鸿蒙内核源码分析(进程概念) | 进程在管理哪些资源
- v45.05 鸿蒙内核源码分析(Fork) | 一次调用,两次返回
- v46.05 鸿蒙内核源码分析(特殊进程) | 老鼠生儿会打洞
- v47.02 鸿蒙内核源码分析(进程回收) | 临终前如何向老祖宗托孤
- v48.05 鸿蒙内核源码分析(信号生产) | 年过半百,依然活力十足
- v49.03 鸿蒙内核源码分析(信号消费) | 谁让CPU连续四次换栈运行
- v71.03 鸿蒙内核源码分析(Shell编辑) | 两个任务,三个阶段
- v72.01 鸿蒙内核源码分析(Shell解析) | 应用窥伺内核的窗口
三个进程
鸿蒙有三个特殊的进程,创建顺序如下:
- 2号进程,
KProcess
,为内核态根进程.启动过程中创建. - 0号进程,
KIdle
为内核态第二个进程,它是通过KProcess
fork 而来的.这有点难理解. - 1号进程,
init
,为用户态根进程.由任务SystemInit
创建.
- 发现没有在图中看不到0号进程,在看完本篇之后请想想为什么?
家族式管理
- 进程(process)是家族式管理,总体分为两大家族,用户态家族和内核态家族.
- 用户态的进程是平民阶层,屌丝矮矬穷,干着各行各业的活,权利有限,人数众多,活动范围有限(用户空间).有关单位肯定不能随便进出.这个阶层有个共同的老祖宗g_userInitProcess (1号进程).
- 内核态的进程是贵族阶层,管理平民阶层的,维持平民生活秩序的,拥有超级权限,能访问整个空间和所有资源,人数不多.这个阶层老祖宗是 g_kernelInitProcess(2号进程).
- 两位老祖宗都不是通过fork来的,而是内核强制规定进程ID号,强制写死基因创建的.
- 这两个阶层可以相互流动吗,有没有可能通过高考改变命运? 答案是: 绝对冇可能!!! 龙生龙,凤生凤,老鼠生儿会打洞.从老祖宗创建的那一刻起就被刻在基因里了,抹不掉了. 因为后续所有的进程都是由这两位老同志克隆(clone)来的,没得商量的继承这份基因.
LosProcessCB
有专门的标签来processMode
区分这两个阶层.整个鸿蒙内核源码并没有提供改变命运机会的set
函数.
2号进程 KProcess
2号进程为内核态的老祖宗,是内核创建的首个进程,源码过程如下,省略了不相干的代码.
解读
- main函数在系列篇中会单独讲,请留意自行翻看,它是在开机之初在SVC模式下创建的.
- 内核态老祖宗的名字叫
KProcess
,优先级为最高 0 级,KProcess
进程是长期活跃的,很多重要的任务都会跑在其之下.例如:Swt_Task
oom_task
system_wq
tcpip_thread
SendToSer
SendToTelnet
eth_irq_task
TouchEventHandler
USB_GIANT_Task
此处不细讲这些任务,在其他篇幅有介绍,但光看名字也能猜个八九,请自行翻看.
- 紧接着
KProcess
以CLONE_FILES
的方式 fork了一个 名为KIdle
的子进程(0号进程). - 内核态的所有进程都来自2号进程这位老同志,子子孙孙,代代相传,形成一颗家族树,和人类的传承所不同的是,它们往往是白发人送黑发人,子孙进程往往都是短命鬼,老祖宗最能活,子孙都死绝了它还在,有些收尸的工作要交给它干.
0 号进程 KIdle
0号进程是内核创建的第二个进程,在OsKernelInitProcess
的末尾将KProcess
设为当前进程后,紧接着就fork
了0号进程.为什么一定要先设置当前进程,因为fork需要一个父进程,而此时系统处于启动阶段,并没有当前进程. 是的,您没有看错.进程是操作系统为方便管理资源而衍生出来的概念,系统并不是非要进程,任务才能运行的. 开机阶段就是啥都没有,默认跑在svc模式下,默认起始地址reset_vector
都是由硬件上电后规定的. 进程,线程都是跑起来后慢慢赋予的意义.OsCurrProcessSet
是从软件层面赋予了此为当前进程的这个概念.KProcess
是内核设置的第一个当前进程.有了它,就可以fork, fork, fork !
解读
- 看过fork篇的可能发现了一个参数,
KIdle
被创建的方式和通过系统调用创建的方式不一样,一个用的是CLONE_FILES
,一个是CLONE_SIGHAND
具体的创建方式如下: KIdle
创建了一个名为Idle
的任务,任务的入口函数为OsIdleTask
,这是个空闲任务,啥也不干的.专门用来给cpu休息的,cpu空闲时就待在这个任务里等活干.- fork 内核态进程和fork用户态进程有个地方会不一样,就是SP寄存器的值.fork用户态的进程一次调用两次返回(父子进程各一次),返回的位置一样(是因为拷贝了父进程陷入内核时的上下文).所以可以通过返回值来判断是父还是子返回.这个在fork篇中有详细的描述.请自行翻看. 但fork内核态进程虽也有两次返回,但是返回的位置却不一样,子进程的返回位置是由内核指定的,例如:
Idle
任务的入口函数为OsIdleTask
.详见代码: - 结论是创建0号进程中的
OsCreateIdleProcess
调用LOS_Fork
后只会有一次返回.而且返回值为0,因为g_freeProcess
中0号进程还没有被分配.详见代码,注意看最后的注释:
1号进程 init
1号进程为用户态的老祖宗.创建过程如下, 省略了不相干的代码.
解读
- 从代码中可以看出用户态的老祖宗创建过程有点意思,首先它的源头和内核态老祖宗一样都在
OsMain
. - 通过创建一个分离模式,优先级为10的系统任务
SystemInit
,来完成.任务的入口函数SystemInit()
的实现由平台集成商来指定. 本篇采用了hi3516dv300
的实现.也就是说用户态祖宗的创建是在sysTask.uwStackSize = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE;//16K
栈中完成的.这个任务归属于内核进程KProcess
. - 用户态老祖宗的名字叫
Init
,优先级为28级. - 用户态的每个进程有独立的虚拟进程空间
vmSpace
,拥有独立的内存映射表(L1,L2表),申请的内存需要重新映射,映射过程在内存系列篇中有详细的说明. init
创建了一个任务,任务的入口地址为__user_init_entry
,由编译器指定.- 用户态进程是指应有程序运行的进程,通过动态加载ELF文件的方式启动.具体加载流程系列篇有讲解,不细说.用户态进程运行在用户空间,但通过系统调用可陷入内核空间.具体看这张图:
百万汉字注解.精读内核源码
百篇博客分析.深挖内核地基
给鸿蒙内核源码加注释过程中,整理出以下文章。内容立足源码,常以生活场景打比方尽可能多的将内核知识点置入某种场景,具有画面感,容易理解记忆。说别人能听得懂的话很重要! 百篇博客绝不是百度教条式的在说一堆诘屈聱牙的概念,那没什么意思。更希望让内核变得栩栩如生,倍感亲切.确实有难度,自不量力,但已经出发,回头已是不可能的了。 😛
与代码有bug需不断debug一样,文章和注解内容会存在不少错漏之处,请多包涵,但会反复修正,持续更新,.xx
代表修改的次数,精雕细琢,言简意赅,力求打造精品内容。
基础工具>> 双向链表 | 位图管理 | 用栈方式 | 定时器 | 原子操作 | 时间管理 |
加载运行>> ELF格式 | ELF解析 | 静态链接 | 重定位 | 进程映像 |
进程管理>> 进程管理 | 进程概念 | Fork | 特殊进程 | 进程回收 | 信号生产 | 信号消费 | Shell编辑 | Shell解析 |
编译构建>> 编译环境 | 编译过程 | 环境脚本 | 构建工具 | gn应用 | 忍者ninja |
进程通讯>> 自旋锁 | 互斥锁 | 进程通讯 | 信号量 | 事件控制 | 消息队列 |
内存管理>> 内存分配 | 内存管理 | 内存汇编 | 内存映射 | 内存规则 | 物理内存 |
前因后果>> 总目录 | 调度故事 | 内存主奴 | 源码注释 | 源码结构 | 静态站点 |
任务管理>> 时钟任务 | 任务调度 | 任务管理 | 调度队列 | 调度机制 | 线程概念 | 并发并行 | CPU | 系统调用 | 任务切换 |
文件系统>> 文件概念 | 文件系统 | 索引节点 | 挂载目录 | 根文件系统 | 字符设备 | VFS | 文件句柄 | 管道文件 |
硬件架构>> 汇编基础 | 汇编传参 | 工作模式 | 寄存器 | 异常接管 | 汇编汇总 | 中断切换 | 中断概念 | 中断管理 |
鸿蒙研究站 | 每天死磕一点点,原创不易,欢迎转载,但请注明出处。
