0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

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

鸿蒙系统HarmonyOS 来源:my.oschina 作者:鸿蒙内核源码分析 2021-04-25 11:58 次阅读

谁是鸿蒙内核最重要的结构体?

答案一定是: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; /**< Current node's pointer to the next node *///后继节点(右手)
} LOS_DL_LIST;

结构体够简单了吧,只有前后两个指向自己的指针,但恰恰是因为太简单,所以才太不简单. 就像氢原子一样,宇宙中无处不在,占比最高,原因是因为它最简单,最稳定!

内核的各自模块都能看到双向链表的身影,下图是各处初始化双向链表的操作,因为太多了,只截取了部分:

pIYBAGCE6DKAAf-bAARvRfH2FV4405.png

很多人问图怎么来的,source insight 4.0是阅读大型C/C++工程的必备工具,要用4.0否则中文有乱码.

可以豪不夸张的说理解LOS_DL_LIST及相关函数是读懂鸿蒙内核的关键。前后指针(注者后续将比喻成一对左右触手)灵活的指挥着系统精准的运行,越是深入分析内核源码,越能感受到内核开发者对LOS_DL_LIST非凡的驾驭能力,笔者仿佛看到了无数双手前后相连,拉起了一个个双向循环链表,把指针的高效能运用到了极致,这也许就是编程的艺术吧!这么重要的结构体还是需详细讲解一下.

基本概念

双向链表是指含有往前和往后两个方向的链表,即每个结点中除存放下一个节点指针外,还增加一个指向其前一个节点的指针。其头指针head是唯一确定的。从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点,这种数据结构形式使得双向链表在查找时更加方便,特别是大量数据的遍历。由于双向链表具有对称性,能方便地完成各种插入、删除等操作,但需要注意前后方向的操作。

有好几个同学问数据在哪? 确实LOS_DL_LIST这个结构看起来怪怪的,它竟没有数据域!所以看到这个结构的人第一反应就是我们怎么访问数据?其实LOS_DL_LIST不是拿来单独用的,它是寄生在内容结构体上的,谁用它谁就是它的数据.看图就明白了.

pIYBAGCE6EaAToV1AADMtl72i_g471.png

功能接口

鸿蒙系统中的双向链表模块为用户提供下面几个接口。

pIYBAGCE6FOAL5ATAAEsiAQ3PvI596.png

请结合下面的代码和图去理解双向链表,不管花多少时间,一定要理解它的插入/删除动作,否则后续内容将无从谈起.

//将指定节点初始化为双向链表节点
LITE_OS_SEC_ALW_INLINE STATIC INLINE VOID LOS_ListInit(LOS_DL_LIST *list)
{
    list->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;
}

pIYBAGCE6GqAMe0lAAHgHk5dkIE576.png

强大的宏

除了内联函数,对双向遍历的初始化,定位,遍历 等等操作提供了更强大的宏支持.使内核以极其简洁高效的代码实现复杂逻辑的处理.

//定义一个节点并初始化为双向链表节点
#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 << cpuid)) {
#endif
                        newTask->taskStatus &= ~OS_TASK_STATUS_READY;
                        OsPriQueueDequeue(processCB->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,具体看进程回收篇.

双向链表是内核最重要的结构体,精读内核的路上它会反复的映入你的眼帘,理解它是理解内核运作的关键所在!

编辑:hfy

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 内核
    +关注

    关注

    3

    文章

    1359

    浏览量

    40179
  • 鸿蒙系统
    +关注

    关注

    183

    文章

    2633

    浏览量

    66119
收藏 人收藏

    评论

    相关推荐

    深度神经网络(DNN)架构解析与优化策略

    深度神经网络(Deep Neural Network, DNN)作为机器学习领域中的一种重要技术,以其强大的特征学习能力和非线性建模能力,在多个领域取得了显著成果。DNN的核心在于其多层结构,通过
    的头像 发表于 07-09 11:00 1306次阅读

    华为鸿蒙内核获中国信通院自主成熟度A级认证

    在科技创新的浪潮中,华为再次以其卓越的自主研发能力引领行业前行。近日,中国信息通信研究院(简称“中国信通院”)官方公众号宣布了一项重要成果:华为技术有限公司的鸿蒙内核成功通过了自主成熟度等级认证
    的头像 发表于 07-03 14:32 579次阅读

    欢创播报 华为宣布鸿蒙内核已超越Linux内核

    1 华为宣布鸿蒙内核已超越Linux内核   6月21日,在华为开发者大会上, HarmonyOS NEXT(鸿蒙NEXT)——真正独立于安卓和iOS的
    的头像 发表于 06-27 11:30 747次阅读

    PLC基本结构解析

    方式和便捷的编程方式,被广泛应用于各种工业控制系统中。本文将详细解析PLC的基本结构,包括其主要组成部分的功能和特点,以便读者对PLC有更深入的了解。
    的头像 发表于 06-25 14:30 758次阅读

    华为发布鸿蒙原生智能,OS深度融合AI,小艺升级为系统级智能

    Beta。鸿蒙原生智能是基于软硬芯云协同的硬件与基础设施架构,AI与OS深度融合的智能系统。 小艺智能:能思考,会规划,可执行 基于鸿蒙原生智能强大的AI底座,搭载盘古大模型,小艺升
    的头像 发表于 06-24 14:30 455次阅读
    华为发布<b class='flag-5'>鸿蒙</b>原生智能,OS<b class='flag-5'>深度</b>融合AI,小艺升级为系统级智能<b class='flag-5'>体</b>

    HDC2024华为发布鸿蒙原生智能:AI与OS深度融合,开启全新的AI时代

    6月21日,华为开发者大会2024(HDC.2024)召开。 HarmonyOS NEXT将AI与OS深度融合,构筑全新鸿蒙原生智能框架。大会现场,华为常务董事、终端BG董事长、智能汽车解决方案BU
    的头像 发表于 06-24 09:28 565次阅读
    HDC2024华为发布<b class='flag-5'>鸿蒙</b>原生智能:AI与OS<b class='flag-5'>深度</b>融合,开启全新的AI时代

    【JAVA UI】【HarmonyOS】【Demo】 鸿蒙如何进行 xml 解析

    鸿蒙鸿蒙如何进行数据解析 【问题描述】有时候我们从服务器获取是 xml 格式数据,我们需要将 xml 转化成 model 对象,该如何使用呢?下面举个例子说明一下,将分以下几步进行 1.准备条件
    的头像 发表于 02-19 15:59 480次阅读
    【JAVA UI】【HarmonyOS】【Demo】 <b class='flag-5'>鸿蒙</b>如何进行 xml <b class='flag-5'>解析</b>

    鸿蒙使用的是微内核

    我们常说,看一个系统是不是自研,就看它的内核,常见的内核分为:宏内核和微内核,当然还有两者结合体,他们到底有什么区别? 白话宏内核和微
    的头像 发表于 01-30 16:43 430次阅读
    <b class='flag-5'>鸿蒙</b>使用的是微<b class='flag-5'>内核</b>?

    鸿蒙OS和开源鸿蒙什么关系?

    开源鸿蒙(Open Harmony) 鸿蒙系统愿来的设计初衷,就是让所有设备都可以运行一个系统,但是每个设备的运算能力和功能都不同,所以内核的设计上,采用了微内核的设计,除了最基础的功
    的头像 发表于 01-30 15:44 1042次阅读
    <b class='flag-5'>鸿蒙</b>OS和开源<b class='flag-5'>鸿蒙</b>什么关系?

    结构与指针的关系

    在C语言中,结构(Struct)是一种用户自定义的数据类型,它允许您将不同类型的数据项组合在一起,以便形成一个更复杂的数据结构结构可以
    的头像 发表于 01-11 08:00 905次阅读
    <b class='flag-5'>结构</b><b class='flag-5'>体</b>与指针的关系

    golang结构如何定义?如何使用呢?

    结构是go语言最重要的数据结构之一,go和其它编程语言不一样,它没有类的概念,类比过来struct就相当于其它语言中的类,因此十分重要
    的头像 发表于 11-28 10:36 391次阅读

    golang结构实例代码

    结构是go语言最重要的数据结构之一,go和其它编程语言不一样,它没有类的概念,类比过来struct就相当于其它语言中的类,因此十分重要
    的头像 发表于 11-28 10:35 410次阅读

    最重要的参数:陀螺仪机械性能

    电子发烧友网站提供《最重要的参数:陀螺仪机械性能.pdf》资料免费下载
    发表于 11-22 11:53 0次下载
    <b class='flag-5'>最重要</b>的参数:陀螺仪机械性能

    安全存储功能中使用的重要结构

    安全存储功能中使用的重要结构 在整个安全存储功能的操作过程中,存在一些很重要结构,这些
    的头像 发表于 11-21 14:36 458次阅读
    安全存储功能中使用的<b class='flag-5'>重要</b><b class='flag-5'>结构</b><b class='flag-5'>体</b>

    Linux内核中使用的数据结构

    Linux内核代码中广泛使用了数据结构和算法,其中最常用的两个是链表和红黑树。 链表 Linux内核代码大量使用了链表这种数据结构。链表是在解决数组不能动态扩展这个缺陷而产生的一种数据
    的头像 发表于 11-09 14:24 439次阅读
    Linux<b class='flag-5'>内核</b>中使用的数据<b class='flag-5'>结构</b>