Linux kernel中的list怎么使用
需要使用链表的时候, 就想到直接使用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))
同样, 这个可以安全的删除元素.