鸿蒙内核源码:进程是内核的资源管理单元

官方基本概念
从系统的角度看,进程是资源管理单元。进程可以使用或等待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; /**< number of threads alive under this process */ //此进程下的活动线程数 uint32 threadcount; /**< total number of threads created under this process */ //在此进程下创建的线程总数 los_dl_list waitlist; /**< the process holds the waitlits to support wait/waitpid *///进程持有等待链表以支持wait/waitpid#if (loscfg_kernel_smp == yes) uint32 timercpu; /**< cpu core number of this task is delayed or pended *///统计各线程被延期或阻塞的时间#endif uintptr sighandler; /**< signal handler */ //信号处理函数,处理如 sigsys 等信号 sigset_t sigshare; /**< signal share bit */ //信号共享位#if (loscfg_kernel_liteipc == yes) procipcinfo ipcinfo; /**< memory pool for lite ipc */ //用于进程间通讯的虚拟设备文件系统,设备装载点为 /dev/lite_ipc#endif losvmspace *vmspace; /**< vmm space for processes */ //虚拟空间,描述进程虚拟内存的数据结构,linux称为内存描述符#ifdef loscfg_fs_vfs struct files_struct *files; /**< files held by the process */ //进程所持有的所有文件,注者称之为进程的文件管理器#endif //每个进程都有属于自己的文件管理器,记录对文件的操作. 注意:一个文件可以被多个进程操作 timer_t timerid; /**< itimer */#ifdef loscfg_security_capability //安全能力 user *user; //进程的拥有者 uint32 capability; //安全能力范围 对应 cap_setgid#endif#ifdef loscfg_security_vid timeridmap timeridmap;#endif#ifdef loscfg_drivers_tzdriver struct file *execfile; /**< exec bin of the process */#endif mode_t umask;} losprocesscb; 进程的模式有两种,内核态和用户态,能想到main函数中肯定会创建一个内核态的最高优先级进程,他就是 kprocess
通过task命令查看任务运行状态,可以看到 kprocess 进程 ,看名字就知道是一个内核进程,在系统启动时创建,图中可以看到 kprocess 的task运行情况,从表里可以看到kprocess内有 10几个task

进程模块是如何初始化的
kprocess 在张大爷的故事里相当于场馆的工作人员,他们也要接受张大爷的调度排队进场,但他们的优先级是最高的0级,他们进场后需完成场馆的准备工作,再开门做生意。如果需要多个工作人员怎么办,就是通过fork,简单说就是复制一个,复制的前提是需要有一个,鸿蒙里就是kprocess,其他工作人员都是通过它fork的。 那用户怎么来的呢?就是真正要排队的人也是一样,先创建一个用户祖先,其他用户皆由祖先fork来的。 注意用户进程和内核进程的祖先是不一样的,有各自的祖先根.分别是g_userinitprocess(1号) 和 g_kernelinitprocess(2号)
/****************************************************************************** 并发(concurrent):多个线程在单个核心运行,同一时间一个线程运行,系统不停切换线程, 看起来像同时运行,实际上是线程不停切换 并行(parallel)每个线程分配给独立的cpu核心,线程同时运行 单核cpu多个进程或多个线程内能实现并发(微观上的串行,宏观上的并行) 多核cpu线程间可以实现宏观和微观上的并行 lite_os_sec_bss 和 lite_os_sec_data_init 是告诉编译器这些全局变量放在哪个数据段******************************************************************************/lite_os_sec_bss losprocesscb *g_runprocess[loscfg_kernel_core_num];// cpu内核个数,超过一个就实现了并行lite_os_sec_bss losprocesscb *g_processcbarray = null; // 进程池数组lite_os_sec_data_init static los_dl_list g_freeprocess;// 空闲状态下的进程链表, .个人觉得应该取名为 g_freeprocesslist @note_thinkinglite_os_sec_data_init static los_dl_list g_processrecylelist;// 需要回收的进程列表lite_os_sec_bss uint32 g_userinitprocess = os_invalid_value;// 用户态的初始init进程,用户态下其他进程由它 forklite_os_sec_bss uint32 g_kernelinitprocess = os_invalid_value;// 内核态初始kprocess进程,内核态下其他进程由它 forklite_os_sec_bss uint32 g_kernelidleprocess = os_invalid_value;// 内核态idle进程,由kprocess forklite_os_sec_bss uint32 g_processmaxnum;// 进程最大数量,默认64个lite_os_sec_bss processgroup *g_processgroup = null;// 全局进程组,负责管理所有进程组//进程模块初始化,被编译放在代码段 .init 中lite_os_sec_text_init uint32 osprocessinit(void){ uint32 index; uint32 size; g_processmaxnum = loscfg_base_core_process_limit;//默认支持64个进程 size = g_processmaxnum * sizeof(losprocesscb);//算出总大小 g_processcbarray = (losprocesscb *)los_memalloc(m_aucsysmem1, size);// 进程池,占用内核堆,内存池分配 if (g_processcbarray == null) { return los_nok; } (void)memset_s(g_processcbarray, size, 0, size);//安全方式重置清0 los_listinit(&g_freeprocess);//进程空闲链表初始化,创建一个进程时从g_freeprocess中申请一个进程描述符使用 los_listinit(&g_processrecylelist);//进程回收链表初始化,回收完成后进入g_freeprocess等待再次被申请使用 for (index = 0; index processstatus &= ~os_process_status_init;// 进程初始化位 置1 g_processgroup = processcb->group;//全局进程组指向了kprocess所在的进程组 los_listinit(&g_processgroup->grouplist);// 进程组链表初始化 oscurrprocessset(processcb);// 设置为当前进程 return oscreateidleprocess();// 创建一个空闲状态的进程}//创建一个名叫kidle的进程,给cpu空闲的时候使用static uint32 oscreateidleprocess(void){ uint32 ret; char *idlename = idle; losprocesscb *idleprocess = null; percpu *percpu = ospercpuget(); uint32 *idletaskid = &percpu->idletaskid;//得到cpu的idle task ret = oscreateresourcefreetask();// 创建一个资源回收任务,优先级为5 用于回收进程退出时的各种资源 if (ret != los_ok) { return ret; } //创建一个名叫kidle的进程,并创建一个idle task,cpu空闲的时候就待在 idle task中等待被唤醒 ret = los_fork(clone_files, kidle, (tsk_entry_func)osidletask, loscfg_base_core_tsk_idle_stack_size); if (ret threadgroupid;//绑定cpu的idletask,或者说改变cpu现有的idle任务 os_tcb_from_tid(*idletaskid)->taskstatus |= os_task_flag_system_task;//设定idle task 为一个系统任务#if (loscfg_kernel_smp == yes) os_tcb_from_tid(*idletaskid)->cpuaffimask = cpuid_to_affi_mask(archcurrcpuid());//多核cpu的任务指定,防止乱串了,注意多核才会有并行处理#endif (void)memset_s(os_tcb_from_tid(*idletaskid)->taskname, os_tcb_name_len, 0, os_tcb_name_len);//task 名字先清0 (void)memcpy_s(os_tcb_from_tid(*idletaskid)->taskname, os_tcb_name_len, idlename, strlen(idlename));//task 名字叫 idle return los_ok;}用户态根进程创建过程 创建init进程,也就是线程池中的1号进程g_userinitprocess,优先级为 28,好低啊


工信部将提高多晶硅产业准入条件
电气图的分类
全球首轮5G网络服务情况调查报告正式公布澳大利亚等地的5G比4G还慢
高压功率运算放大器HJGPA61A概述
scada燃气系统的详细介绍
鸿蒙内核源码:进程是内核的资源管理单元
杨晓东辞去中微星董事职务 张韵东或接替
君正集成电路 JZ4775嵌入式主板简介
博世力士乐开放自动化赋能中国智能制造
Kindle 2拆解新机型改变此前的参考设计
LTM4632为三个电源轨提供完整的高性能稳压器解决方案
智能设备助推,中国能否占领MEMS市场?
科大讯飞在人工智能领域所取得的进展以及对于未来的展望
桁架机器人的常用使用范围
重新审视百元基因测序,多款APP涉嫌过度收集个人生物识别信息
FPGA设计中的HLS 工具应用
关于毫米波雷达的性能分析和应用
什么是电涌,电涌的产生及危害
示波器差分探头怎么接线
5G双频WiFi模块SKW93A在工业传输数据的优势有哪些