如下为利用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内核中链表的实现与应用的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python 根据名称获取pid_【Py
- 下一篇: linux 其他常用命令