zoukankan      html  css  js  c++  java
  • 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("
    ");
    
    
        /* 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()
    ");
        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
    ", tmp->to, tmp->from);
    
        }
        printf("
    ");
        /* 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()
    ");
        list_for_each_entry(tmp, &mylist.list, list)
             printf("to= %d from= %d
    ", tmp->to, tmp->from);
        printf("
    ");
        
    
        /* 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()
    ");
        list_for_each_safe(pos, q, &mylist.list){
             tmp= list_entry(pos, struct kool_list, list);
             printf("freeing item to= %d from= %d
    ", 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
    ", 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
    ", 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
    ", tmp->to, tmp->from);
        list_for_each(pos, &mylist.list){
             tmp= list_entry(pos, struct kool_list, list);
             printf("to= %d from= %d
    ", 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))

    同样, 这个可以安全的删除元素.

  • 相关阅读:
    iOS单选和全选
    仿微信-ActionSheet
    NSArray 快速求和、平均值、最大值、最小值
    iOS学习资源集合
    iOS-Runtime字体适配
    仿网易新闻标题栏
    极光推送封装
    iOS导航栏自由缩放头像效果
    iOS判断字母、数字串
    Perl6多线程3: Promise start / in / await
  • 原文地址:https://www.cnblogs.com/hwy89289709/p/6754300.html
Copyright © 2011-2022 走看看