鸿蒙内核图,【鸿蒙学院】鸿蒙内核源码分析(特殊进程篇)

三个进程

鸿蒙有三个特殊的进程,创建顺序如下:

2号进程,KProcess,为内核态根进程.启动过程中创建.0号进程,KIdle为内核态第二个进程,它是通过KProcess fork 而来的.这有点难理解.1号进程,init,为用户态根进程.由任务SystemInit创建.

48b957feac85eb37fbcc75cd91decdd8.png

发现没有在图中看不到0号进程,在看完本篇之后请想想为什么?

家族式管理

进程(process)是家族式管理,总体分为两大家族,用户态家族和内核态家族.用户态的进程是平民阶层,干着各行各业的活,权利有限,人数众多,活动范围有限.中南海肯定不能随便进出.这个阶层有个共同的老祖宗g_userInitProcess (1号进程).

7457b69abcf5581407d2e9ac70c6dbed.png

内核态的进程是贵族阶层,管理平民阶层的,维持平民生活秩序的,拥有超级权限,人数不多.这个阶层老祖宗是 g_kernelInitProcess(2号进程).

bfc46a8c67cdd2bacf608e8989322986.png

这两个阶层可以相互流动吗,有没有可以通过高考改变命运的机会? 答案是: 绝对不可能!!! 龙生龙,凤生凤,老鼠生儿会打洞.从老祖宗创建的那一刻起就被刻在基因里了,抹不掉了. 因为所有的进程都是由这两位老同志克隆(clone)来的,继承了这份基因.LosProcessCB有专门的标签来processMode区分这两个阶层.整个鸿蒙内核源码并没有提供改变命运机会的set函数.

ec517d2ab87b3c7f7790e06f5e823496.png

2号进程 KProcess

2号进程为内核态的老祖宗,也是内核创建的第一个进程,源码过程如下,省略了不相干的代码.

933cadc93fec54def459051519b84946.png

630087e343eb16a1a69dcbf14afd6a85.png

解读

main函数在系列篇中会单独讲,请留意自行翻看,它是在开机之初在SVC模式下创建的.内核态老祖宗的名字叫 KProcess,优先级为最高 0 级."KProcess"进程是长期活跃的,很多重要的任务都会跑在其之下.例如:Swt_Taskoom_tasksystem_wqtcpip_threadSendToSerSendToTelneteth_irq_taskTouchEventHandlerUSB_GIANT_Task 此处不细讲这些任务,在其他篇幅有介绍,但光看名字也能猜个八九,请自行翻看.紧接着KProcess 以CLONE_FILES的方式 fork了一个 名为"KIdle"的子进程.内核态的所有进程都来自2号进程这位老同志,子子孙孙,代代相传,形成一颗家族树,和人类的传承所不同的是,它们往往是白发人送黑发人,子孙进程往往都是短命鬼,老祖宗最能活,子孙都死绝了它还在,有些收尸的工作要交给它干.

0 号进程 KIdle

0号进程是内核创建的第一个进程,在OsKernelInitProcess的末尾将2号进程设为当前进程后,紧接着就fork了0号进程.为什么一定要先设置当前进程,因为fork需要一个父进程,而此时系统处于启动阶段,并没有当前进程. 是的,你没有看错.进程是操作系统为方便管理资源而衍生出来的概念,系统并不是非要进程,任务才能运行的. 开机阶段就是啥都没有,默认跑在svc模式下,默认指定了入口地址reset_vector都是由硬件上电后规定的. 进程,线程都是跑起来后慢慢赋予的含义,OsCurrProcessSet是从软件层面赋予了此为当前进程的这个概念.此处是内核设置的第一个当前进程.

9809dfb7bd2069e1ed327606a395662c.png

b5f14f2a412e2a544920a755bcc46505.png

解读

看过fork篇的可能发现了一个参数, KIdle被创建的方式和通过系统调用创建的方式不一样,一个用的是CLONE_FILES,一个是 CLONE_SIGHAND 具体的创建方式如下:

22d9f6ec0c4f158823bc0131a7765f96.png

KIdle创建了一个名为Idle的任务,任务的入口函数为OsIdleTask,这是个空闲任务,啥也不干的.专门用来给cpu休息的,cpu空闲时就待在这个任务里等活干.

f1e8b27ca91ff475888dcca3ccd3f2c6.png

fork 内核态进程和fork 用户态进程有个地方会不一样,就是SP寄存器的值.fork用户态的进程 一次调用两次返回(父子进程各一次),返回的位置一样(是因为拷贝了父进程陷入内核时的上下文).所以只能通过返回值来判断是父还是子返回.这个在fork篇中有详细的描述.请自行翻看. 但fork内核态进程虽也有两次返回,但是返回的位置却不一样,子进程的返回位置是由内核指定的.例如:OsIdleTask就是入口函数.详见代码:

20695749ff3f747d317af6e42ebc8064.png

f53ea56fbfa6ecf97a8219195200ad6d.png

结论是创建0号进程中的OsCreateIdleProcess调用LOS_Fork后只会有一次返回.而且返回值为0,因为 g_freeProcess中0号进程还没有被分配.详见代码,注意看最后的注释:

c6caa8b7786a80df59b711e9fb2eb3b4.png

03e3caf3d9719be62a1e92315671784d.png

1号进程 init

1号进程为用户态的老祖宗源码过程如下, 省略了不相干的代码.

2cd83d6a8a1832c5fb484f1a708e9715.png

fde882a4ad1dffa40ab78e6f6f9e7547.png

e9343f298f84ca9df36a2c05b23461fc.png

e26a674b4251d6d68ee196317f294751.png

eee0d84902741d93fb9db28413ab90d2.png

解读

从代码中可以看出用户态的老祖宗创建过程有点意思,首先它的源头和内核态老祖宗一样都在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文件的方式启动.具体加载流程系列篇有讲解,不细说.用户态进程运行在用户空间,但通过系统调用可以陷入内核空间.具体看这张图:

e4862f4c3caf98c2c203e397ee37d269.png

想了解更多精彩内容,快来关注计算机java编程

举报/反馈

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值