v02.06 鸿蒙内核源码分析(进程管理) | 谁在管理内核资源 原创
子曰:“始吾于人也,听其言而信其行,今吾于人也,听其言而观其行。于予与改是。”《论语》:公冶长篇
百篇博客分析.本篇为: (进程管理篇) | 谁在管理内核资源
进程管理相关篇为:
- v02.06 鸿蒙内核源码分析(进程管理) | 谁在管理内核资源
- v24.03 鸿蒙内核源码分析(进程概念) | 如何更好的理解进程
- v45.05 鸿蒙内核源码分析(Fork) | 一次调用 两次返回
- v46.05 鸿蒙内核源码分析(特殊进程) | 老鼠生儿会打洞
- v47.02 鸿蒙内核源码分析(进程回收) | 临终托孤的短命娃
- v48.05 鸿蒙内核源码分析(信号生产) | 年过半百 活力十足
- v49.03 鸿蒙内核源码分析(信号消费) | 谁让CPU连续四次换栈运行
- v71.03 鸿蒙内核源码分析(Shell编辑) | 两个任务 三个阶段
- v72.01 鸿蒙内核源码分析(Shell解析) | 应用窥伺内核的窗口
进程管理示意图
解读
以下几句话很重要,是理解鸿蒙内核关于进程部分的关键之所在,务必将空间,态,栈这些概念理解清楚.
- 内存空间分用户空间和内核空间,内核严格界定了它们的范围,应用程序一般是运行在用户空间,但是有些功能需要也只能由内核提供,比如读写文件,这就引出了应用程序运行时的两种状态(用户态和内核态).
- 系统调用(
syscall
)将用户态和内核态隔离开来,用户态线程想访问内核资源必须经过系统调用切到该线程的内核态.内核态下的指令在该线程的内核栈中运行,内核栈由内核空间提供. - 鸿蒙内核只有一个内核进程空间,被
KIdle
,KProcess
两个进程共用,这两个进程所管理的线程只有内核态,同时也只会有内核栈. Init
进程(也称1
号进程)是所有应用进程的祖宗KIdle
进程(也称0
号进程)是内核给CPU
开的小灶,CPU
没事干的时候就呆在KIdle
进程中休息KProcess
进程(也称2
号进程),内核的主要工作是通过KProcess
来完成的,- 图中的线程和
task
是一个意思,进程是资源管理单元, 任务是内核的调度单元,让CPU
干活的是任务. - 对以上说明还不甚理解的,请前往系列篇翻看进程模块其他文章.
基本概念
-
从系统的角度看,进程是资源管理单元。进程可以使用或等待
CPU
、使用内存空间等系统资源,并独立于其它进程运行。 -
OpenHarmony
内核的进程模块可以给用户提供多个进程,实现了进程之间的切换和通信,帮助用户管理业务程序流程。这样用户可以将更多的精力投入到业务功能的实现中。 -
OpenHarmony
内核中的进程采用抢占式调度机制,支持时间片轮转调度方式和FIFO
调度机制。 -
OpenHarmony
内核的进程一共有32
个优先级(0-31
),用户进程可配置的优先级有22
个(10-31),最高优先级为10
,最低优先级为31
。 -
高优先级的进程可抢占低优先级进程,低优先级进程必须在高优先级进程阻塞或结束后才能得到调度。
-
每一个用户态进程均拥有自己独立的进程空间,相互之间不可见,实现进程间隔离。
-
用户态根进程
init
由内核态创建,其它用户态进程均由init
进程fork
而来。
进程状态流转
-
初始化(
Init
):该进程正在被创建。 -
就绪(
Ready
):该进程在就绪列表中,等待CPU调度。 -
运行(
Running
):该进程正在运行。 -
阻塞(
Pend
):该进程被阻塞挂起。本进程内所有的线程均被阻塞时,进程被阻塞挂起。 -
僵尸态(
Zombies
):该进程运行结束,等待父进程回收其控制块资源。
-
Init→Ready
:进程创建或fork时,拿到该进程控制块后进入Init状态,处于进程初始化阶段,当进程初始化完成将进程插入调度队列,此时进程进入就绪状态。
-
Ready→Running
:进程创建后进入就绪态,发生进程切换时,就绪列表中最高优先级的进程被执行,从而进入运行态。若此时该进程中已无其它线程处于就绪态,则该进程从就绪列表删除,只处于运行态;若此时该进程中还有其它线程处于就绪态,则该进程依旧在就绪队列,此时进程的就绪态和运行态共存。
-
Running→Pend
:进程内所有的线程均处于阻塞态时,进程在最后一个线程转为阻塞态时,同步进入阻塞态,然后发生进程切换。
-
Pend→Ready
/Pend→Running
:阻塞进程内的任意线程恢复就绪态时,进程被加入到就绪队列,同步转为就绪态,若此时发生进程切换,则进程状态由就绪态转为运行态。
-
Ready→Pend
:进程内的最后一个就绪态线程处于阻塞态时,进程从就绪列表中删除,进程由就绪态转为阻塞态。
-
Running→Ready
:进程由运行态转为就绪态的情况有以下两种:
- 有更高优先级的进程创建或者恢复后,会发生进程调度,此刻就绪列表中最高优先级进程变为运行态,那么原先运行的进程由运行态变为就绪态。
- 若进程的调度策略为
SCHED_RR
,且存在同一优先级的另一个进程处于就绪态,则该进程的时间片消耗光之后,该进程由运行态转为就绪态,另一个同优先级的进程由就绪态转为运行态。
-
Running→Zombies
:当进程的主线程或所有线程运行结束后,进程由运行态转为僵尸态,等待父进程回收资源。
使用场景
进程创建后,用户只能操作自己进程空间的资源,无法操作其它进程的资源(共享资源除外)。用户态允许进程挂起,恢复,延时等操作,同时也可以设置用户态进程调度优先级和调度策略,获取进程调度优先级和调度策略。进程结束的时候,进程会主动释放持有的进程资源,但持有的进程pid
资源需要父进程通过wait/waitpid
或父进程退出时回收,可以前往查看百篇博客系列篇:进程回收篇.
开始正式分析
请注意进程是资源管理单元 ,而非最终调度(执行)单元,调度单元是谁?是 Task
,也叫任务,也可以叫线程(Thread
).
看下官方对应状态定义
一个进程从创建到消亡过程,在内核肯定是极其复杂的。一件这么复杂的事情肯定会有个复杂的结构体来承载,它就是LosProcessCB
(进程控制块),代码很长但必须全部拿出来。
进程的模式有两种,内核态和用户态,能想到main
函数中肯定会创建一个内核态的最高优先级进程,他就是 KProcess
通过task
命令查看任务运行状态,可以看到 KProcess
进程 ,看名字就知道是一个内核进程,在系统启动时创建,图中可以看到 KProcess
的task
运行情况,从表里可以看到KProcess
内有 10
几个task
进程模块是如何初始化的
注意应用进程和内核进程的祖先是不一样的,有各自的祖先根.分别是g_userInitProcess
(1
号进程) 和 g_kernelInitProcess
(2
号进程)
代码已经很清楚,创建了一个进程池,默认64
个进程,也就是不改宏LOSCFG_BASE_CORE_PROCESS_LIMIT
的情况下 系统最多是64
个进程,但有两个进程先被占用,用户态和内核态各一个,他们是后续创建进程的根,所以最多留给外面的只有62
个进程可创建,代码的最后两个根进程的task
阻塞链表被清空了,因为没有阻塞任务当然要清空.
内核根进程创建过程
创建Kprocess
进程,也就是线程池中的2
号进程g_kernelInitProcess
,设为最高优先级 0
应用根进程创建过程
创建Init
进程,也就是线程池中的1
号进程g_userInitProcess
,优先级为28
,好低啊
百篇博客分析.深挖内核地基
- 给鸿蒙内核源码加注释过程中,整理出以下文章。内容立足源码,常以生活场景打比方尽可能多的将内核知识点置入某种场景,具有画面感,容易理解记忆。说别人能听得懂的话很重要! 百篇博客绝不是百度教条式的在说一堆诘屈聱牙的概念,那没什么意思。更希望让内核变得栩栩如生,倍感亲切.确实有难度,自不量力,但已经出发,回头已是不可能的了。 😛
- 与代码有bug需不断debug一样,文章和注解内容会存在不少错漏之处,请多包涵,但会反复修正,持续更新,v**.xx 代表文章序号和修改的次数,精雕细琢,言简意赅,力求打造精品内容。
按功能模块:
- 前因后果 >> 总目录 | 调度故事 | 内存主奴 | 源码注释 | 源码结构 | 静态站点 |
- 基础工具 >> 双向链表 | 位图管理 | 用栈方式 | 定时器 | 原子操作 | 时间管理 |
- 加载运行 >> ELF格式 | ELF解析 | 静态链接 | 重定位 | 进程映像 |
- 进程管理 >> 进程管理 | 进程概念 | Fork | 特殊进程 | 进程回收 | 信号生产 | 信号消费 | Shell编辑 | Shell解析 |
- 编译构建 >> 编译环境 | 编译过程 | 环境脚本 | 构建工具 | gn应用 | 忍者ninja |
- 进程通讯 >> 自旋锁 | 互斥锁 | 进程通讯 | 信号量 | 事件控制 | 消息队列 |
- 内存管理 >> 内存分配 | 内存管理 | 内存汇编 | 内存映射 | 内存规则 | 物理内存 |
- 任务管理 >> 时钟任务 | 任务调度 | 任务管理 | 调度队列 | 调度机制 | 线程概念 | 并发并行 | CPU | 系统调用 | 任务切换 |
- 文件系统 >> 文件概念 | 文件系统 | 索引节点 | 挂载目录 | 根文件系统 | 字符设备 | VFS | 文件句柄 | 管道文件 |
- 硬件架构 >> 汇编基础 | 汇编传参 | 工作模式 | 寄存器 | 异常接管 | 汇编汇总 | 中断切换 | 中断概念 | 中断管理 |
百万汉字注解.精读内核源码
四大码仓中文注解 . 定期同步官方代码
鸿蒙研究站( weharmonyos ) | 每天死磕一点点,原创不易,欢迎转载,请注明出处。若能支持点赞则更佳,感谢每一份支持。
对这句话“代码的最后两个根进程的task阻塞链表被清空了,因为没有阻塞任务当然要清空.”有些疑问。
其实我理解应该是,1号用户根进程g_userInitProcess和2号内核根进程g_kernelInitProcess被使用了,需要从`g_freeProcess`空闲链表上移除。这里和阻塞任务没有关系。