Linux内核源码学习(第一天)——Linux内核中的链表

0x00 前言

linux中的双向链表和传统的双向链表不太一样,是注入了抽象封装灵魂的链表,

他不是把将数据结构塞入链表,而是将链表节点塞入数据

之前在windows里就见识过_ETHREAD结构.....的双向链表,但苦于没有源码,所以在linux里好好康一康。

链表的头文件是list.h(include\linux\list.h),我们重此来探究

参考文章:

https://blog.csdn.net/xu_ya_fei/article/details/49744699

https://www.cnblogs.com/wangzahngjun/p/5556448.html

https://wenku.baidu.com/view/5df5736fa0116c175e0e4817.html

https://blog.csdn.net/wanshilun/article/details/79747710

https://www.ibm.com/developerworks/cn/linux/kernel/l-chain/index.html

0x01 传统双向链表

详见此文http://data.biancheng.net/view/8.html

就不在此赘述了。

0x02 基础数据结构

1. 内核链表的定义

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

可以看出来,linux内核里并没有数据域,这正是体现抽象思想的表现,把双向链表的共性(前驱指针,后继指针)作为一个基本数据结构。

所以我们就可以如下使用

struct My_List{
    void* My_Data;
    struct list_head My_List;
};

1) list_head 是一种侵入式链表,数据附加在链表之上,使得 list_head 数据结构是通用的,使用起来就不需要考虑节点数据的类型。

2)list_head结构体可以添加到结构体的任何位置

3)可以为 list_head 命名

4)可以添加多个list_head链表

2.是否有效

#ifdef CONFIG_DEBUG_LIST
extern bool __list_add_valid(struct list_head *new,
			      struct list_head *prev,
			      struct list_head *next);
extern bool __list_del_entry_valid(struct list_head *entry);
#else
static inline bool __list_add_valid(struct list_head *new,
				struct list_head *prev,
				struct list_head *next)
{
	return true;
}
static inline bool __list_del_entry_valid(struct list_head *entry)
{
	return true;
}
#endif

通常在进行插入和删除时会首先判断是否有效

 

3.WRITE_ONCE && READ_ONCE

在某些情况下CPU对内存中变量读写并不是一次完成的,这可能会出现竞争。而READ_ONCE和WRITE_ONCE实现对变量一次性读取和一次性写入。

详情可以看这篇文章:https://blog.csdn.net/cloudblaze/article/details/51676139

 

4. offsetof  &&  container_of

在下文 "遍历" 时详细说明。

 

0x03 基础函数

1. 声明与初始化

上一段描述了链表的链节点,在LIST_HEAD这个宏中描述了头节点

在源码中可以看到俩个宏:

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

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

第一个:&(name)  前驱指针,后继指针都是指向自己的指针

第二个:调用了第一个的初始化,使用宏 LIST_HEAD_INIT 进行初始化,这会使用变量name 的地址来填充prev和next 结构体的两个变量

两者的使用的不同:第一个宏是初始化,第二个宏是声明+初始化

除了宏以外还有Linux还提供了一个INIT_LIST_HEAD宏用于运行时初始化链表

static inline void
INIT_LIST_HEAD(struct list_head *list)
{
    list->next = list->prev = list;
}

2. 插入

在linux内核中一共有俩个插入的函数

static inline void list_add()
static inline void list_add_tail()

list_add在链表头插入,实现栈的功能

list_add_tail在链表尾

  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值