linux内核链表源码,Linux内核源代码——链表

本文详细介绍了Linux内核中的双向链表实现方式,并通过具体示例展示了如何定义链表节点、初始化链表以及进行节点操作等核心功能。

关于Linux内核代码的真正接触还是最近2个月,首先接触这个最简单的就是双向链表了,这个链表可谓真的短小。原来有些时候,看这种经典代码也是一种享受。在大学里面写的那些双向链表那只不过是学习而已。

#ifndef _LINUX_LIST_H

#define _LINUX_LIST_H

#ifdef __KERNEL__

structlist_head {

structlist_head *next, *prev;

};

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

#define LIST_HEAD(name) /

structlist_head name = LIST_HEAD_INIT(name)

#define INIT_LIST_HEAD(ptr) do { /

(ptr)->next = (ptr); (ptr)->prev = (ptr); /

}while(0)

static__inline__void__list_add(structlist_head *new,

structlist_head * prev,

structlist_head * next)

{

next->prev =new;

new->next = next;

new->prev = prev;

prev->next =new;

}

static__inline__voidlist_add(structlist_head *new,structlist_head *head)

{

__list_add(new, head, head->next);

}

static__inline__voidlist_add_tail(structlist_head *new,structlist_head *head)

{

__list_add(new, head->prev, head);

}

static__inline__void__list_del(structlist_head * prev,

structlist_head * next)

{

next->prev = prev;

prev->next = next;

}

static__inline__voidlist_del(structlist_head *entry)

{

__list_del(entry->prev, entry->next);

}

static__inline__voidlist_del_init(structlist_head *entry)

{

__list_del(entry->prev, entry->next);

INIT_LIST_HEAD(entry);

}

static__inline__intlist_empty(structlist_head *head)

{

returnhead->next == head;

}

static__inline__voidlist_splice(structlist_head *list,structlist_head *head)

{

structlist_head *first = list->next;

if(first != list) {

structlist_head *last = list->prev;

structlist_head *at = head->next;

first->prev = head;

head->next = first;

last->next = at;

at->prev = last;

}

}

#define list_entry(ptr, type, member) /

((type *)((char*)(ptr)-(unsignedlong)(&((type *)0)->member)))

#define list_for_each(pos, head) /

for(pos = (head)->next; pos != (head); pos = pos->next)

#endif /* __KERNEL__ */

#endif

这些链表结构体只能在内核使用,但其实我们在平时写链表时,可以仿照这样,精简。

如果我们需要某种数据结构的队列,就在这种结构内部放上list_head结构即可。例如:

typedef struct page {

struct list_head list;

.....

struct list_head lru;

....

}page_t;

这里面放了了2个list_head结构体(list和lru),这代表page结构体可以存在两个队列中,如下面图所示:

a3cd5dee173984134efe0bd8e937c333.png

在这链表里面最关键的地方在

#define list_entry(ptr, type, member) /

((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))

通过它才可以访问链表里面真正的元素

例如:

list_head pstList;

struct page *page;

page = list_entry(pstList, struct page, list); 其中pstList 指向一个page结构体。

list_entry的原理的,通过这个结构体链表头在结构体中的偏移来获取 某个结构体的地址。

(unsigned long)(&((type *)0)->member)),这个是获取结构链表头的偏移值,如在page中,list偏移page的值

然后再把该地址减去该偏移才真正得到我们想要的结构体地址。0b1331709591d260c1c78e86d0c51c18.png

一、linux内核链表 1、普通链表的数据区域的局限性 之前定义数据区域时直接int data,我们认为我们的链表中需要存储的是一个int类型的数。但是实际上现实编程中链接中的节点不可能这么简单,而是多种多样的。 一般实际项目中的链表,节点中存储的数据其实是一个结构体,这个结构体中包含若干的成员,这些成员加起来构成了我们的节点数据区域。 2、一般性解决思路:即把数据区封装为一个结构体 (1)因为链表实际解决的问题是多种多样的,所以内部数据区域的结构体构成也是多种多样的。 这样也导致了不同程序当中的链表总体构成是多种多样的。 我们无法通过一套泛性的、普遍适用的操作函数来访问所有的链表,意味着我们设计一个链表就得写一套链表的操作函数(节点创建、插入、删除、遍历……)。 (2)实际上深层次分析会发现 不同的链表虽然这些方法不能通用需要单独写,但是实际上内部的思路和方法是相同的,只是函数的局部地区有不同。 实际上链表操作是相同的,而涉及到数据区域的操作就有不同 (3)问题 能不能有一种办法把所有链表中操作方法里共同的部分提取出来用一套标准方法实现,然后把不同的部分留着让具体链表的实现者自己去处理。 3、内核链表的设计思路 (1)内核链表中实现一个纯链表的封装,以及纯链表的各种操作函数 纯链表就是没有数据区域,只有前后向指针; 各种操作函数是节点创建、插入、删除、遍历。 这个纯链表本身自己没有任何用处,它的用法是给我们具体链表作为核心来调用。 4、list.h文件简介 (1)内核中核心纯链表的实现在include/linux/list.h文件中 (2)list.h中就是一个纯链表的完整封装,包含节点定义和各种链表操作方法。 二、内核链表的基本算法和使用简介 1、内核链表的节点创建、删除、遍历等 2、内核链表的使用实践 (1)问题:内核链表只有纯链表,没有数据区域,怎么使用? 使用方法是将内核链表作为将来整个数据结构的结构体的一个成员内嵌进去。类似于公司收购,实现被收购公司的功能。 这里面要借助container_of宏。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值