Linux核心第八記

來源:互聯網
上載者:User

在Linux核心中使用了大量的鏈表結構來組織資料結構。這些鏈表大多數採用了[include/linux/list.h]中實現的一套精彩的鏈表資料結構。

預備知識:

#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)

其中(TYPE *)0表示將常量0強制轉化為TYPE*類型指標所指向的地址,&((TYPE *)0)->MEMBER表示取(TYPE*)0指標的成員MEMBER的地址,因為TYPE類型的結構的首地址是0,所以MEMBER成員的地址就是相對首地址的位移量,這裡的size_t為unsigned
int類型,主要是便於在不同系統之間的移植。

所以,該宏的含義是求一個TYPE類型中MEMBER成員相對該類型結構首地址的位移量。

#define container_of(ptr, type, member) ({                      \
        const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
        (type *)( (char *)__mptr - offsetof(type,member) );})

其中ptr是指向member的指標,type是一個結構類型,member是結構中的成員,typeof的用法,typeof(int*)p <==> int* p;這裡typeof( ((type *)0)->member ) *__mptr 相當於一個member類型的指標,但是這個指標被特殊處理過了,就是把type類型的首地址設定成0,const
typeof( ((type *)0)->member ) *__mptr = (ptr);這裡實際上就是把member指標賦值。(char *)__mptr - offsetof(type,member) ,首先,為什麼要把__mptr的地址即member的地址,轉換成char*型的,便於按位元組計算。member的地址減去其在結構中的位移量,就得到結構的首地址。

#include <stdio.h>#define FIND(TYPE,MEMBER) (size_t)&(((TYPE*)0)->MEMBER)#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)#define container_of(ptr, type, member) ({                      \        const typeof( ((type *)0)->member ) *__mptr = (ptr);    \        (type *)( (char *)__mptr - offsetof(type,member) );})struct list_head {    struct list_head *next, *prev;};struct student{    int a;    char b[19];    double c;    struct list_head list;};int main(void){    struct student s= {1,{0},0.0,{NULL,NULL}};    struct list_head * ptr = &s.list;    printf("%08x\n",s);    printf("addr of s : %08x\n",&s);    printf("addr of s.a : %08x\n",&s.a);    printf("addr of s.b : %08x\n",&s.b);    printf("addr of s.c : %08x\n",&s.c);    printf("addr of ptr : %08x\n",ptr);    printf("offset of list: %08x\n",FIND(struct student,list));    struct student *stu  = container_of(ptr,struct student,list);//這一句上有問題。    printf("addr of stu : %08x\n",stu);    return 0;}

結果:

hubimaso@ubuntu:~/Linux$ ./container_of 00000001addr of s : bfe5d424addr of s.a : bfe5d424addr of s.b : bfe5d428addr of s.c : bfe5d43caddr of ptr : bfe5d444offset of list: 00000020addr of stu : bfe5d424

#define LIST_POISON1  ((void *) 0x00100100)
#define LIST_POISON2  ((void *) 0x00200200)

第一句話是將0x00100100十六進位數強制轉換成void*型指標。地址是0x00100100 

第二句話同理。(貌似這兩個地址沒人用)用來給不用的指標賦值


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

就是讓ptr指向的結構回到最原始狀態。如下面的第二個圖。


#define LIST_HEAD_INIT(name) { &(name), &(name) }

#define LIST_HEAD(name) \
struct list_head name = LIST_HEAD_INIT(name)

跟上面的功能是一樣的。初始化結構。


1.鏈表資料的結構的定義:

struct list_head

{

struct list_head *next,*prev;

}

list_head結構包含兩個指向list_head結構的指標prev和next,由此可見,核心的鏈表具備雙鏈表的功能,實際上,通常它都組織成雙向迴圈鏈表。(注意這個結構體沒有資料部分,也正是這樣定義體現了他的獨到之處)。


第一個圖不多說,第二個圖:linux核心鏈表的指標域不再是象第一個圖那樣,僅僅是指向下一結構體的指標(該結構體包含了資料域),而是這個linux的指標域內容是list_head類型的結構體,而該結構體分別有兩個指標指向上一個list_head的結構體和下一個list_head的結構體。

為什麼這樣定義呢?

傳統類型的指標是包括資料域的結構類型,如果該結構變化,則指標的類型也在變。如果下面的方式,這樣的指標都是list_head類型的,不會隨著結構體類型變化而變化。

那怎麼樣取到資料呢?


2.鏈表操作(*重點)

Linux核心中提供的鏈表操作主要有:

1)初始化鏈表頭

INIT_LIST_HEAD(list_head *head)

原函數:

static inline void INIT_LIST_HEAD(struct list_head *list)
{
list->next = list;
list->prev = list;
}


2)插入節點

list_add(struct list_head *new, struct list_head *head)

原函數:

static inline void list_add(struct list_head *new, struct list_head *head)
{
__list_add(new, head, head->next);
}

static inline void __list_add(struct list_head *new,
     struct list_head *prev,
     struct list_head *next)
{
next->prev = new;//head->next->prev,圖中的1即第1步
new->next = next;//圖中的1即第2步
new->prev = prev;//圖中的1即第3步
prev->next = new;//head->next,//圖中的1即第4步,包括打黑X的部分
}


(少畫了一個實線指標,即head的下一個節點的prev是指向head的,head的prev指標指向尾節點。有些地方不好畫出來,需要理解)

list_add_tail(struct list_head *new, struct list_head *head)
原型:
static inline void list_add_tail(struct list_head *new, struct list_head *head)
{
__list_add(new, head->prev, head);
}

3)刪除節點

list_del(struct list_head *entry)

原型:static inline void list_del(struct list_head *entry)
{
__list_del(entry->prev, entry->next);
entry->next = LIST_POISON1;
entry->prev = LIST_POISON2;
}

而:

static inline void __list_del(struct list_head * prev, struct list_head * next)//這個函數很經典,看上去是將兩個節點串連起來。
{
next->prev = prev;
prev->next = next;
}

4)static inline void list_del_init(struct list_head *entry)
{
__list_del(entry->prev, entry->next);
INIT_LIST_HEAD(entry);//與上面唯一的不同就是將刪除的節點,該節點初始化了。
}

5)#define list_entry(ptr, type, member) \
container_of(ptr, type, member)

不用說了。

6)#define list_for_each_entry(pos, head, member)
\
for (pos = list_entry((head)->next, typeof(*pos), member);
\
    &pos->member != (head);
\
    pos = list_entry(pos->member.next, typeof(*pos), member))

遍曆鏈表。pos是封裝的結構指標,head是原始的結構指標,member原始結構的名。這裡說的封裝結構就相當要上述例子中的student結構體,原始結構相當於list,member相當於原始結構體的名字list。

關鍵是&pos->member ! = (head);這句話,當相等時說明以及遍曆完了,這時的member就是尾節點,member的next就是head;

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.