深度解析鸿蒙内核最重要的结构体

谁是鸿蒙内核最重要的结构体?
答案一定是: los_dl_list(双向链表),它长这样.
typedef struct los_dl_list {//双向链表,内核最重要结构体 struct los_dl_list *pstprev; /**< current node's pointer to the previous node *///前驱节点(左手) struct los_dl_list *pstnext; /**pstnext = list; list->pstprev = list;}//将指定节点挂到双向链表头部lite_os_sec_alw_inline static inline void los_listadd(los_dl_list *list, los_dl_list *node){ node->pstnext = list->pstnext; node->pstprev = list; list->pstnext->pstprev = node; list->pstnext = node;}//将指定节点从链表中删除,自己把自己摘掉lite_os_sec_alw_inline static inline void los_listdelete(los_dl_list *node){ node->pstnext->pstprev = node->pstprev; node->pstprev->pstnext = node->pstnext; node->pstnext = null; node->pstprev = null;}
强大的宏
除了内联函数,对双向遍历的初始化,定位,遍历 等等操作提供了更强大的宏支持.使内核以极其简洁高效的代码实现复杂逻辑的处理.
//定义一个节点并初始化为双向链表节点#define los_dl_list_head(list) los_dl_list list = { &(list), &(list) }//获取指定结构体内的成员相对于结构体起始地址的偏移量#define los_off_set_of(type, member) ((uintptr)&((type *)0)->member)//获取包含链表的结构体地址,接口的第一个入参表示的是链表中的某个节点,第二个入参是要获取的结构体名称,第三个入参是链表在该结构体中的名称#define los_dl_list_entry(item, type, member) \ ((type *)(void *)((char *)(item) - los_off_set_of(type, member)))//遍历双向链表#define los_dl_list_for_each(item, list) \ for (item = (list)->pstnext; \ (item) != (list); \ item = (item)->pstnext)//遍历指定双向链表,获取包含该链表节点的结构体地址,并存储包含当前节点的后继节点的结构体地址#define los_dl_list_for_each_entry_safe(item, next, list, type, member) \ for (item = los_dl_list_entry((list)->pstnext, type, member), \ next = los_dl_list_entry((item)->member.pstnext, type, member); \ &(item)->member != (list); \ item = next, next = los_dl_list_entry((item)->member.pstnext, type, member))//遍历指定双向链表,获取包含该链表节点的结构体地址#define los_dl_list_for_each_entry(item, list, type, member) \ for (item = los_dl_list_entry((list)->pstnext, type, member); \ &(item)->member != (list); \ item = los_dl_list_entry((item)->member.pstnext, type, member)) 例如在调度算法中获取当前最高优先级的任务时,就需要遍历整个进程和进程任务的所有就绪列表. los_dl_list_for_each_entry高效的解决了层层循环的问题,让代码简洁易懂.
lite_os_sec_text_minor lostaskcb *osgettoptask(void){ uint32 priority, processpriority; uint32 bitmap; uint32 processbitmap; lostaskcb *newtask = null;#if (loscfg_kernel_smp == yes) uint32 cpuid = archcurrcpuid();#endif losprocesscb *processcb = null; processbitmap = g_priqueuebitmap; while (processbitmap) { processpriority = clz(processbitmap); los_dl_list_for_each_entry(processcb, &g_priqueuelist[processpriority], losprocesscb, pendlist) { bitmap = processcb->threadschedulemap; while (bitmap) { priority = clz(bitmap); los_dl_list_for_each_entry(newtask, &processcb->threadpriqueuelist[priority], lostaskcb, pendlist) {#if (loscfg_kernel_smp == yes) if (newtask->cpuaffimask & (1u threadpriqueuelist, &processcb->threadschedulemap, &newtask->pendlist); osdequeemptyschedmap(processcb); goto out;#if (loscfg_kernel_smp == yes) }#endif } bitmap &= ~(1u << (os_priority_queue_num - priority - 1)); } } processbitmap &= ~(1u << (os_priority_queue_num - processpriority - 1)); }out: return newtask;} 结构体的最爱
los_dl_list是复杂结构体的最爱,以下举例 processcb(进程控制块)是描述一个进程的所有信息,其中用到了 8个双向链表,这简直比章鱼还牛逼,章鱼也才四双触手,但进程有8双(16只)触手.
typedef struct processcb { //...此处省略其他变量 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个民族是一家,来自同一个父进程. los_dl_list subordinategrouplist; /**< linkage in my group list */ //进程是组长时,有哪些组员进程 los_dl_list threadsiblinglist; /**< list of threads under this process *///进程的线程(任务)列表 los_dl_list threadpriqueuelist[os_priority_queue_num]; /**< the process's thread group schedules thepriority hash table */ //进程的线程组调度优先级哈希表 los_dl_list waitlist; /**< the process holds the waitlits to support wait/waitpid *///进程持有等待链表以支持wait/waitpid} losprocesscb; 解读
pendlist 个人认为它是鸿蒙内核功能最多的一个链表,它远不止字面意思阻塞链表这么简单,只有深入解读源码后才能体会它真的是太会来事了,一般把它理解为阻塞链表就行.上面挂的是处于阻塞状态的进程.
childrenlist孩子链表,所有由它fork出来的进程都挂到这个链表上.上面的孩子进程在死亡前会将自己从上面摘出去,转而挂到exitchildlist链表上.
exitchildlist退出孩子链表,进入死亡程序的进程要挂到这个链表上,一个进程的死亡是件挺麻烦的事,进程池的数量有限,需要及时回收进程资源,但家族管理关系复杂,要去很多地方消除痕迹.尤其还有其他进程在看你笑话,等你死亡(wait/waitpid)了通知它们一声.
siblinglist兄弟链表,和你同一个父亲的进程都挂到了这个链表上.
subordinategrouplist 朋友圈链表,里面是因为兴趣爱好(进程组)而挂在一起的进程,它们可以不是一个父亲,不是一个祖父,但一定是同一个老祖宗(用户态和内核态根进程).
threadsiblinglist线程链表,上面挂的是进程id都是这个进程的线程(任务),进程和线程的关系是1:n的关系,一个线程只能属于一个进程.这里要注意任务在其生命周期中是不能改所属进程的.
threadpriqueuelist线程的调度队列数组,一共32个,任务和进程一样有32个优先级,调度算法的过程是先找到优先级最高的进程,在从该进程的任务队列里去最高的优先级任务运行.
waitlist 是等待子进程消亡的任务链表,注意上面挂的是任务.任务是通过系统调用
pid_t wait(int *status); pid_t waitpid(pid_t pid, int *status, int options);将任务挂到waitlist上.鸿蒙waitpid系统调用为syswait,具体看进程回收篇. 双向链表是内核最重要的结构体,精读内核的路上它会反复的映入你的眼帘,理解它是理解内核运作的关键所在!


天玑9200与V2强强联合,vivo X90系列亮相 旗舰之上更胜旗舰
PAL可定制机器人可能成为一线工作者
NC500/500SM系列BITE模块
印度正在考虑出台中国式的比特币和加密货币交易所禁令
东风本田艾力绅C-NCAP安全碰撞测试结果分析
深度解析鸿蒙内核最重要的结构体
数字化仪概述用途
如何使用Microchip模块实现LoRaWAN物联网应用
基于以太网控制器NC28J60和HR901170A实现以太网通信
互联网即将消失 智能网关助力物联网崛起
OceanBase以7.07亿tpmC的成绩打破去年TPC-C基准测试纪录?
搭载 OLED大屏 iPhone X Plus面板首次曝光
安森美半导体24亿美元成功收购飞兆半导体
推动汽车数字化转型的趋势是什么 本文告诉你答案
华为Mate9 Pro和Mate9对比评测,谁更出色?
DshanMCU-R128s2硬件设计参考
场效应管基础知识
宏微科技:预计明年营收增速不低于50%
如何正确选择/使用万用表
索尼和雅马哈联手打造自动驾驶汽车