linux内核链表使用介绍
1.linux内核常用的数据有双链表,hash链表,红黑树,基树,区间树,根数,B树。闲来无事,进行了一些研究。
2.linux内核的双链表程序,程序还是比较简单。但是和大学时候的双链表程序还是有点差别的。现在我们逐一讲解。内核里面有个经典的宏[container_of],这个宏的意思是通过结构体中一个数据成员的位置偏移从而获得整个结构体的指针。其实也是很容易理解,这里主要是找到这个成员的偏移地址,然后成员的地址再去减去这个偏移地址,自然便得到指针的首地址。现在我们来看下这个宏。我的系统中linux这个宏的路径为/usr/src/kernels/2.6.9-42.EL-i686/include/linux/kernel.h.
可以看到此宏的定义如下:
#define container_of(ptr, type, member)({ \
const typeof( ((type *)0)->member )*__mptr = (ptr); \
(type *)( (char *)__mptr -offsetof(type,member) );})
然而这个又是什么意思呢? 首先我们看下系统本身是怎么注释的。
*@ptr: the pointer to the member. [指向数据成员的指针]
*@type: the type of the container structthis is embedded in.[数据类型]
*@member: the name of the member within the struct.[结构体数据成员]
我们来分析下这个宏,我们首先将0强制转化为type*类型,接下来获取结构体的数据成员[这里所做的都是为了获取数据的类型],在使用__mptr临时变量将数据进行保存,在获取临时变量的地址,最后使用offsetof获取偏移地址,使用临时变量的地址减去偏移地址,即可获得整个结构体的首地址。
0x1010 |
0x1014 |
0x1018 |
0x1026 |
假如上图是一个普通结构体,我们使用第三个成员变量,此时__mptr=0x1018 offsetof(**)=0x0008,这时将两个数据做差即可获得0x1010,即整个结构体的首地址。
现在我们举一个简单的例子。
#include<stdio.h>
#include<stddef.h>
#include<stdlib.h>
//#include<linux/kernel.h>
#definecontainer(ptr, type, member) ({ \
const typeof(((type*)0)->member)*__mptr =(ptr); \
(type*)((char*)__mptr - offsetof(type,member)); })
structTest
{
int number;
int age;
char name[10];
char sex;
char is_handsome;
};
intmain()
{
struct Test* test = (structTest*)malloc(sizeof(struct Test));
test->number = 123456;
test->age = 23;
strcpy(test->name,"Jack_Li");
test->is_handsome = 'Y';
printf("first get the struct address: %p\n", test);
printf("number=%d, age=%d, name=%s,is_handsome=%c \n", test->number, test->age, test->name,test->is_handsome);
int* pAge = NULL;
pAge = &test->age;
/*you will know to use 'container_of' */
/*here I can't use container_of, I guesscontainer_of is in kernel status,not user status'*/
//struct Test* pTest = container_of(pAge,struct Test, age);
struct Test* pTest = container(pAge, structTest, age);
printf("second get the struct address:%p \n", pTest);
printf("number=%d, age=%d, name=%s,is_handsome=%c \n", pTest->number, pTest->age, pTest->name,pTest->is_handsome);
free(test);
return 0;
}
这里我添加了linux/kernel.h 头文件是不能直接使用container_of宏的,[不知道为啥],后来直接将系统中的宏定义直接抽取出来,然后间接使用了offsetof宏,这里是可行的。
从上面的例子中,这里使用container_of宏是十分有意义的,正因为这个宏的存在,双链表数据结构才在内核中得到使用,不然一个数据结构都要维护一个双链表,是极其得不偿失的。
现在只需要将链表的结构体指针,直接存放在数据结构的变量定义中,操作链表也可以获得整个结构体的指针。链表将在第三部分讲解。
3.内核双链表: