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

来源:互联网 发布:南风知我意2书包网 编辑:程序博客网 时间:2024/06/05 06:24

前言

最近想看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的小技巧太多了,要好好学习。有空再研究下红黑树。

1 0