Linux内核链表和驱动demo简介

内核链表存在的意义
linux内核作为操作系统的核心,首要的任务就是高效地完成系统的资源分配和调度。
linux内核应用的各种数据结构就是为此而生的。内核链表就是其中最基础的结构之一。
内核链表的主要有以下几点特点:

  1. 节省空间
  2. 通用性
  3. 可拓展性
  4. 封装性

内核链表与传统链表的简单对比
传统的链表:
1、每一个节点的指针域和节点所对应的数据结构类型密切相关
2、传统链表的指针域指向的是下一个或者前一个节点的首地址
3、由于指针域跟节点的数据结构相关,对于不同的链表,需要定义
各自的链表操作方法(插入,删除,遍历等)

内核链表:
1、指针域跟数据类型无关,内核链表在的操作的时候(插入,删除,
遍历等)不再关注数据结构的类型
2、内核链表中的指针域指向下一个或前一个节点的指针域,而
不是节点的首地址

list_head定义
list_head是内核中的双向链表,与普通的链表最大的不同之处在于没有数据域,这样设计的好处是具有了通用性,几乎所有的内核管理设施都用到该数据结构。

struct list_head定义:
struct list_head {
struct list_head *next,prev;
};/
common/include/linux/list.h */
结构
实际使用中我们是将内核链表嵌入到要进行统一管理的宿主结构体中,结构如图所示,data表示宿主结构体的数据域,head和node表示内核链表结点。
在这里插入图片描述
常用操作
LIST_HEAD(name) //定义并初始化链表头
INIT_LIST_HEAD(struct list_head *list)//初始化链表头

list_add(struct list_head *new2add, struct list_head *head)//增
list_del(struct list_head *entry)//删
list_replace(struct list_head *old,struct list_head *new2add)//修改
list_for_each(pos, head)//遍历
list_splice()//合并

list_entry
这是一个用来获取宿主结构体首地址的宏函数:
#define list_entry(ptr, type, member)
container_of(ptr, type, member)
ptr:指向member的指针
type:宿主结构体类型
member:宿主结构体中链表结点变量名

container_of():返回值为宿主结构体的首地址
#define container_of(ptr, type, member) ({
const typeof( ((type *)0)->member ) *__mptr = (ptr);
(type *)( (char *)__mptr - offsetof(type,member) );})
list_for_each_entry
作用是循环遍历每一个pos中的member子项,返回值为pos:
#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(pos->member.next))
pos:宿主结构体类型的指针
head:链表头
member:宿主结构体中链表结点变量名
内核具体应用示例
在内核中有很多list_head结构体的应用,这里以binder.c中binder_work结构体为例:
struct binder_work {
struct list_head entry;
enum {
BINDER_WORK_TRANSACTION = 1,
BINDER_WORK_TRANSACTION_COMPLETE,

} type;
};/
common/drivers/staging/android/binder.c */

hlist_head
定义:
/* common/include/linux/list.h */
struct hlist_head {
struct hlist_node *first;
}; //表头
struct hlist_node {
struct hlist_node *next, **pprev;
}; //结点

hash表头:散列表,只存放一个hlist_node的指针,指向hlist_node链表

hash节点:双链表, 但和一般的双链表有所不同,next指向链表的下一个节点, 但pprev不是指向当前节点的前一个节点,而是指向当前节点的 前一 个节点的next指针,因此ppre是二级指针。

结构
实际使用中我们是将内核链表嵌入到要进行统一管理的宿主结构体中,其中第一个普通结点的ppre指向表头结点的first,最后一个结点的next指向NULL
在这里插入图片描述
一些操作
//初始化表头和节点
#define HLIST_HEAD_INIT { .first = NULL }
#define HLIST_HEAD(name) struct hlist_head name = { .first = NULL }
#define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL)
#define INIT_HLIST_NODE(ptr) ((ptr)->next = NULL, (ptr)->pprev = NULL)

static inline int hlist_unhashed(const struct hlist_node *h)
{
return !h->pprev;//判断该节点是否链进了哈希表链中
}

//获取所嵌入的宿主结构体的首地址,原理和list_head相同
#define hlist_entry(ptr, type, member) container_of(ptr,type,member)

hlist_add_head
//添加节点到链表头
static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h)
{
struct hlist_node *first = h->first;
n->next = first;//新节点的next指针指向原头结点
if (first)
first->pprev = &n->next;//原头结点的pprev指向新节点的next字段
h->first = n;//first指针指向新的节点(更换了头结点)
n->pprev = &h->first; //此时n是链表的头结点,将它的pprev指向list_head的first字段
}

__hlist_del

list_del 只是简单的调用__list_del函数。然后将prev、next指针分别被设为LIST_POSITION2
和LIST_POSITION1两个特殊值,对LIST_POSITION1和LIST_POSITION2的访问都将引起页故
障,它属于不安全的删除。

static inline void __hlist_del(struct hlist_node *n)
{
struct hlist_node *next = n->next;
struct hlist_node **pprev = n->pprev;
*pprev = next; // pprev指向的是前一个节点的next指针,当该节点是头节点时指向 hlist_head的first,
//两种情况下都可以通过这个操作删除掉所需删除的节点。
if (next)
next->pprev = pprev;//使删除节点的后一个节点的pprev指向所删除节点的前一个节点的next字段。
}

内核中具体应用示例
在内核哈希表的使用也是非常普遍的,如inode、文件系统完整性检测等。这里以binder
中的binder_proc结构体为例:
struct binder_proc {
struct hlist_node proc_node;

struct hlist_node deferred_work_node;

struct list_head buffers;

struct list_head delivered_death;
};/* common/drivers/staging/android/binder.c */

具体内核链表例子demo
//结构体定义
struct student
{
int id;
char* name;
struct list_head list;
};

// 创建并初始化链表头
LIST_HEAD(stu_head);

// 添加到链表
list_add (&stu1->list, &stu_head);

//遍历并打印
list_for_each_entry(stu, &stu_head, list)
{
print_student(stu);
}
在这里插入图片描述
具体例子连接:https://download.csdn.net/download/yjlyyj/85509068

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值