linux内核双向链表学习

来源:互联网 发布:伍聚网络股票 编辑:程序博客网 时间:2024/05/24 08:34

      在linux内核中双向链表普遍存在,因此,理解链表的添加,删除,替换,遍历等操作很重要。

1、概述

      这里主要讲述对链表里宏定义说明、基本操作,下面是链表的基本框图(摘自百度图片)。


2、宏定义说明

(1)offsetof

         #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)   计算结构体成员MEMBER在结构体TYPE中的偏移量

 (TYPE *)0将0地址强制转化为TYPE类型结构体

 (TYPE *)0)->MEMBER指向结构体成员

&((TYPE *)0)->MEMBER获取成员地址,由于结构体TYPE的地址为0,所以成员地址就是该成员在TYPE中的偏移量。

((size_t) &((TYPE *)0)->MEMBER)将地址转化为size_t数据类型


(2)container_of

       具体定义如下(src/include/linux/kernel.h):

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

       根据结构体type的成员变量member指针ptr地址推导出结构体type变量的起始地址指针。typeof:类似sizeof获取参数的类型,有两种形式:表达式和类型。参数为表达式,则表达式不执行返回该表达式的类型,extern int foo();typeof(foo()) var;等价于int var。参数为数据类型,typeof(int *)p1, p2;等价于int *p1, *p2。

typeof( ((type *)0)->member )由于(type *)0)->member为表达式,因此typeof返回该member的数据类型

const typeof( ((type *)0)->member ) *__mptr = (ptr); 定义一个数据类型同member的常量指针,并指向ptr指向的地址。

(char *)__mptr 将指针转化为字符型指针,地址可转化到1字节

offsetof(type,member)成员member在结构体type中的偏移量

(char *)__mptr - offsetof(type,member) mptr即结构体type中member真实地址-member在结构体中的偏移量,得到该结构体type的起始地址。

(type *)( (char *)__mptr - offsetof(type,member) )将该地址转化为结构体type指针


3、linux内核链表操作函数

(1)涉及文件

src/include/linux/list.h

src/include/linux/types.h

基本思路:它是将双向链表节点嵌套在其它的结构体中;在遍历链表的时候,根据双链表节点的指针获取"它所在结构体的指针",从而再获取数据

(2)节点定义

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;
}

LIST_HEAD新建一个双向链表表头都指向自己,LIST_HEAD_INIT和INIT_LIST_HEAD意义一样。

(3)添加节点

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);
}

一个是向链表头添加,一个向链表末尾添加。

(4)删除节点

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

static inline void __list_del_entry(struct list_head *entry)
{
    __list_del(entry->prev, entry->next);
}


static inline void list_del(struct list_head *entry)
{
    __list_del(entry->prev, entry->next);
    entry->next = LIST_POISON1;
    entry->prev = LIST_POISON2;
}

(5)获取节点

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

根据结构体type的成员变量member指针ptr地址推导出结构体type变量的起始地址指针,详细见上一节说明。

(6)循环获取节点

#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))

pos:结构体指针变量,head:双向链表头变量,member:结构体成员变量类型为struct list_head 

typeof(*pos):根绝结构体指针pos获取结构体类型

pos = list_entry((head)->next, typeof(*pos), member):根据链表头获取链表第一个元素

&pos->member != (head):判断双向链表是否循环结束

pos = list_entry(pos->member.next, typeof(*pos), member):获取下一个链表元素


4、简单example

定义实际结构体:

struct family

{

int num;

int sex;

        int age;

char* name;

struct list_head list;

};

定义双向链表头:LIST_HEAD(family_head)

定义结构体实体:struct family father, mother, son;

添加到链表:list_add(&father.list,  &family_head), list_add_tail(&mother.list,  &family_head), list_add_tail(&son_list,  &family_head),整个链表就是family_head <=>father<=>mother<=>son<=>family_head。

搜寻整个链表:

        定义结构体指针 struct family *pfamily

        list_for_each_entry(pfamliy, family_head, list) 每次返回的就是father, mother, son的起始地址

0 0
原创粉丝点击