关于linux内核的数据结构——list_head

前言

最近想看linux内核,发现看了两周,还没啥进展,恰恰有些基础的东西往往忘记,这里先记录下最简单的东西,以后多记录。不可好高骛远,这里记录一个最基础的数据结构。list_head

正文

我们在学习谭浩强的《c程序设计》时候知道一个链表结构
最简单的结构是

struct student {
int num;
float score;
struct student * next;
};

具体用法我就不详细介绍,这里循环效率比较快。当然这种结构会有一个比较大的问题,只一个单向的。只用稍微改变以下。

struct student {
int num;
float score;
struct student *next , *prev;
};

可会我们还是有一个问题,发现没有?在任何时候需要一种list的时候,都要重新创建一个这样的结构体。如果是面向对象的语言也许很容易解决,直接声明一个借口就好了。可是这是c语言。可是c语言那么强大难道解决不了,当然不了。我们可以强制计算地址。
这里我们引出我们本节的强大的内容:

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

额,是不是太过简单了,开始学生的结构体就可以改变了:

struct student {
    struct list_head listint num;
    float score;
};

我们只要根据list的地址计算出student的地址(ps这里貌似他们两个相等),就可以直接间接实现了面向对象的所谓的上溯造型。这里我们还是看下linux内核提供的几个宏定义,很厉害

#define memlist_entry list_entry
#define list_entry(ptr, type, member) 
((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
//调用直接可以这样
student stu1= memlist_entry(mlist, struct student, list);

最终调用我们变成了这样

student stu1= ((student*)((char *)(ptr)-(unsigned long)(&((student*)0)->list)))

其实抛开前面强制转化。ptr是我们head_list的指针,而最重要的是我们如何获得我们head_list在student结构体中的位置呢。就有了这个变态的代码(unsigned long)(&((stdent *)0)->list),因为我们让在0号地址上创建一个student结构体。当然list指针就是所谓的偏移量。至于为什么要把ptr强制转化成char。我认为啥都可以。感觉int也可以。
(ps我写的代码是在linux2.4上的。路径是、include/linux/list.h.最新版本的的list_entry方法有点复杂,我暂时没研究,不过原理基本差不多。)
然后我们再说几个无聊的问题。


#define INIT_LIST_HEAD(ptr) do { \
    (ptr)->next = (ptr); (ptr)->prev = (ptr); \
}

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_del(struct list_head * prev,
                  struct list_head * next)
{
    next->prev = prev;
    prev->next = next;
}

这里不太复杂。仅仅是让大家记得这个几个函数。

后记

慢慢努力学习,这种c的小技巧太多了,要好好学习。有空再研究下红黑树。

你可能感兴趣的:(编程语言,Linux)