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))

同樣, 這個可以安全的刪除元素.

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM