linux/list.h (

本文深入探讨了Linux内核中双向循环链表的结构体定义,阐述了链表节点不包含数据域的作用,并详细解释了如何通过list_head结构体维护数据结构列表。同时,介绍了链表操作如添加、删除和遍历的方法,以及list_for_each宏的使用,最后解释了结构体成员初始化和container_of宏的作用。
linux/list.h中,双向循环链表的结构体定义:

struct list_head {
    struct list_head *next, *prev;
};


可以看到,这个结构中不包括数据域。也就是说,这个结点本身并不保存什么数据信息。那它的作用是什么呢?

linux内核经常要维护数据结构的列表。实现的方法就是通过将 struct list_head 结构体嵌入到所需要维护的数据结构中去,通过维护这个链表结构体,来达到所要维护数据结构的列表。

LDD3的图11-1明确地显示了这种关系。

在把 list_head 嵌入到一个 custom structure 后,这些 custom structure 就有了链接的关系。
通过 list_entry macro 可以从一个链表节点得到包含该节点的 custom structure 的指针。

/**
 * list_entry - get the struct for this entry
 * @ptr:    the &struct list_head pointer.
 * @type:    the type of the struct this is embedded in.
 * @member:    the name of the list_struct within the struct.
 */

#define list_entry(ptr, type, member) \
    container_of(ptr, type, member)

(解释补在后面)

对于链表的操作:添加、删除...等,头文件中都有。一个需要理解的操作是:遍历
也就是通过该链表,遍历整个 custom structure 链表。
这个是通过 list_for_each_entry宏实现的,其代码如下:

/**
 * 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 list_for_each_entry(pos, head, member)                \
    for (pos = list_entry((head)->next, typeof(*pos), member);    \
     prefetch(pos->member.next), &pos->member != (head);     \
     pos = list_entry(pos->member.next, typeof(*pos), member))

其中的prefetch是预取当前结点的下一个结点,当链表节点很多时,它能加快运行速度。

关于结构体成员的初始化
一开始没有看懂下面这个代码:

#define LIST_HEAD_INIT(name) { &(name), &(name) }

#define LIST_HEAD(name) \
    struct list_head name = LIST_HEAD_INIT(name)

第一个宏后面的 {&(name), &(name)} 在第二个宏中当作右值直接赋给 struct list_head name.
一个双向循环链表初始是将头结点的两个成员指针赋值为本结构体的地址!

该头文件还提到一个hlist,它主要是为HASH表而构造的数据类型。有数据结构的基础,仔细看代码就可以弄清楚它的结构。这里主不多提了。
http://www.ibm.com/developerworks/cn/linux/kernel/l-chain/#N101F3

补:contaner_of的理解

------------------
#define container_of(ptr, type, member) ({            \
    const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
    (type *)( (char *)__mptr - offsetof(type,member) );})
----
    第一句:定义一指针指向ptr。
    第二句:用ptr的地址 |减去| member成员变量在type中的偏移地址,
        再强转换成type类型的地址。
    作用:
    -------        
    由member成员的地址(ptr为指向member的指针)得到包含member成员的type的地址。
    -------
    一个例子:
        struct scull_dev *dev;
        container_of(inode->i_cdev, struct scull_dev, cdev);
    其中:i_cdev是指向cdev的指针。[LDD3-P62]

------------------
#ifdef __compiler_offsetof
#define offsetof(TYPE,MEMBER) __compiler_offsetof(TYPE,MEMBER)
#else
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#endif
----
    作用:获取MEMBER在TYPE中的偏移地址
------------------


PS:
注意到贯穿整个文件的:   static inline
还有内核API:双下划线'__'为前缀,不过这些API都会被封装到省略'__'的相同API函数中去。
是的,你可以使用 Linux 内核中的 `include/linux/list.h` 文件,但需要注意的是:**它只能在内核空间中使用**。如果你是在**用户空间**编写程序(比如一个普通的 C 程序),就不能直接使用这个头文件,因为它是为 Linux 内核设计的,依赖于内核的编译环境和一些宏定义。 --- ## 一、`include/linux/list.h` 的用途 `list.h` 是 Linux 内核中实现**通用双向循环链表**的核心头文件。它的特点如下: - **嵌入式链表**:链表节点(`struct list_head`)被嵌入到结构体内部,而不是作为独立结构。 - **宏定义丰富**:提供 `list_add`, `list_del`, `list_for_each_entry` 等宏操作链表。 - **高效、通用、安全**:被广泛用于内核模块、驱动、文件系统等。 --- ## 二、能否在用户空间使用? ### ✅ 可以使用,但需要做一定适配: 你可以**将 `list.h` 和相关依赖文件复制到你的用户空间程序中**,并做一些修改,使其能够在用户空间正常编译运行。 --- ## 三、如何适配 list.h 到用户空间? 以下是一个简化版的 `list.h`,适配了用户空间使用的版本: ### 1. 创建 `ulist.h` ```c // ulist.h - 用户空间版 list.h 简化版 #ifndef ULIST_H #define ULIST_H #include <stddef.h> // 链表结构体 struct list_head { struct list_head *next, *prev; }; // 初始化链表头 #define LIST_HEAD_INIT(name) { &(name), &(name) } #define LIST_HEAD(name) struct list_head name = LIST_HEAD_INIT(name) static inline void INIT_LIST_HEAD(struct list_head *list) { list->next = list; list->prev = list; } // 添加节点 static inline void __list_add(struct list_head *new, struct list_head *prev, struct list_head *next) { next->prev = new; new->next = next; new->prev = prev; prev->next = new; } static inline void list_add(struct list_head *new, struct list_head *head) { __list_add(new, head, head->next); } static inline void list_add_tail(struct list_head *new, struct list_head *head) { __list_add(new, head->prev, head); } // 删除节点 static inline void __list_del(struct list_head *prev, struct list_head *next) { next->prev = prev; prev->next = next; } static inline void list_del(struct list_head *entry) { __list_del(entry->prev, entry->next); } // 判断链表是否为空 static inline int list_empty(const struct list_head *head) { return head->next == head; } // 遍历链表 #define list_for_each(pos, head) \ for (pos = (head)->next; pos != (head); pos = pos->next) // 获取包含链表节点的结构体指针 #define list_entry(ptr, type, member) \ ((type *)((char *)(ptr) - offsetof(type, member))) // 遍历链表并获取结构体指针 #define list_for_each_entry(pos, head, member) \ for (pos = list_entry((head)->next, typeof(*pos), member); \ &pos->member != (head); \ pos = list_entry(pos->member.next, typeof(*pos), member)) #endif ``` --- ## 四、使用方式(示例) 你可以像在内核中一样使用这个简化版的 `list.h`: ```c #include "ulist.h" #include <stdio.h> #include <stdlib.h> #include <string.h> // 定义结构体 struct student { int id; char name[32]; struct list_head list; }; int main() { struct list_head student_list; INIT_LIST_HEAD(&student_list); // 添加节点 struct student *s1 = malloc(sizeof(struct student)); s1->id = 1; strcpy(s1->name, "Alice"); list_add_tail(&s1->list, &student_list); struct student *s2 = malloc(sizeof(struct student)); s2->id = 2; strcpy(s2->name, "Bob"); list_add_tail(&s2->list, &student_list); // 遍历链表 struct student *pos; list_for_each_entry(pos, &student_list, list) { printf("Student: %d, %s\n", pos->id, pos->name); } // 清理 list_for_each_entry_safe(pos, tmp, &student_list, list) { list_del(&pos->list); free(pos); } return 0; } ``` --- ## 五、总结 | 项目 | 内核空间 | 用户空间 | |------|----------|----------| | 能否使用 `include/linux/list.h` | ✅ 是 | ❌ 否 | | 是否可移植 | 否 | ✅ 是 | | 是否需要修改 | 否 | ✅ 是 | | 是否支持所有宏 | ✅ 是 | ❌ 部分支持 | --- ## 六、相关问题
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值