需要使用鏈表的時候, 就想到直接使用Linux kernel 里面的 list 了. 於是找到一片文章, 相當於翻譯這篇文章. 學習怎么用的筆記
原文地址: http://isis.poly.edu/kulesh/stuff/src/klist/
原文日期: This is a working copy, last updated on April 5th, 2005. Feel free to email your comments.
我看完之后,總結這些函數 的用處,以后方便的立刻使用, 像知道為什么, 就往下看哦
struct list_head : 鏈表結構體的指針的數據結構
LIST_HEAD_INIT: 用於初始化這個頭部編成獨立結點
INIT_LIST_HEAD(ptr) : 將這個指針初始化為孤立的結點
LIST_HEAD(name) : 初始化一個變量為name, 並且初始化
list_add(struct list_head *new, struct list_head *head): 將新元素, 放到了鏈表的頭端
void list_add_tail(struct list_head *new, struct list_head *head): 添加一個到尾部
list_del(struct list_head *entry): 刪除這個結點, 沒有切斷聯系
list_del_init(struct list_head *entry) : 刪除這個元素並且初始化
list_move(struct list_head *list, struct list_head *head): 將第一個鏈表的頭刪, 然后連接到第二個鏈表.
list_move_tail(struct list_head *list, struct list_head *head): 將第一個鏈表移動到, 到第二個鏈表后面
list_empty(struct list_head *head): 判斷這個鏈表是否為空
list_splice(struct list_head *list, struct list_head *head): 將第一個鏈表接到第二個鏈表的開頭.
list_splice_init(struct list_head *list, struct list_head *head): 在上面的功能之上, 再將第一個頭結點初始化
list_entry(ptr, type, member): 獲得這個指針的所再的項的開頭
list_for_each(pos, head): 從前到后遍歷鏈表
list_for_each_prev(pos, head): 從后到前遍歷鏈表
list_for_each_safe(pos, n, head): 刪除當前結點不會造成斷鏈
list_for_each_entry(pos, head, member): 這個可以自己少寫一句list_entry
list_for_each_entry_safe(pos, n, head, member): 遍歷可以安全刪除
簡介:
linux內核大部分都是C語言寫的. 不像其他語言, C語言沒有一個很好的內置的數據結構. 也沒有一些標准庫能夠支持. 因此, 能夠借用內核里面的一個循環鏈表是十分讓人興奮的.
源文件是在 include/linux/list.h. 這是個與類型無關, 容易實用, 循環的鏈表, 使用C語言寫成. 這個實現能夠有效率,並且可移植. 在下面的使用當中, 作者修改了部分, 刪除了有關硬件的細節. 所以也可以使用這個東西在我們的程序. 文中提供了下在文件.
使用這個列表, 有以下的優點:
類型無關, 可移植, 容易使用, 可讀, 節約時間.
如果要創建一個list, 我們可以這樣定義數據結構
struct my_cool_list{ struct list_head list; /* kernel's list structure */ int my_cool_data; void* my_cool_void; };
注意:
鏈表是在你的數據項里面的一個成員.
你可以將struct list_head 這個結構體放在任何地方
你可以將struct list_head 變量命名為任意的名字
你可以有多個list
比如說下面這個例子:
struct todo_tasks{ char *task_name; unsigned int name_len; short int status; int sub_tasks; int subtasks_completed; struct list_head completed_subtasks;/* list structure */ int subtasks_waiting; struct list_head waiting_subtasks; /* another list of same or different items! */ struct list_head todo_list; /* list of todo_tasks */ };
在linux kernel 里面有很多使用這個的例子:
比如說 struct inode 和 struct block_device.
list_head的定義是
struct list_head{ struct list_head *next; struct list_head *prev; }
如何使用這個list
正如下面這段代碼所顯示的:
#include <stdio.h> #include <stdlib.h> #include "list.h" struct kool_list{ int to; struct list_head list; int from; }; int main(int argc, char **argv){ struct kool_list *tmp; struct list_head *pos, *q; unsigned int i; struct kool_list mylist; INIT_LIST_HEAD(&mylist.list); /* or you could have declared this with the following macro * LIST_HEAD(mylist); which declares and initializes the list */ /* adding elements to mylist */ for(i=5; i!=0; --i){ tmp= (struct kool_list *)malloc(sizeof(struct kool_list)); /* INIT_LIST_HEAD(&tmp->list); * * this initializes a dynamically allocated list_head. we * you can omit this if subsequent call is add_list() or * anything along that line because the next, prev * fields get initialized in those functions. */ printf("enter to and from:"); scanf("%d %d", &tmp->to, &tmp->from); /* add the new item 'tmp' to the list of items in mylist */ list_add(&(tmp->list), &(mylist.list)); /* you can also use list_add_tail() which adds new items to * the tail end of the list */ } printf("\n"); /* now you have a circularly linked list of items of type struct kool_list. * now let us go through the items and print them out */ /* list_for_each() is a macro for a for loop. * first parameter is used as the counter in for loop. in other words, inside the * loop it points to the current item's list_head. * second parameter is the pointer to the list. it is not manipulated by the macro. */ printf("traversing the list using list_for_each()\n"); list_for_each(pos, &mylist.list){ /* at this point: pos->next points to the next item's 'list' variable and * pos->prev points to the previous item's 'list' variable. Here item is * of type struct kool_list. But we need to access the item itself not the * variable 'list' in the item! macro list_entry() does just that. See "How * does this work?" below for an explanation of how this is done. */ tmp= list_entry(pos, struct kool_list, list); /* given a pointer to struct list_head, type of data structure it is part of, * and it's name (struct list_head's name in the data structure) it returns a * pointer to the data structure in which the pointer is part of. * For example, in the above line list_entry() will return a pointer to the * struct kool_list item it is embedded in! */ printf("to= %d from= %d\n", tmp->to, tmp->from); } printf("\n"); /* since this is a circularly linked list. you can traverse the list in reverse order * as well. all you need to do is replace 'list_for_each' with 'list_for_each_prev' * everything else remain the same! * * Also you can traverse the list using list_for_each_entry() to iterate over a given * type of entries. For example: */ printf("traversing the list using list_for_each_entry()\n"); list_for_each_entry(tmp, &mylist.list, list) printf("to= %d from= %d\n", tmp->to, tmp->from); printf("\n"); /* now let's be good and free the kool_list items. since we will be removing items * off the list using list_del() we need to use a safer version of the list_for_each() * macro aptly named list_for_each_safe(). Note that you MUST use this macro if the loop * involves deletions of items (or moving items from one list to another). */ printf("deleting the list using list_for_each_safe()\n"); list_for_each_safe(pos, q, &mylist.list){ tmp= list_entry(pos, struct kool_list, list); printf("freeing item to= %d from= %d\n", tmp->to, tmp->from); list_del(pos); free(tmp); } return 0; }
這個文件, 能夠使用的功能是創建, 增加, 刪除和遍歷這個list. 首先瀏覽下源代碼:
struct list_head { struct list_head *next, *prev; };
定義一個有前一個和后一個的鏈表的指針.
#define LIST_HEAD_INIT(name) { &(name), &(name) }
初始化這個鏈表指向自己. 在應用的時候, 定義完自己的 struct kool_list mylist, 然后可以使用INIT_LIST_HEAD(&mylist.list) 來進行這個的連邊的初始化. 但我看代碼,我覺得也可以利用 LIST_HEAD(mylist)
#define LIST_HEAD(name) \ struct list_head name = LIST_HEAD_INIT(name)
這個宏, 定義了一個名為name的變量, 並且初始化它位鏈表
#define INIT_LIST_HEAD(ptr) do { \ (ptr)->next = (ptr); (ptr)->prev = (ptr); \ } while (0)
這個宏是將next設置為ptr,然后將ptr的宏的前一個設置位ptr. 將head設置為頭結點, 也就是將ptr設置位頭.
/* * Insert a new entry between two known consecutive entries. * * This is only for internal list manipulation where we know * the prev/next entries already! */ 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_add - add a new entry * @new: new entry to be added * @head: list head to add it after * * Insert a new entry after the specified head. * This is good for implementing stacks. */ static inline void list_add(struct list_head *new, struct list_head *head) { __list_add(new, head, head->next); }
list_add() 這個就是將新添加的鏈表放在new這里. 要注意的是, new必須是list_head的結構, head是頭部, head->next就是下一個, 那么這個函數的用法就是, 將新元素, 放到了鏈表的頭端.
/** * list_add_tail - add a new entry * @new: new entry to be added * @head: list head to add it before * * Insert a new entry before the specified head. * This is useful for implementing queues. */ static inline void list_add_tail(struct list_head *new, struct list_head *head) { __list_add(new, head->prev, head); }
添加到尾部, 就是使用head->prev, 然后head作為下一個.
/* * Delete a list entry by making the prev/next entries * point to each other. * * This is only for internal list manipulation where we know * the prev/next entries already! */ static inline void __list_del(struct list_head *prev, struct list_head *next) { next->prev = prev; prev->next = next; }
刪除一個鏈表的屍體, 只要把前和后相連接起來就好了. 這里也是只用於知道內部的結點的時候才能用
/** * list_del - deletes entry from list. * @entry: the element to delete from the list. * Note: list_empty on entry does not return true after this, the entry is in an undefined state. */ static inline void list_del(struct list_head *entry) { __list_del(entry->prev, entry->next); entry->next = (void *) 0; entry->prev = (void *) 0; }
直接刪除這個結點, 沒有將聯系斬斷
/** * list_del_init - deletes entry from list and reinitialize it. * @entry: the element to delete from the list. */ static inline void list_del_init(struct list_head *entry) { __list_del(entry->prev, entry->next); INIT_LIST_HEAD(entry); }
將這個結點從鏈表中刪除, 然后將這個結點初始化為自己, 也就是將這個結點初始化為孤立結點.
/** * list_move - delete from one list and add as another's head * @list: the entry to move * @head: the head that will precede our entry */ static inline void list_move(struct list_head *list, struct list_head *head) { __list_del(list->prev, list->next); list_add(list, head); }
將一個結點刪除, 然后添加它添加到另外一個鏈表的頭部
/** * list_move_tail - delete from one list and add as another's tail * @list: the entry to move * @head: the head that will follow our entry */ static inline void list_move_tail(struct list_head *list, struct list_head *head) { __list_del(list->prev, list->next); list_add_tail(list, head); }
將一個結點刪除, 然后將它添加到另外一個鏈表的尾部
/** * list_empty - tests whether a list is empty * @head: the list to test. */ static inline int list_empty(struct list_head *head) { return head->next == head; }
測試這個鏈表是否是空的, 只要判斷頭部是否指向自己
static inline void __list_splice(struct list_head *list, struct list_head *head) { struct list_head *first = list->next; struct list_head *last = list->prev; struct list_head *at = head->next; first->prev = head; head->next = first; last->next = at; at->prev = last; }
將兩個循環鏈表連接起來, 做法就是找到第一個鏈表, 不要頭,將第一個鏈表的頭尾, 頭連接到第二個頭的后面, 然后將第一個鏈表的尾巴, 接到原來第二個第二個元素的前面..
/** * list_splice - join two lists * @list: the new list to add. * @head: the place to add it in the first list. */ static inline void list_splice(struct list_head *list, struct list_head *head) { if (!list_empty(list)) __list_splice(list, head); }
這個就是在上面的函數的基礎上, 先判斷第一個list是否為空, 然后再進行連接.
/** * list_splice_init - join two lists and reinitialise the emptied list. * @list: the new list to add. * @head: the place to add it in the first list. * * The list at @list is reinitialised */ static inline void list_splice_init(struct list_head *list, struct list_head *head) { if (!list_empty(list)) { __list_splice(list, head); INIT_LIST_HEAD(list); } }
這個是在上面的基礎上, 將第一個鏈表的頭進行了初始化為一個孤立的點.
/** * list_entry - get the struct for this entry * @ptr: the &struct list_head pointer. * @type: the type of the struct this is embedded in. * @member: the name of the list_struct within the struct. */ #define list_entry(ptr, type, member) \ ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
這個的作用就是從ptr所指的type的類型,尋找member的這個變量.稍微講解下這個的用法
如果調用是
list_entry(pos, struct kool_list, list); #define list_entry(ptr, type, member) \ ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member))) 那么這個宏,就會擴展為 ((struct kool_list *)((char *)(pos) - (unsigned long)(&((struct kool_list *)0)->list))) 這樣就可以計算出那個想要的成員的開始的地址, 最后轉換,就成了這個的入口了.
/** * list_for_each - iterate over a list * @pos: the &struct list_head to use as a loop counter. * @head: the head for your list. */ #define list_for_each(pos, head) \ for (pos = (head)->next; pos != (head); \ pos = pos->next)
這個宏, 的作用就是從head開始的第一個函數開始, 遍歷這個鏈表, 用for語句
用例:
list_for_each(pos, &mylist.list){ /* at this point: pos->next points to the next item's 'list' variable and * pos->prev points to the previous item's 'list' variable. Here item is * of type struct kool_list. But we need to access the item itself not the * variable 'list' in the item! macro list_entry() does just that. See "How * does this work?" below for an explanation of how this is done. */ tmp= list_entry(pos, struct kool_list, list); /* given a pointer to struct list_head, type of data structure it is part of, * and it's name (struct list_head's name in the data structure) it returns a * pointer to the data structure in which the pointer is part of. * For example, in the above line list_entry() will return a pointer to the * struct kool_list item it is embedded in! */ printf("to= %d from= %d\n", tmp->to, tmp->from); }
這個用於遍歷, 對於刪除元素並不安全,刪除了可能會斷鏈
/** * list_for_each_prev - iterate over a list backwards * @pos: the &struct list_head to use as a loop counter. * @head: the head for your list. */ #define list_for_each_prev(pos, head) \ for (pos = (head)->prev; pos != (head); \ pos = pos->prev)
反向遍歷這個這個鏈表
/** * list_for_each_safe - iterate over a list safe against removal of list entry * @pos: the &struct list_head to use as a loop counter. * @n: another &struct list_head to use as temporary storage * @head: the head for your list. */ #define list_for_each_safe(pos, n, head) \ for (pos = (head)->next, n = pos->next; pos != (head); \ pos = n, n = pos->next)
使用的例子是:
list_for_each_safe(pos, q, &mylist.list){ tmp= list_entry(pos, struct kool_list, list); printf("freeing item to= %d from= %d\n", tmp->to, tmp->from); list_del(pos); free(tmp); }
這個版本的用法就是, 你能夠在循環結構里面安全的刪除所遍歷的元素
/** * list_for_each_entry - iterate over list of given type * @pos: the type * to use as a loop counter. * @head: the head for your list. * @member: the name of the list_struct within the struct. */ #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))
遍歷鏈表, 尋找鏈表中的某個成員. 可以讓我們根據某個鏈表來進行遍歷.
list_for_each_entry(tmp
list_for_each_entry(tmp, &mylist.list, list) printf("to= %d from= %d\n", tmp->to, tmp->from);
list_for_each(pos, &mylist.list){ tmp= list_entry(pos, struct kool_list, list); printf("to= %d from= %d\n", tmp->to, tmp->from); }
相比list_for_each, 簡化了里面自己需要i寫的語句.
/** * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry * @pos: the type * to use as a loop counter. * @n: another type * to use as temporary storage * @head: the head for your list. * @member: the name of the list_struct within the struct. */ #define list_for_each_entry_safe(pos, n, head, member) \ for (pos = list_entry((head)->next, typeof(*pos), member), \ n = list_entry(pos->member.next, typeof(*pos), member); \ &pos->member != (head); \ pos = n, n = list_entry(n->member.next, typeof(*n), member))
同樣, 這個可以安全的刪除元素.