首页 > 其他 > 详细

双向链表初探

时间:2021-05-11 00:35:31      阅读:34      评论:0      收藏:0      [点我收藏+]

最近鸿蒙系统大火,趁着热度,学习了一下鸿蒙内核的kernel/los_list.h的源码,纯属个人爱好,学Linux总得看点内核汲取汲取营养,下面源码有兴趣尝试conainer_of的建议Linux系统下编译运行,windows把linux/kernel.h去掉即可,头文件linux/kernel.h和stddef.h不是必须的,只是两个分别存放了offsetof和container_of函数,与LOS_OFF_SET_OF(type, member) 和 LOS_DL_LIST_ENTRY(item, type, member)具有相同的功能,stdlib.h和stdio.h头文件是必须的,删除则编译不成功。


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

答案一定是: 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;

这个结构体很简单,只有两个指针域的双向链表,简单到甚至连数据域都没有。可是没有数据域的链表不是形同虚设吗?那你将大错特错,几乎内核里的所有模块都能看的到该双向链表的身影。可以豪不夸张的说理解LOS_DL_LIST及相关函数是读懂鸿蒙内核的关键。


基本概念

技术分享图片

 

双向链表是指含有往前和往后两个方向的链表,即每个结点中除存放下一个节点指针外,还增加一个指向其前一个节点的指针。其头指针head是唯一确定的。 如图所示,该双向链表是镶嵌在其他结构体中使用的。

 

使用方法:内嵌与其他结构体使用

这是一种很巧妙的链表实现,在内核中用这种结构进行进程管理等,下面给出一个简单的实例,来说明该如何使用,相关API已在程序中给出(手打确实有点累),其中LOS_OFF_SET_OF(type, member)可以用stddef.h中的offsetof(type, member)来进行代替,LOS_DL_LIST_ENTRY(item, type, member)可以用linux/kernel.h中的container_of(item, type, member)来进行代替。程序很简单,新建4个Thread,加入队列,再遍历输出队列中的Thread,看看这种内嵌的双向链表是否真的起到作用(比较地址是否相同)

老天作证,原版绝对严格对齐

技术分享图片

#include <stdio.h>

/**
 *@defgroup los_list Doubly linked list
 *@ingroup kernel
 */
#ifndef _LOS_LIST_H
#define _LOS_LIST_H

#include <stdlib.h>
#include <stddef.h>
#include <linux/kernel.h>

#ifdef __cplusplus
#if __cplusplus
extern "C" {
#endif /* __cplusplus */
#endif /* __cplusplus */


/**
 *@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;




#ifndef BOOL
#define BOOL int8_t
#endif


#ifndef CHAR
#define CHAR char
#endif

#ifndef NULL
#define NULL ((void *)0)
#endif



/*
#define container_of(ptr, type, member) ({                              const typeof( ((type *)0)->member ) *__mptr = (ptr);            (type *)( (char *)__mptr - offsetof(type,member) );})
*/



/**
 *宏定义,初始化双向链表
 */
#define LOS_DL_LIST_HEAD(list) LOS_DL_LIST list = {&(list), &(list)}



/**
 *计算type结构体成员member相对于结构体的地址偏移量
 */
#define LOS_OFF_SET_OF(type, member) ((unsigned long)&((type *)0)->member)



/**
 *获取包含LOS_DL_LIST item的结构体的地址
 */
#define LOS_DL_LIST_ENTRY(item, type, member) \
    ((type *)(void *)((char *)(item) - LOS_OFF_SET_OF(type, member)))    


/**
 *遍历双向链表LOS_DL_LIST
 */
#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))


#define LOS_List_PeekTailType(list, type, member) ({     \ type *__t; if ((list)->pstPrev == list) { __t = NULL; } else { __t = LOS_DL_LIST_ENTRY((list)->pstPrev, type, member); } __t; })
#define LOS_List_PeekHeadType(list, type, member) ({ \ type *__t; if ((list)->pstNext == list) { __t = NULL; } else { __t = LOS_DL_LIST_ENTRY((list)->pstNext, type, member); } __t; })
#define LOS_List_RemoveHeadType(list, type, member) ({ \ type *__t; if ((list)->pstNext == list) { __t = NULL; } else { __t = LOS_DL_LIST_ENTRY((list)->pstNext, type, member); LOS_ListDelete((list)->pstNext); } __t; })
#define LOS_ListNextType(list, item, type, member) ({ \ type *__t; if ((item)->pstNext == list) { __t = NULL; } else { __t = LOS_DL_LIST_ENTRY((item)->pstNext, type, member); } __t; }) /** *@ingroup los_list * *@par Description: * This API is used to initialize a doubly linked list. */ static inline void LOS_ListInit(LOS_DL_LIST *list) { list->pstNext = list; list->pstPrev = list; } /** *@ingoup los_list * *@par Decription: * This API is uesd to remove a node in a doubly linked list. */ 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; }
/** *@ingroup los_list * *@par Description: * This API is uesd to insert a new node in a doubly linked list. */ 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; } /** *@ingroup los_list * *@par Description: * This API is used to insert a new node to the tail of a doubly linked list. */ static inline void LOS_ListTailInsert(LOS_DL_LIST *list, LOS_DL_LIST *node) { LOS_ListAdd(list->pstPrev, node); } /** *@ingroup los_list * *@par Description: * This API is used to insert a new node to the head of a doubly linked list. */ static inline void LOS_listHeadInsert(LOS_DL_LIST *list, LOS_DL_LIST *node) { LOS_ListAdd(list, node); } /** *@ingroup los_list * *@par Description: * This API is used to return whether a doubly linked list is empty. */ static inline BOOL LOS_ListEmpty(LOS_DL_LIST *list) { return (BOOL)(list->pstNext == list); }
#ifdef __cplusplus
#if __cplusplus extern "C" { #endif /* __cplusplus */ #endif /* __cplusplus */ #endif /* _LOS_LIST_H */
/** *@ingroup jckeep_thread_demo * Structure of a thread */ typedef struct Thread { char *thread_name; int thread_ID; LOS_DL_LIST thread_line; } Thread; int main() { LOS_DL_LIST_HEAD(list1); Thread a = {"chrome", 1}; Thread b = {"Edge", 2}; Thread c = {"bash", 3}; Thread d = {"visual studio code", 4}; LOS_ListTailInsert(&list1, &a.thread_line); LOS_ListTailInsert(&list1, &b.thread_line); LOS_ListTailInsert(&list1, &c.thread_line); LOS_ListTailInsert(&list1, &d.thread_line); printf("Thread_1‘s address is %p\nThread_2‘s address is %p\n" "Thread_3‘s address is %p\nThread_4‘s address is %p\n", &a, &b, &c, &d); Thread *A = LOS_List_PeekHeadType(&list1, Thread, thread_line); Thread *B = LOS_List_PeekTailType(&list1, Thread, thread_line); //Thread *A = LOS_DL_LIST_ENTRY(list1.pstNext ,Thread, thread_line); //Thread *B = LOS_DL_LIST_ENTRY(list1.pstNext->pstNext, Thread, thread_line); Thread *item; LOS_DL_LIST_FOR_EACH_ENTRY(item, &list1, Thread, thread_line) { printf("Thread address: %p\t\033[4mThread_name\033[0m: \033[1;31m%30s\033[0m\t" "Thread_ID: \033[1;32m%d\033[0m\tThread_line: %p\n", item, item->thread_name ,item->thread_ID, &item->thread_line); } return 0; }

 

 

运行结果:

技术分享图片

可以看到Thread 1, 2, 3, 4对应前后的地址均相同。

 

双向链表初探

原文:https://www.cnblogs.com/JCKeep/p/14753394.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!