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.內核雙鏈表: