linux内核中的list详解

更新时间:2023-05-06 18:44:35 阅读: 评论:0

linux内核中的list详解
原因:
file_operation 结构中的open函数定义如下:
int (*open)(struct inode *inode, struct file* filp);
inode中含有i_cdev属性,它描述的是字符设备。在自己定义的字符设备中,一般会包含字符设备的指针,而open方法被调用时,通常需要获取特定的设备对象,这里就涉及到一个问题:如何通过结构中的某个变量获取结构本身的指针。Linux 内核中提供了container_of宏(WDM中也定义了相似功能的宏)。C99中定义了两个宏,typeofofftof,它们返回的是某个变量的类型和结构中某变量在结构中的偏移量。可以预想的是,没有编译器的支持,container_of的宏是很难实现的(至少我还没有想出能够不用 typeof宏实现container_of的方法)。
优点:
值得一提的是,offtof宏的实现非常巧妙,它把0地址转化为TYPE结构的指针,然后获取该结构中MEMBER成员的指针,并将其强制类型转换为size_t类型。于是,由于结构从0地址
开始定义,因此,cast后的MEMBER成员地址,实际上就是它在结构中的偏移量。这也显示出了C语言中指针的强大。因为,在某个体系结构下实现的libc,结构中各个成员的偏移总是可以预见的,这比C#那种以托管的方式管理内存的自由度要大的多。

实现:
container_of宏定义在include/linux/kernel.h中:
offtof宏定义在include/linux/stddef.h中:
container_of宏,它的功能是得到包含某个结构成员的结构的指针:
其实现如下:
#define offtof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#define container_of(ptr, type, member) ({                      \
        const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
        (type *)( (char *)__mptr - offtof(type,member) );})
 
    分析可知__mptr指向的是一个type结构里typeof(((type *)0)->member)类型member成员
的指针,offtof(type,member)是这个成员在结构中的偏移,单位是字节,所以为了计算type结构的起始地址,__mptr减去它自己的偏移。
说明:offtof是求MEMBERTYPE结构体中的偏移量,container_of是给定结构体某个成员的地址反推其结构体地址。两个宏实现的方法都很直观,无需多解释。倒是有一点:container_oflinux/kernel.h中定义的,其中用到的typeof 操作符和语句表达式(即({ statements })”这种写法)都是gccC的扩展.

/hondrif82q/archive/2007/04/14/1564893.aspx
linux内核中的list详解
 
1 list_entry作用就是通过list_head型指针ptr换算成其宿主结构的起始地址,该宿主结构是type型的,
ptr在其宿主结构中定义为member成员。定义在内核源文件include/linux/list.h中,
对比list_entry(ptr,type,member)可知有以下结果:
其中list相当于member成员,struct example_struct相当于type成员,ptr相当于ptr成员。而list{}成员嵌套于example_struct{}里面。ptr指向example_struct{}中的list成员变量的。在list_entry()作用下,将ptr指针回转指向struct example_struct{}结构体的开始处
#define list_entry(ptr, type, member) \
 container_of(ptr, type, member)
 container_of是个宏,定义如下:
#define container_of(ptr, type, member) ({  \
        const typeof( ((type *)0)->member ) *__mptr = (ptr); \      // typeofgcc扩展,用于得知数据类型的,这句话作用大
              //概是看ptr是否是结构体里成员变量member的类型,不是编译时将报错,类型检测的

        (type *)( (char *)__mptr - offtof(type,member) );})     
offtof是计算成员member在结构体的type里面的偏移数,__mptr的地址减去偏移地址就可以得到type结构体的开始地址
container_of向上面这样定义展开后可以直接赋值如下面这样
#include <stdio.h>
int main(int argc,char argv[])
{
 int x = 1,y = 2;
 int m =
 ({
  x++;
  x + y;
 });
 
 fprintf(stdout,"----%d\n",m);
}
这种用法应该是gcc的扩展
2
linux内核中,list是无处不在,好多代码都includelist.h。在linux源代码中,由于list的运用过于频繁,所以就把 list 单独定义了,如果有哪个结构要成为列表,就把结构的第一个成员定义成list_head,然后初始化就可以了。
    list.h应该是在include/linux/list.h,里面的代码很易懂,大家翻翻吧。当然数据结构要过关先,起码要知道list是双向链表还有双向链表的结构是如何的。
    一部分代码:
typedef struct list_head {
        struct list_head *next, *prev;        //从定义中可以看出是个双向队列
} list_t;
#define LIST_HEAD_INIT(name) { &(name), &(name) }     
#define LIST_HEAD(name) \
        struct list_head name = LIST_HEAD_INIT(name)    //定义一个空的列表
#define INIT_LIST_HEAD(ptr) do { \              //初始化一个已定义的列表
        (ptr)->next = (ptr); (ptr)->prev = (ptr); \
} while (0)
.
.
前三个宏用来初始化一个nextprev指针皆指向自身的空链表,宏可以用到的地方明显受限于c语法约束。例如,LIST_HEAD_INIT()用来初始化结构元素,第二个宏用来初始化静态变量,第三个用于函数内部。
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);
}
list.h中还有一个比较有趣的宏
/**
* list_entry - get the struct for this entry
* @ptr:        the &struct list_head pointer.
* @type:      the type of the struct this is embedded in.
* @member:    the name of the list_struct within the struct.
*/
#define list_entry(ptr, type, member) \
        ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
从上面我们知道list_entry的作用是返回类型type的数据结构地址,但是具体怎么实现的呢?看代码怪怪的。其实我们只要知道 &((type *)0)->member 的意思就明白了。它是取结构成员偏移量的一种方法,将常数0强制为结构指针,取其成员的地址,结果就是成员的偏移量。
list_entry宏根据list_head型指针ptr换算成其宿主结构的起始地址,该宿主结构是 type型的,而ptr在其宿主结构中定义为member成员。如下图:
req-->|type型对象起始地址
|
|... ...
ptr-->|ptr指针所指的member成员地址
|
|... ...
ptr指向图中所示的位置,通过(unsigned long)(&((type*)0)->member)得到ptr req之间的差值,ptr减去这个差值就得到了type型宿主结构的指针req,返回类型为(type*)
    了解上面那个宏,你就知道了 list_head 是如何工作的了。首先,我们先定义并初始化一个 list_head,然后声明一个结构,如下
struct demo {
struct list_head list;
int i;
}
struct demo demo1;
list_add(&demo1->list, list_head)        //demo1加入列表,list_head仅仅就是两个指针
这样的列表是非常灵活的,不同类型的变量都可以加到列表中去。如下图所示:

_________________________________________________
|  list    |        1          |        2  |          3          |
|_head |____________|___________|____________ |____
                    ||                  ||                    ||
                    ||                  ||                    ||
                demo1            demo2      struct dev dev1

本文发布于:2023-05-06 18:44:35,感谢您对本站的认可!

本文链接:https://www.wtabcd.cn/fanwen/fan/90/98323.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:结构   地址   成员   定义   指针   宿主
相关文章
留言与评论(共有 0 条评论)
   
验证码:
Copyright ©2019-2022 Comsenz Inc.Powered by © 专利检索| 网站地图