鸿蒙内核源码分析(双向循环链表篇)|开篇致敬鸿蒙内核开发者

提示:本文基于开源鸿蒙内核分析,官方源码【kernel_liteos_a】官方文档【docs】参考文档【Huawei LiteOS
本文作者:鸿蒙内核发烧友,将持续研究鸿蒙内核,更新博文,敬请关注。内容仅代表个人观点,错误之处,欢迎大家指正完善。本系列全部文章进入 查看 鸿蒙系统源码分析(总目录)


本文详细讲述双向循环链表,详见源码: los_list.h 

目录

为何鸿蒙内核源码分析系列开篇就说 LOS_DL_LIST?

基本概念

功能接口

具体的使用场景

任务队列涉及的相关代码

内联函数 inline

本系列全部文章进入鸿蒙系统源码分析(总目录)查看




为何鸿蒙内核源码分析系列开篇就说 LOS_DL_LIST

因为它在鸿蒙 LOS 内核中无处不在,可以说在整个内核占了极大的比重,基本通过它把所有的结构体像胶水一样粘在一起,豪不夸张的说理解LOS_DL_LIST及相关函数 是读懂鸿蒙内核的关键。前后指针就像人的两只左右手一样灵活的指挥着系统精准的运行,越是深入分析内核源码,越是能体会在内核开发者对LOS_DL_LIST的非凡的驾驭能力,笔者仿佛看到了无数双手前后相连,拉起了无数个双向循环链表,把指针妙处运用到了极致,这也许就是编程的艺术吧!

致敬鸿蒙内核开发者,鸿蒙内核源码可以作为大学操作系统,数据结构两门课的教学项目。

/**
 * @ingroup los_list
 * Structure of a node in a doubly linked 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;

LITE_OS_SEC_ALW_INLINE STATIC INLINE VOID LOS_ListInit(LOS_DL_LIST *list)
{
    list->pstNext = list;
    list->pstPrev = list;
}

真的是无处不在吗?答:是真的,看看使用它的源码吧,无处不在。

基本概念

双向链表是指含有往前和往后两个方向的链表,即每个结点中除存放下一个节点指针外,还增加一个指向其前一个节点的指针。其头指针head是唯一确定的。

二重リンクリスト内の任意のノードから始めて、その先行ノードと後続ノードに簡単にアクセスできます。この形式のデータ構造により、特に大量のデータトラバーサルの場合に、二重リンクリストを検索しやすくなります。二重リンクリストの対称性により、挿入や削除などのさまざまな操作を手軽に完了できますが、前後方向の操作には注意が必要です。

関数インターフェース

Huawei LiteOSシステムの二重リンクリストモジュールは、ユーザーに次のインターフェースを提供します。

関数分類

インターフェース名

解説

リンクリストを初期化する

LOS_ListInit

リンクリストを初期化します。

ノードを追加

LOS_ListAdd

リンクリストに新しいノードを追加します。

リンクリストの最後にノードを挿入する

LOS_ListTailInsert

二重リンクリストの最後にノードを挿入します。

ノードを削除

LOS_ListDelete

リンクリストから指定されたノードを削除します。

二重リンクリストが空かどうかを判別

LOS_ListEmpty

リンクリストが空かどうかを確認します。

ノードを削除し、リンクリストを初期化します

LOS_ListDelInit

リンクリストから指定されたノードを削除し、このノードを使用してリンクリストを初期化します。

リンクリストをリンクリストに挿入する LOS_ListAddList 2つの循環リンクリストから大きな循環リンクリスト
末尾からノードを挿入 LOS_ListTailInsert (LOS_DL_LIST *リスト、LOS_DL_LIST *ノード)
頭からノードを挿入 LOS_ListHeadInsert (LOS_DL_LIST *リスト、LOS_DL_LIST *ノード)
リンクされたリストを最後から挿入する LOS_ListTailInsertList (LOS_DL_LIST * oldList、LOS_DL_LIST * newList)
先頭からリンクリストを挿入 LOS_ListTailInsertList (LOS_DL_LIST * oldList、LOS_DL_LIST * newList)

Hongmengは、双方向の循環リンクリストを使用して、構造データ構造間の関連付けを実現し、単一ノードの先頭と末尾の挿入をサポートします。さらに、リンクリストが別のリンクリストの挿入をサポートし、2つの循環リンクリストを大きな円形リンクリストに結合することは非常に微妙です。独創的でシンプル。詳細については、コードを参照してください

 //双向链表初始化
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 BOOL LOS_ListEmpty(LOS_DL_LIST *list)
{
    return (BOOL)(list->pstNext == list);
}

//从链表中删除节点
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;
}

//指针互换,具体向双向循环链表中插入节点
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_ListAddList(LOS_DL_LIST *oldList, LOS_DL_LIST *newList)
{
    // 先用临时指针记录头尾位置
    LOS_DL_LIST *oldListHead = oldList->pstNext;
    LOS_DL_LIST *oldListTail = oldList;
    LOS_DL_LIST *newListHead = newList;
    LOS_DL_LIST *newListTail = newList->pstPrev;
    // 前后指针完成切换
    oldListTail->pstNext = newListHead;
    newListHead->pstPrev = oldListTail;
    oldListHead->pstPrev = newListTail;
    newListTail->pstNext = oldListHead;
}
// 这里与其说插入不如说合并,同样支持从头或尾部合并
LITE_OS_SEC_ALW_INLINE STATIC INLINE VOID LOS_ListTailInsertList(LOS_DL_LIST *oldList, LOS_DL_LIST *newList)
{
    LOS_ListAddList(oldList->pstPrev, newList);
}
LITE_OS_SEC_ALW_INLINE STATIC INLINE VOID LOS_ListHeadInsertList(LOS_DL_LIST *oldList, LOS_DL_LIST *newList)
{
    LOS_ListAddList(oldList, newList);
}

Hongmengカーネルのソースコードを読み取るときは、LOS_DL_LISTを使用してコード間の関係をリアルタイムで理解し、ランタイムシーンを想像することで、カーネルコードの美しさを体験できます。

特定の使用シナリオ

その使用シナリオの1つを見て、デザイナーのすばらしい意図を体験し、コードをアップロードします。

typedef struct ProcessCB {
    CHAR                 processName[OS_PCB_NAME_LEN]; /**< Process name */
    UINT32               processID;                    /**< process ID = leader thread ID */
    UINT16               processStatus;                /**< [15:4] process Status; [3:0] The number of threads currently
                                                            running in the process */
    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 */
    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 */
    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

    LOS_DL_LIST          waitList;     /**< The process holds the waitLits to support 
} LosProcessCB;

これはLosProcessCB(プロセス制御ブロック)です。これは、構造が非常に複雑であり、他の定義が省略されているため、LOS_DL_LISTは関連しています。カーネルソースコードは自分で読み取ることができます。LosProcessCBには、7つの双方向循環リンクリストが含まれ、プロセスグループのキューは配列です、32個の準備完了キューの二重循環リンクリストが含まれています。これらのリンクされたリストには、ライフサイクル内のプロセスの実行中のプロセスロジック、プロセスとスレッド間の関係ロジック、スレッドの実行中のプロセスロジックなどが含まれます。はい、このような複雑なデータ構造は、誕生から死に至るプロセスを記述するために必要ですのプロセス。

タスクキューに関連するコード

これらは、タスクキューがデキューしてキューに入る操作です。その背後には、LOS_DL_LISTの追加と削除のプロセスがあります。落ち着いてこれらのマクロを理解してください

#define OS_PROCESS_PRI_QUEUE_SIZE(processCB) OsPriQueueProcessSize(g_priQueueList, (processCB)->priority)

#define OS_TASK_PRI_QUEUE_ENQUEUE(processCB, taskCB) \
    OsPriQueueEnqueue((processCB)->threadPriQueueList, &((processCB)->threadScheduleMap), \
                      &((taskCB)->pendList), (taskCB)->priority)
#define OS_TASK_PRI_QUEUE_ENQUEUE_HEAD(processCB, taskCB) \
    OsPriQueueEnqueueHead((processCB)->threadPriQueueList, &((processCB)->threadScheduleMap), \
                      &((taskCB)->pendList), (taskCB)->priority)

#define OS_TASK_PRI_QUEUE_DEQUEUE(processCB, taskCB) \
    OsPriQueueDequeue((processCB)->threadPriQueueList, &((processCB)->threadScheduleMap), &((taskCB)->pendList))


#define OS_TASK_SCHED_QUEUE_ENQUEUE(taskCB, status) OsTaskSchedQueueEnqueue(taskCB, status)
#define OS_TASK_SCHED_QUEUE_DEQUEUE(taskCB, status) OsTaskSchedQueueDequeue(taskCB, status)

#define OS_PROCESS_PRI_QUEUE_ENQUEUE(processCB) \
    OsPriQueueEnqueue(g_priQueueList, &g_priQueueBitmap, &((processCB)->pendList), (processCB)->priority)
#define OS_PROCESS_PRI_QUEUE_ENQUEUE_HEAD(processCB) \
    OsPriQueueEnqueueHead(g_priQueueList, &g_priQueueBitmap, &((processCB)->pendList), (processCB)->priority)
#define OS_PROCESS_PRI_QUEUE_DEQUEUE(processCB) OsPriQueueProcessDequeue(&((processCB)->pendList))

#define OS_TASK_PRI_QUEUE_SIZE(processCB, taskCB) OsPriQueueSize((processCB)->threadPriQueueList, (taskCB)->priority)
#define OS_TASK_GET_NEW(processCB) LOS_DL_LIST_ENTRY(OsPriQueueTop((processCB)->threadPriQueueList,     \
                                                                    &((processCB)->threadScheduleMap)), \
                                                     LosTaskCB, pendList)

インライン関数インライン

Hongmengカーネルは多くのインライン関数を使用していますが、インライン関数の利点は何ですか?わからない場合はご自身で確認してください。ここでは基本的な知識は普及していません。ソースlos_list.hwood .cファイルのみ最も頻繁に呼び出されるこれらのインライン関数は、通常の関数のようにスタックからポップアウトするために必要な時間とスペースを排除し、非常に効率的です。

/* Define OS code data sections */
/* The indicator function is inline */
#ifndef LITE_OS_SEC_ALW_INLINE
#define LITE_OS_SEC_ALW_INLINE  /* __attribute__((always_inline)) */
#endif

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_ListTailInsert(LOS_DL_LIST *list, LOS_DL_LIST *node)
{
    LOS_ListAdd(list->pstPrev, node);
}

このシリーズのすべての記事は、Hongmengシステムのソースコード分析(総合カタログ)ビューに入ります

 

 

 

おすすめ

転載: blog.csdn.net/kuangyufei/article/details/108585659