Bidirectional circular linked list: the “post station” of data in Hongmeng light kernel

Time:2021-8-7

Summary:Double linked list is one of the most important data structures of Hongmeng light kernel, which is widely used in various modules.

This article is shared from Huawei cloud community《Hongmeng light kernel m core source code analysis Series II data structure – bidirectional circular linked list》, original author: zhushy.

When learning the source code of openharmony Hongmeng light kernel, we often encounter the use of some data structures. If you don’t master their usage, it will be very difficult and difficult to read the source code. This article will introduce readers to the important data structure in the source code, the double linked list. During the explanation, it will be combined with data structure related drawings to cultivate readers’ plane imagination ability of data structures and help them better learn and understand the usage of these data structures.

The source code involved in this article, taking the openharmony liteos-m kernel as an example, can be found on the open source sitehttps://gitee.com/openharmony…obtain.

1 bidirectional circular linked list

Bidirectional linked list Los_ DL_ The source code of list is in utils \ LOS_ List. H bidirectional linked list header file, including Los_ DL_ List structure definition, inline inline function Los_ Listxxx, and related function macro definitions Los_ DL_ LIST_ XXXX。 The two-way linked list header file can access utils / Los from the web page_ List. H, you can also check out local reading.

1.1 two way linked list structure

Bidirectional linked list node structure Los_ DL_ List is defined as follows. Its structure is very simple, general and abstract. It only contains two nodes: precursor and successor, which is responsible for the role of two-way linked list. The two-way linked list does not contain any business data information and is generally not used alone. Generally, two-way linked list nodes and business data information are used as structure members to form a business structure. The use example will be described later.

typedef struct LOS_DL_LIST {
    struct LOS_ DL_ LIST *pstPrev; /**  Pointer to the precursor node of the current linked list node*/
    struct LOS_ DL_ LIST *pstNext; /**  Pointer to the successor node of the current linked list node*/
} LOS_DL_LIST;

Starting from any node in the two-way linked list, you can easily access its predecessor and successor nodes. This ring data structure makes the two-way linked list very convenient in search, insertion, deletion and other operations. When a bi-directional linked list is used in a business scenario, a Los can be defined_ DL_ The global variable of list type is used as the head node of the bidirectional circular linked list, and the linked list member nodes of the business structure are mounted on the head node in turn. In addition, the two-way linked list nodes of some business structures are used as head nodes to mount the linked list member nodes of other business structures in turn. The next node can be traversed successively from the head node. The precursor node of the head node is the tail node.

Next, learn how to use the bidirectional linked list structure through the definition of the mutually exclusive lock structure losmuxcb in the Hongmeng light kernel code:

typedef struct {
    UINT8 muxStat;       /**<  Mutex status*/
    UINT16 muxCount;     /**<  The number of times the mutex is currently held*/
    UINT32 muxID;        /**<  Mutex number ID*/
    LOS_ DL_ LIST muxList; /**<  Bidirectional linked list of mutually exclusive locks*/
    LosTaskCB *owner;    /**<  Task TCB currently holding lock*/
    UINT16 priority;     /**<  Priority of tasks holding mutexes*/
} LosMuxCB;

The mutex structure includes the bidirectional linked list LOS_ DL_ List muxlist member variables and other member variables containing mutex business information. Here, each mutex is linked through a two-way linked list and attached to the header node LOS_ DL_ LIST g_ unusedMuxList; Business data is carried through other business member variables. The linked list and other business member relationships are shown in the following figure:
Bidirectional circular linked list: the

2 initialize the two-way linked list

2.1 LOS_ListInit(LOS_DL_LIST *list)

LOS_ DL_ The two members of list, pstprev and pstnext, are LOS_ DL_ Pointer to the list structure type. You need to apply for sizeof (LOS) for a two-way linked list node_ DL_ List). After applying for memory for the linked list node, you can call to initialize LOS_ ListInit(LOS_ DL_ List * list) method to link this node into a circular two-way linked list. When initializing a linked list, there is only one linked list node, and the predecessor and successor nodes of this node are themselves. The linked list node is initialized as a linked list, as shown in the figure:
Bidirectional circular linked list: the

The source code is as follows:

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

2.2 LOS_DL_LIST_HEAD(LOS_DL_LIST list)

Except Los_ Listinit() also provides a functional macro Los with the same function_ DL_ LIST_ Head directly defines a two-way linked list node to initialize the node as a two-way linked list. Different from Los_ Listinit() does not need to dynamically request memory space before calling functional macros.

#define LOS_DL_LIST_HEAD(list) LOS_DL_LIST list = { &(list), &(list) }

3. Judge the empty linked list

3.1 LOS_ListEmpty(LOS_DL_LIST *list)

This inline function is used to determine whether the linked list is empty. If the predecessor / successor nodes of the two-way linked list are themselves, there is only one chain node, and there is no linked list node attached to the business structure, the linked list is called an empty linked list.

The source code is as follows:

LITE_OS_SEC_ALW_INLINE STATIC_INLINE BOOL LOS_ListEmpty(LOS_DL_LIST *node)
{
    return (BOOL)(node->pstNext == node);
}

4 insert bidirectional linked list node

Two way linked list provides three methods to insert linked list nodes. LOS is inserted after the specified linked list node_ Listadd, tail insert Los_ Listtailinsert, header insert Los_ ListHeadInsert。 For the node inserted in the head, the first traversal starts from the head, and the last traversal starts from the node inserted in the tail.

4.1 LOS_ListAdd(LOS_DL_LIST list, LOS_DL_LIST node)
The inline function to the linked list nodeInsert a linked list node into the bidirectional linked list where the list is locatedNode, the insertion position is in the linked list nodeBehind the list. As shown in the figure, during the insertion process, theThe successor node of node is set to list – > pstnext,The precursor node of node isList, and the precursor node of list – > pstnext fromList is modified tonode,The successor node of list is changed from list – > pstnext tonode。

Illustration:
Bidirectional circular linked list: the

The source code is as follows:

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;
}

4.2 LOS_ListTailInsert(LOS_DL_LIST list, LOS_DL_LIST node)

The inline function to the linked list nodeInsert a linked list node into the bidirectional linked list where the list is locatedNode, the insertion position is in front of the linked list node * list and behind the list – > pstprev node.

The source code is as follows:

LITE_OS_SEC_ALW_INLINE STATIC INLINE VOID LOS_ListTailInsert(LOS_DL_LIST *list, LOS_DL_LIST *node)
{
    LOS_ListAdd(list->pstPrev, node);
}

4.3 LOS_ListHeadInsert(LOS_DL_LIST list, LOS_DL_LIST node)

The inline function and Los_ Listadd() implements the same function to the linked list nodeInsert a linked list node into the bidirectional linked list where the list is locatedNode, the insertion position is behind the linked list node * list.

The source code is as follows:

LITE_OS_SEC_ALW_INLINE STATIC INLINE VOID LOS_ListHeadInsert(LOS_DL_LIST *list, LOS_DL_LIST *node)
{
    LOS_ListAdd(list, node);
}

5 delete bidirectional linked list node

Bi directional linked list provides two methods to delete linked list nodes, and deletes the specified node LOS_ Listdelete(), delete and initialize as a new linked list Los_ ListDelInit()。

5.1 LOS_ListDelete(LOS_DL_LIST *node)

The inline function will link the list nodesNode is deleted from the bidirectional linked list. After a node is deleted, you may need to actively release the memory occupied by the node. As shown in the figure, in the process of deleting a node, theThe predecessor of the subsequent node of node is changed toThe precursor node of node,The successor of the predecessor node of node is changed toNode, andThe precursor and successor nodes of the node node are set to null, so that the * node node is separated from the bidirectional linked list.

Illustration:
Bidirectional circular linked list: the

The source code is as follows:

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;
}

5.2 LOS_ListDelInit(LOS_DL_LIST *list)

The inline function deletes the linked list node * list from the bidirectional linked list, and reinitializes the deleted node into a new bidirectional linked list.

And Los_ Similar to listdelete(), this function will alsoThe predecessor of the successor node of list is changed toList precursor,The successor of the precursor node of list is changed toList, but the difference is that because it needs to be reinitialized as a new two-way linked list, this function will notThe list’s predecessor and successor nodes are set to null, but the deleted node is reinitialized toList is a new two-way linked list of the head node.

The source code is as follows:

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

6 get the bidirectional linked list node

The bidirectional linked list also provides the operation of obtaining the linked list node and the structure address containing the linked list.

6.1 LOS_DL_LIST_LAST(object)
Gets the precursor node of the specified linked list node.

The source code is as follows:

#define LOS_DL_LIST_LAST(object) ((object)->pstPrev)

6.2 LOS_DL_LIST_FIRST(object)

Gets the successor node of the specified linked list node.

The source code is as follows:

#define LOS_DL_LIST_FIRST(object) ((object)->pstNext)

7 traverse the bidirectional circular linked list node

Two way circular linked list provides two methods to traverse the two-way linked list, Los_ DL_ LIST_ FOR_ Each and Los_ DL_ LIST_ FOR_ EACH_ SAFE。

7.1 LOS_DL_LIST_FOR_EACH(item, list)

This macro defines LOS_ DL_ LIST_ FOR_ Each traverses the two-way linked list and saves the linked list node obtained in each cycle in the first input parameter, and the second input parameter is the starting node of the two-way linked list to be traversed. This macro is a for loop condition. In each loop, get the next linked list node and save it to the input parameter item. The business code is written in the code block {} after the macro.

The source code is as follows:

#define LOS_DL_LIST_FOR_EACH(item, list) \
    for ((item) = (list)->pstNext; (item) != (list); (item) = (item)->pstNext)
We use examples to demonstrate how to use LOS_ DL_ LIST_ FOR_ EACH。 In kernel \ SRC \ Los_ In the task. C file, uint32

The fragment of ospriqueuesize (uint32 priority) function is as follows:

STATIC UINT32 OsPriqueueSize(UINT32 priority)
{
    UINT32 itemCnt = 0;
    LOS_DL_LIST *curPQNode = (LOS_DL_LIST *)NULL;

⑴  LOS_DL_LIST_FOR_EACH(curPQNode, &g_losPriorityQueueList[priority]) {
        ++itemCnt;
    }

    return itemCnt;
}

Where (1) code, G_ Lospriorityqueuelist [priority] is a two-way linked list to be iterated, and curpqnode points to the linked list node in the traversal process.

7.2 LOS_DL_LIST_FOR_EACH_SAFE(item, next, list)

This macro defines LOS_ DL_ LIST_ FOR_ EACH_ Safe and Los_ DL_ LIST_ FOR_ The only difference between each is that there is an additional input parameter next, which represents the next node of the traversed two-way linked list node. This macro is used for safe deletion. If the traversed item is deleted, it will not affect the continued traversal.

The source code is as follows:

#define LOS_DL_LIST_FOR_EACH_SAFE(item, next, list) \
    for ((item) = (list)->pstNext, (next) = (item)->pstNext; (item) != (list); \
            (item) = (next), (next) = (item)->pstNext)

8 get the structure of the linked list node

8.1 LOS_OFF_SET_OF(type, member)

Obtain the memory address offset of the member member variable relative to the structure type according to the structure type name type and the member variable name member. In the application scenario of linked list, the business structure contains a two-way linked list as a member. When you know the memory address of the two-way linked list member variable and the offset relative to the business structure, you can further obtain the memory address of the business structure. See Los below for details_ DL_ LIST_ Macro implementation of entry.

The source code is as follows:

#define LOS_OFF_SET_OF(type, member) ((UINTPTR)&((type *)0)->member)

8.2 LOS_DL_LIST_ENTRY(item, type, member)

The three parameters in the function macro are: business structure type name type, bidirectional linked list member variable name member as a structure member, and bidirectional linked list node pointer item as a structure member. By calling the macro function Los_ DL_ LIST_ Entry is to obtain the memory address of the business structure where the two-way linked list node is located.

The source code is as follows:

Based on the memory address of the bidirectional linked list node and the address offset of the bidirectional linked list member variable in the structure, the memory address of the structure can be calculated.

#define LOS_DL_LIST_ENTRY(item, type, member) \
    ((type *)(VOID *)((CHAR *)(item) - LOS_OFF_SET_OF(type, member)))

9 traverse the structure containing the two-way linked list

Bidirectional linked list provides three macro definitions to traverse the structure containing bidirectional linked list members, Los_ DL_ LIST_ FOR_ EACH_ ENTRY、LOS_ DL_ LIST_ FOR_ EACH_ ENTRY_ Safe and Los_ DL_ LIST_ FOR_ EACH_ ENTRY_ HOOK。

9.1 LOS_DL_LIST_FOR_EACH_ENTRY(item, list, type, member)

This macro defines LOS_ DL_ LIST_ FOR_ EACH_ By traversing the two-way linked list, entry obtains the structure variables containing the two-way linked list members in each cycle and saves them in the first input parameter. The second input parameter is the starting node of the bidirectional linked list to be traversed, the third input parameter is the type name of the structure to be obtained, and the fourth input parameter is the name of the bidirectional linked list member variable in the structure. This macro is a for loop condition, and the business code is written in the code block {} behind the macro.

The source code is as follows:

Initialization statement of for loop item = Los_ DL_ LIST_ Entry ((list) – > pstnext, type, member) means to obtain the structure containing the first valid node of the bidirectional linked list and save it to the pointer variable item. Conditional test statement & (item) – > member! =( List) means that when the two-way linked list traverses a circle to its own node, the loop stops. Circular UPDATE statement item = Los_ DL_ LIST_ In entry ((item) – > member.pstnext, type, member)), use (item) – > member.pstnext to traverse to the next linked list node, and then obtain the pointer variable item of the corresponding next structure according to this node until the traversal is completed.

#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))

9.2 LOS_DL_LIST_FOR_EACH_ENTRY_SAFE(item, next, list, type, member)

The macro defines and Los_ DL_ LIST_ FOR_ EACH_ The only difference of entry is that there is an additional input parameter next, which represents the next structure of the traversed structure. This macro is used for safe deletion. If the traversed item is deleted, it will not affect the continued traversal.

The source code is as follows:

#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))

9.3 LOS_DL_LIST_FOR_EACH_ENTRY_HOOK(item, list, type, member, hook)

The macro defines and Los_ DL_ LIST_ FOR_ EACH_ The difference of entry is that there is an additional input parameter hook, which represents the hook function. In each traversal cycle, the hook function will be called to customize the user task.

The source code is as follows:

#define LOS_DL_LIST_FOR_EACH_ENTRY_HOOK(item, list, type, member, hook)  \
    for (item = LOS_DL_LIST_ENTRY((list)->pstNext, type, member), hook;  \
         &(item)->member != (list);                                      \
         item = LOS_DL_LIST_ENTRY((item)->member.pstNext, type, member), hook)

Click focus to learn about Huawei cloud’s new technologies for the first time~