官方基本概念
-
从系统的角度看,进程是资源管理单元。进程可以使用或等待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或父进程退出时回收。
开始正式分析
对应张大爷的故事,进程就是那些在场馆外32个队列里排队的,那些队列就是进程的就绪队列。
请注意 进程是资源管理单元 ,而非最终调度单元,调度单元是谁?是 Task
,看下官方对应状态定义
#define OS_PROCESS_STATUS_INIT 0x0010U //进程初始状态
#define OS_PROCESS_STATUS_READY 0x0020U //进程就绪状态
#define OS_PROCESS_STATUS_RUNNING 0x0040U //进程运行状态
#define OS_PROCESS_STATUS_PEND 0x0080U //进程阻塞状态
#define OS_PROCESS_STATUS_ZOMBIES 0x100U //进程僵死状态
一个进程从创建到消亡过程,在内核肯定是极其复杂的。为了方便理解进程,整个系列文章笔者会用张大爷的故事打比方,从生活中的例子来将神秘的系统内核外化解剖出来给大家看。一件这么复杂的事情肯定会有个复杂的结构体来承载,它就是LosProcessCB(进程控制块),代码很长但必须全部拿出来。
LITE_OS_SEC_BSS LosProcessCB *g_runProcess[LOSCFG_KERNEL_CORE_NUM]; //用一个指针数组记录进程运行,LOSCFG_KERNEL_CORE_NUM 为 CPU的核数
LITE_OS_SEC_BSS LosProcessCB *g_processCBArray = NULL;//进程池,最大进程数为 64个
LITE_OS_SEC_DATA_INIT STATIC LOS_DL_LIST g_freeProcess;//记录空闲的进程链表
LITE_OS_SEC_DATA_INIT STATIC LOS_DL_LIST g_processRecyleList;//记录回收的进程列表
typedef struct ProcessCB {
CHAR processName[OS_PCB_NAME_LEN]; /**< Process name */ //进程名称
UINT32 processID; /**< process ID = leader thread ID */ //进程ID,由进程池分配,范围[0,64]
UINT16 processStatus; /**< [15:4] process Status; [3:0] The number of threads currently
running in the process *///这里设计很巧妙.用一个16表示了两层逻辑 数量和状态,点赞!
UINT16 priority; /**< process priority */ //进程优先级
UINT16 policy; /**< process policy */ //进程的调度方式,默认抢占式
UINT16 timeSlice; /**< Remaining time slice *///进程时间片,默认2个tick
UINT16 consoleID; /**< The console id of task belongs *///任务的控制台id归属
UINT16 processMode; /**< Kernel Mode:0; User Mode:1; */ //模式指定为内核还是用户进程
UINT32 parentProcessID; /**< Parent process ID */ //父进程ID
UINT32 exitCode; /**< process exit status */ //进程退出状态码
LOS_DL_LIST pendList; /**< Block list to which the process belongs */ //进程所属的阻塞列表,如果因拿锁失败,就由此节点挂到等锁链表上
LOS_DL_LIST childrenList; /**< my children process list */ //孩子进程都挂到这里,形成双循环链表
LOS_DL_LIST exitChildList; /**< my exit children process list */ //那些要退出孩子进程挂到这里,白发人送黑发人。
LOS_DL_LIST siblingList; /**< linkage in my parent's children list */ //兄弟进程链表, 56个民族是一家,来自同一个父进程.
ProcessGroup *group; /**< Process group to which a process belongs */ //所属进程组
LOS_DL_LIST subordinateGroupList; /**< linkage in my group list */ //进程是组长时,有哪些组员进程
UINT32 threadGroupID; /**< Which thread group , is the main thread ID of the process */ //哪个线程组是进程的主线程ID
UINT32 threadScheduleMap; /**< The scheduling bitmap table for the thread group of the
process */ //进程的各线程调度位图
LOS_DL_LIST threadSiblingList; /**< List of threads under this process *///进程的线程(任务)列表
LOS_DL_LIST threadPriQueueList[OS_PRIORITY_QUEUE_NUM]; /**< The process's thread group schedules the
priority hash table */ //进程的线程组调度优先级哈希表
volatile UINT32 threadNumber