日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

如下为利用Linux内核链表创建,Linux内核中链表的实现与应用

發布時間:2025/3/11 linux 44 豆豆
生活随笔 收集整理的這篇文章主要介紹了 如下为利用Linux内核链表创建,Linux内核中链表的实现与应用 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

鏈表(循環雙向鏈表)是Linux內核中最簡單、最常用的一種數據結構。

1、鏈表的定義

struct list_head {

struct list_head *next, *prev;

}

這個不含數據域的鏈表,可以嵌入到任何數據結構中,例如可按如下方式定義含有數據域的鏈表:

struct my_list {

void ?* mydata;

struct list_head ?list;

} ;

2、鏈表的聲明和初始化宏

struct list_head 只定義了鏈表結點,并沒有專門定義鏈表頭.那么一個鏈表結點是如何建立起來的?

內核代碼 list.h 中定義了兩個宏:

#defind ?LIST_HEAD_INIT(name) ? ?{ &(name), &(name) } ? ? ?//僅初始化

#defind ?LIST_HEAD(name) ? ? struct list_head ?name = LIST_HEAD_INIT(name) ?//聲明并初始化

如果要聲明并初始化鏈表頭mylist_head,則直接調用:LIST_HEAD(mylist_head),之后,

mylist_head的next、prev指針都初始化為指向自己。這樣,就有了一個帶頭結點的空鏈表。

判斷鏈表是否為空的函數:

static inline int list_empty(const struct list_head ?* head) {

return head->next ?== ?head;

} ? ?//返回1表示鏈表為空,0表示不空

3、在鏈表中增加一個結點

(內核代碼中,函數名前加兩個下劃線表示內部函數)

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 ;

}

list.h 中增加結點的兩個函數為:

(鏈表是循環的,可以將任何結點傳遞給head,調用這個內部函數以分別在鏈表頭和尾增加結點)

static inline void list_add(struct list_head *new, struct llist_head *head)

{

__list_add(new, head, head -> next) ;

}

static inline void list_add_tail(struct list_head 8new, struct list_head *head)

{

__list_add(new, head -> prev, head) ;

}

附:給head傳遞第一個結點,可以用來實現一個隊列,傳遞最后一個結點,可以實現一個棧。

static 加在函數前,表示這個函數是靜態函數,其實際上是對作用域的限制,指該函數作用域僅局限

于本文件。所以說,static 具有信息隱蔽的作用。而函數前加?inline?關鍵字的函數,叫內聯函數,表

示編譯程序在調用這個函數時,立即將該函數展開。

4、 遍歷鏈表

list.h 中定義了如下遍歷鏈表的宏:

#define ? list_for_each(pos, head) ? ?for(pos = (head)-> next ; ?pos != (head) ; ?pos = pos -> next)

這種遍歷僅僅是找到一個個結點的當前位置,那如何通過pos獲得起始結點的地址,從而可以引用結

點的域?list.h 中定義了 list_entry 宏:

#define ? list_entry( ptr, type, member ) ?\

( (type *) ( (char *) (ptr) ?- (unsigned long) ( &( (type *)0 ) ?-> ?member ) ) )

分析:(unsigned long) ( &( (type *)0 ) ?-> ?member ) 把 0 地址轉化為 type 結構的指針,然后獲取該

結構中 member 域的指針,也就是獲得了 member 在type 結構中的偏移量。其中??(char *) (ptr) 求

出的是 ptr?的絕對地址,二者相減,于是得到 type 類型結構體的起始地址,即起始結點的地址。

5、鏈表的應用

一個用以創建、增加、刪除和遍歷一個雙向鏈表的Linux內核模塊

點擊(此處)折疊或打開

#include

#include

#include

#include

MODULE_LICENCE("GPL");

MODULE_AUTHOR("LUOTAIJIA");

#define N 10

struct numlist {

int num;

struct list_head list;

};

struct numlist numhead;

static int __init doublelist_init(void)

{

//初始化頭結點

struct numlist * listnode; //每次申請鏈表結點時所用的指針

struct list_head * pos;

struct numlist * p;

int i;

printk("doublelist is starting...\n");

INIT_LIST_HEAD(&numhead.list);

/*

* static inline void INIT_LIST_HEAD(struct list_head *list)

* {

* list->next = list;

* list->prev = list;

* }

*/

//建立N個結點,依次加入到鏈表當中

for (i=0; i

listnode = (struct numlist *)kmalloc(sizeof(struct numlist), GFP_KERNEL);

//void *kmalloc(size_t size, int flages)

//分配內存,size 要分配內存大小,flags 內存類型

listnode->num = i+1;

list_add_tail(&listnode->list, &numhead.list);

printk("Node %d has added to the doublelist...\n", i+1);

}

//遍歷鏈表

i = 1;

list_for_each(pos, &numhead.list) {

p = list_entry(pos, struct numlist, list);

printk("Node %d's data: %d\n", i, p->num);

i++;

}

return 0;

}

static void __exit doublelist_exit(void)

{

struct list_head *pos, *n;

struct numlist *p;

int i;

//依次刪除N個結點

i = 1;

list_for_each_safe(pos, n, &numhead.list) {

//為了安全刪除結點而進行的遍歷

list_del(pos); //從鏈表中刪除當前結點

p = list_entry(pos, struct numlist, llist);

//得到當前數據結點的首地址,即指針

kfree(p); //釋放該數據結點所占空間

printk("Node %d has removed from the doublelist...\n", i++);

}

printk("doublelist is exiting...\n");

}

module_init(doublelist_init);

module_exit(doublelist_exit);

參考資料:Linux操作系統原理與應用(第2版) ? ?陳莉君、康華 編著

總結

以上是生活随笔為你收集整理的如下为利用Linux内核链表创建,Linux内核中链表的实现与应用的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。