RT-Thread源码分析1:双链表

文章目录

    • 前情提要
    • RT-Thread双链表构造
    • 双链表节点数据结构
    • 双链表处理函数
      • 1. 初始化链表对象:RT_LIST_OBJECT_INIT(object)
      • 2. 链表初始化:rt_list_init(rt_list_t *l)
      • 3. rt_list_insert_after(*l, *n)
      • 4. rt_list_insert_before(*l, *n)
      • 5. 移除一个链表节点:rt_list_remove(rt_list_t *n)
      • 6. 判断链表是否为空:rt_list_isempty(const rt_list_t *l)
      • 7. 获取链表长度:rt_list_len(const rt_list_t *l)
      • 8. 获取当前所在结构体的地址:rt_list_entry(node, type, member)
      • 以下函数RT-Thread Nano内核未调用
        • 9. 遍历链表:rt_list_for_each(pos, head)
        • 10. 安全地遍历链表:rt_list_for_each_safe(pos, n, head)
        • 11. 循环遍历head链表中每一个pos中的member成员:rt_list_for_each_entry(pos, head, member)
        • 12. 安全地循环遍历head链表中每一个pos中的member成员:rt_list_for_each_entry_safe(pos, n, head, member)
        • 13. 获取链表中的第一个元素:rt_list_first_entry(ptr, type, member)
    • 关于作者

前情提要

源码文件:RT-Thread Nano V3.1.3\include\rtservice.h (源文件修改日期:2017-11-15)
RT-Thread官网:https://www.rt-thread.org/

RT-Thread内核采用面向对象的设计思想进行设计,系统级的基础设施都是一种内核对象,例如线程,信号量,互斥量,定时器等。

对象容器中包含了每类内核对象的信息,包括对象类型,大小等。

对象容器给每类内核对象分配了一个链表,所有的内核对象都被链接到该链表上。

如图 RT-Thread 的内核对象容器及链表如下图所示:
RT-Thread源码分析1:双链表_第1张图片
由此可见,学习内核源码前,需要了解链表的构造及实现函数。

RT-Thread Nano V3.1.3\include\rtservice.h中包含单链表和双链表,RT-Thread主要使用双链表结构,作为RT-Thread源码分析第1篇文章,首先学习双链表。

关于单链表内容,请参考作者另一篇文章:RT-Thread源码分析2:单链表


RT-Thread双链表构造

RT-Thread双链表实际上是循环双向链表,每个节点有两个指针成员nextprev

next指向下一个节点,prev指向上一个节点,链表头不是链表成员,作为链表处理函数的入口;

链表构造示意图如下:
RT-Thread源码分析1:双链表_第2张图片


双链表节点数据结构

/**
 * Double List structure
 */
struct rt_list_node
{
    struct rt_list_node *next;                          /**< point to next node. */
    struct rt_list_node *prev;                          /**< point to prev node. */
};
typedef struct rt_list_node rt_list_t;                  /**< Type for lists. */

双链表处理函数


函数定义中用到的宏定义说明
rt_inline,定义如下,static 关键字的作用是令函数只能在当前的文件中使用;inline 表示内联,用 static 修饰后在调用函数时会建议编译器进行内联展开。

#define  rt_inline  static __inline 

1. 初始化链表对象:RT_LIST_OBJECT_INIT(object)

注:RT-Thread Nano内核未调用

/**
 * @brief initialize a list object
 */
#define RT_LIST_OBJECT_INIT(object) { &(object), &(object) }

2. 链表初始化:rt_list_init(rt_list_t *l)

/**
 * @brief initialize a list
 *
 * @param l list to be initialized
 */
rt_inline void rt_list_init(rt_list_t *l)
{
    l->next = l->prev = l;
}

RT-Thread源码分析1:双链表_第3张图片

3. rt_list_insert_after(*l, *n)

在双链表头后面插入一个节点

/**
 * @brief insert a node after a list
 *
 * @param l list to insert it
 * @param n new node to be inserted
 */
rt_inline void rt_list_insert_after(rt_list_t *l, rt_list_t *n)
{
    l->next->prev = n;
    n->next = l->next;

    l->next = n;
    n->prev = l;
}

RT-Thread源码分析1:双链表_第4张图片

4. rt_list_insert_before(*l, *n)

在双链表头前面插入一个节点

/**
 * @brief insert a node before a list
 *
 * @param n new node to be inserted
 * @param l list to insert it
 */
rt_inline void rt_list_insert_before(rt_list_t *l, rt_list_t *n)
{
    l->prev->next = n;
    n->prev = l->prev;

    l->prev = n;
    n->next = l;
}

RT-Thread源码分析1:双链表_第5张图片

5. 移除一个链表节点:rt_list_remove(rt_list_t *n)

/**
 * @brief remove node from list.
 * @param n the node to remove from the list.
 */
rt_inline void rt_list_remove(rt_list_t *n)
{
    n->next->prev = n->prev;
    n->prev->next = n->next;

    n->next = n->prev = n;
}

RT-Thread源码分析1:双链表_第6张图片

6. 判断链表是否为空:rt_list_isempty(const rt_list_t *l)

/**
 * @brief tests whether a list is empty
 * @param l the list to test.
 */
rt_inline int rt_list_isempty(const rt_list_t *l)
{
    return l->next == l;
}

说明:如果链表头的指针next指向自身,说明链表为空。

7. 获取链表长度:rt_list_len(const rt_list_t *l)

/**
 * @brief get the list length
 * @param l the list to get.
 */
rt_inline unsigned int rt_list_len(const rt_list_t *l)
{
    unsigned int len = 0;
    const rt_list_t *p = l;
    while (p->next != l)
    {
        p = p->next;
        len ++;
    }

    return len;
}

注意:链表长度不包含链表头,如下链表,长度为3。
RT-Thread源码分析1:双链表_第7张图片

8. 获取当前所在结构体的地址:rt_list_entry(node, type, member)

根据成员变量(链表节点成员)地址,获取当前所在结构体的地址

/**
 * @brief get the struct for this entry
 * @param node the entry point
 * @param type the type of structure
 * @param member the name of list in structure
 */
#define rt_list_entry(node, type, member) \
    rt_container_of(node, type, member)

参数node:指向链表节点(属于结构体成员)的指针变量
参数type:结构体类型
参数member:链表节点在结构体中的成员名
返回值:指向当前结构体的指针

/**
 * rt_container_of - return the member address of ptr, if the type of ptr is the
 * struct type.
 */
#define rt_container_of(ptr, type, member) \
    ((type *)((char *)(ptr) - (unsigned long)(&((type *)0)->member)))

此函数来源于Linux内核源码

从一个结构的成员指针找到其容器的指针。

#define list_entry(ptr, type, member) /  
((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))

分析rt_container_of(ptr, type, member),举例结构体定义为:

typedef struct xxx
{
	......;	//结构体中的其他成员变量
	type_m member;
	......;	//结构体中的其他成员变量
}type;

/* 定义变量 */
type a;
type *p;
type_m *ptr;

ptr = &(a.member);
p   = rt_container_of(ptr, type, member);
/* 结果:p指向a,得到a的地址 */

((type *)0)把“0”强制转化为指针类型,指向type类型的数据;

&((type *)0)->member因为指针是type *类型的,所以可以取到以“0”为基地址的type型变量成员member的地址,也就是偏移地址;也就等于成员member到结构体基地址的偏移字节数。(注:内存单元按字节编址)

(unsigned long)(&((type *)0)->member)把偏移地址强制转化为unsigned long型数据;

(char *)(ptr)ptr为只指向type_m类型的指针(指向member成员的真实地址,使用(char *)强制转化为指向char型的指针;这样ptr指针的加减操作步长为一个字节。

((char *)(ptr) - (unsigned long)(&((type *)0)->member))member成员的真实地址 减去 偏移地址,得到结构体真实地址,此时地址指向的数据类型为char型。

(type *)强制类型转化为结构体指针类型,最终返回指向结构体的真实地址。


以下函数RT-Thread Nano内核未调用

9. 遍历链表:rt_list_for_each(pos, head)

注:RT-Thread Nano V3.1.3\components\finsh\cmd.c long list_mempool(void)调用一次

/**
 * rt_list_for_each - iterate over a list
 * @pos:	the rt_list_t * to use as a loop cursor.
 * @head:	the head for your list.
 */
#define rt_list_for_each(pos, head) \
    for (pos = (head)->next; pos != (head); pos = pos->next)

pos 指向宿主结构的指针,在for循环中是一个迭代变量
head 链表头

10. 安全地遍历链表:rt_list_for_each_safe(pos, n, head)

注:RT-Thread Nano内核未调用

/**
 * rt_list_for_each_safe - iterate over a list safe against removal of list entry
 * @pos:	the rt_list_t * to use as a loop cursor.
 * @n:		another rt_list_t * to use as temporary storage
 * @head:	the head for your list.
 */
#define rt_list_for_each_safe(pos, n, head) \
	for (pos = (head)->next, n = pos->next; pos != (head); \
		pos = n, n = pos->next)

pos 指向宿主结构的指针,在for循环中是一个迭代变量
n 用于临时存储下一个数据结构的指针变量
head 链表头

11. 循环遍历head链表中每一个pos中的member成员:rt_list_for_each_entry(pos, head, member)

注:RT-Thread Nano内核未调用

/**
 * rt_list_for_each_entry  -   iterate over list of given type
 * @pos:    the type * to use as a loop cursor.
 * @head:   the head for your list.
 * @member: the name of the list_struct within the struct.
 */
#define rt_list_for_each_entry(pos, head, member) \
    for (pos = rt_list_entry((head)->next, typeof(*pos), member); \
         &pos->member != (head); \
         pos = rt_list_entry(pos->member.next, typeof(*pos), member))

pos 指向宿主结构的指针,在for循环中是一个迭代变量
head 链表头
member 结构体中链表的成员名

12. 安全地循环遍历head链表中每一个pos中的member成员:rt_list_for_each_entry_safe(pos, n, head, member)

注:RT-Thread Nano内核未调用

/**
 * rt_list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
 * @pos:    the type * to use as a loop cursor.
 * @n:      another type * to use as temporary storage
 * @head:   the head for your list.
 * @member: the name of the list_struct within the struct.
 */
#define rt_list_for_each_entry_safe(pos, n, head, member) \
    for (pos = rt_list_entry((head)->next, typeof(*pos), member), \
         n = rt_list_entry(pos->member.next, typeof(*pos), member); \
         &pos->member != (head); \
         pos = n, n = rt_list_entry(n->member.next, typeof(*n), member))
13. 获取链表中的第一个元素:rt_list_first_entry(ptr, type, member)

注:RT-Thread Nano内核未调用

/**
 * rt_list_first_entry - get the first element from a list
 * @ptr:    the list head to take the element from.
 * @type:   the type of the struct this is embedded in.
 * @member: the name of the list_struct within the struct.
 *
 * Note, that list is expected to be not empty.
 */
#define rt_list_first_entry(ptr, type, member) \
    rt_list_entry((ptr)->next, type, member)

本文完~

关于作者

微信公众号:萤火虫的电子笔记

分享电子产品开发软、硬件方面知识,51单片机、STM32、ARM、AltiumDesigner PCB设计、开发平台、软件工具等知识分享。

欢迎转发,请注明出处及作者。关注微信公众号,获取最新文章动态。
在这里插入图片描述

你可能感兴趣的:(嵌入式:RTOS,RT-Thread,链表,双链表)