Linux内核链表

来源:互联网 发布:左淘宝店铺头像 编辑:程序博客网 时间:2024/05/12 10:43

     链表是一种常用的数据结构,早在大一学C语言的时候就已经学过,之后又在数据结构、算法等课上多次提及和使用。但是最近在学习Linux的内核,发现在Linux内核中的链表跟我们平常所使用的链表有很大的不同,现来总结一下。

    首先,我们之前所使用的链表的每一个结点都是一样的。无论是单向链表还是双向链表、还是循环链表,使用链表之前都要声明一个结构体Node,在Node中存放数据域和指针域,以双向链表为例:

typedef struct Node{struct Node* next;struct Node* prev;        int num;}Node;

    在链表中,我们需要维护一个头结点,每次访问链表中的数据就需要从头结点开始,依次往后寻找。找到结点之后,再在结点中找出数据。简单来说,一般的链表是结点“包裹”着数据。

    这样带来一个不便就是所有的结点的类型必须是一样的,如果是不同类型的结点,他们的数据域的类型、个数等不一样的时候,就不能把它们串在一起。

    但是,在Linux内核中,经常会需要把不同数据结构的结点串在一起形成链表,这是我们就需要对链表进行改进:我们在得到链表的某个结点后,不是再在结点内部寻找数据,而是在外面找。如下面两张图所示:


    这样的设计,使得指针域单独出来形成一个结点,作为整个结点的一个成员,然后再和其他数据域的成员形成一个大的结点。

    现在的问题是,在得到指针域的结点后,如何访问其他的数据域?利用指针的偏移。

    Linux提供了一个宏:list_entry,具体的源码如下:

#define list_entry(ptr, type, member) \    container_of(ptr, type, member)#define container_of(ptr, type, member) ({          \    const typeof(((type *)0)->member)*__mptr = (ptr);    \             (type *)((char *)__mptr - offsetof(type, member)); })#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)

首先,传入的三个参数分别代表:

    指向结构体中某个成员的指针

    结构体

    结构体的该成员

返回该结构体的首地址,也就是得到结构体,这样我们就能访问结构体中的成员。

 

例如:结构体S中有以下三个成员,我们可以通过其中一个成员的地址来获得整个结构体的地址:

typedef struct S{int first;double second;char third;}S;S s;S* ss = list_entry(&s.second, S, second);

下面解释一下这个宏:

①const typeof(((type *)0)->member)*__mptr= (ptr);

这句话声明一个与menber类型一样的真正_mptr,并保存ptr的值,在上面的例子中,就是:

const typeof(((S *)0)->second)*__mptr= (&s.second);

也就是const int* _mptr= &s.second;

 

offsetof(TYPE,MEMBER) ((size_t) &((TYPE *)0)->MEMBER)

这句话其实是获得成员MEMBER相对于结构体的偏移值,在上面的例子中,就是:

(size_t) &((S *)0)->second

在上面的例子中:

offsetof( S, first)= 0;

offsetof( S, second)= 8;

offsetof( S, third)= 16;

 

(type *)((char*)__mptr - offsetof(type, member));

这句话就是用刚刚声明的临时变量_mptr减去偏移值,这样就得到了结构体的首地址,如图:

    有了这个“根据结构体的某个成员的地址来获得结构体的首地址”的宏,就可以通过链表结点的指针域来获得“外部”的数据域。

   下面举个例子,例子中的双向循环链表有添加结点(在头结点之前加、在头结点之后加)和删除结点的功能。每次操作之后输出整个链表。

#include <stdio.h>#include <stdlib.h>typedef struct list_head{struct list_head* next;struct list_head* prev;}list_head;#define list_entry(ptr, type, member) container_of(ptr, type, member)#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)#define container_of(ptr, type, member) ({ \    const typeof( ((type *)0)->member ) *__mptr = (ptr); \    (type *)( (char *)__mptr - offsetof(type,member) );})//初始化链表,结点的前驱和后继指向自己static inline void init_list_head( list_head* list ){list->next = list;list->prev = list;}//插入结点static inline void __list_add( list_head* newNode, list_head* prevNode, list_head* nextNode ){nextNode->prev = newNode;newNode->next = nextNode;newNode->prev = prevNode;prevNode->next = newNode;}//在头结点之后插入static inline void _list_add_behind_head( list_head* newNode, list_head* head ){__list_add( newNode, head, head->next );}//在头结点之前插入static inline void _list_add_front_head( list_head* newNode, list_head* head ){__list_add( newNode, head->prev, head );}static inline void __list_del( list_head* delNode ){delNode->prev->next = delNode->next;delNode->next->prev = delNode->prev;delNode->next = NULL;delNode->prev = NULL;free( delNode );}typedef struct A{int first;list_head list;}A;typedef struct B{int first;int second;list_head list;}B;typedef struct C{int first;int second;int third;list_head list;}C;int main(){A a = { 11 };B b = { 21, 22 };C c = { 31, 32, 33 };//初始化头结点init_list_head( &a.list );printf("%d\n",a.first);_list_add_behind_head( &b.list, &a.list ); //在头结点后插入b_list_add_behind_head( &c.list, &a.list ); //在头结点后插入c,此时链表为a→c→b→a(循环)//输出a后面的结点,C类型printf("%d\n",list_entry( a.list.next, C, list )->first); printf("%d\n",list_entry( a.list.next, C, list )->second);printf("%d\n",list_entry( a.list.next, C, list )->third);//输出a后面的后面的结点,B类型printf("%d\n",list_entry( a.list.next->next, B, list )->first);printf("%d\n",list_entry( a.list.next->next, B, list )->second);//输出a后面的后面的后面的结点,本身printf("%d\n\n",list_entry( a.list.next->next->next, A, list )->first);C d = { 41, 42, 43 };_list_add_front_head( &d.list, &a.list ); //在头结点a之前插入一个C类型的结d,此时链表为a→c→b→d→a(循环)printf("%d\n",list_entry( a.list.prev, C, list )->first);printf("%d\n",list_entry( a.list.prev, C, list )->second);printf("%d\n",list_entry( a.list.prev, C, list )->third);printf("%d\n",list_entry( a.list.prev->prev, B, list )->first);printf("%d\n",list_entry( a.list.prev->prev, B, list )->second);printf("%d\n",list_entry( a.list.prev->prev->prev, C, list )->first);printf("%d\n",list_entry( a.list.prev->prev->prev, C, list )->second);printf("%d\n",list_entry( a.list.prev->prev->prev, C, list )->third);printf("%d\n\n",list_entry( a.list.prev->prev->prev->prev, A, list )->first);__list_del( &c.list ); //删除结点c,此时链表为a→b→d→a(循环)printf("%d\n",list_entry( a.list.next, B, list )->first);printf("%d\n",list_entry( a.list.next, B, list )->second);printf("%d\n",list_entry( a.list.next->next, C, list )->first);printf("%d\n",list_entry( a.list.next->next, C, list )->second);printf("%d\n",list_entry( a.list.next->next, C, list )->third);printf("%d\n\n",list_entry( a.list.next->next->next, A, list )->first);return 0;}

    在本例中我们可以看到,我们讲不同类型的结点ABC串在了一起,而且我们只需要有头结点,我们就可以直接访问链表中的所有结点的所有成员。

0 0