Linux 内核链表 list.h 的使用
C 语言本身并不自带集合(Collection)工具,当我们需要把结构体(struct)实例串联起来时,就需要在结构体内声明指向下一实例的指针,构成所谓的“链表”。而为了实现对链表的操作,我们需要另外实现一系列的函数,例如添加、删除、搜索、复制等等。而利用 Kernel 源代码中自带的 list.h,则可以方便地实现任意类型结构体的串联。
编程需求
假设我有一个表示学生资料的结构体:
#define MAX_STRING_LENGTH 50 typedef struct student { char first_name[MAX_STRING_LENGTH]; char last_name[MAX_STRING_LENGTH]; unsigned int age; } student_t;
传统的做法,当我们需要将一系列学生的数据串联起来,那我们需要在该结构体内部添加一枚指针:
typedef struct student { char first_name[MAX_STRING_LENGTH]; char last_name[MAX_STRING_LENGTH]; unsigned int age; struct student *next; /* Look at dis ;p */ } student_t;
几乎每位 C 语言学习者都有在教科书中见过此类实现方法。但是这样做的缺点是:我们需要额外编写一系列函数,实现对该类型链表的操作。然而,稍微复杂点的项目内,有个十几个结构体那是很平常的事情。如果我们想为它们都实现链表功能,那我们就需要为每个类型的结构体编写一套函数,然后我们就累 shi 了。
有没有一种更为通用的办法呢?不需要顾及结构体本身的类型,就可以简简单单地把它们串起来。有点类似 Java 中的 LinkedList,如果我需要把一堆 Student 类的对象管理起来,那就很简单:
LinkedList<Student> lstStudents = new LinkedList<Student>();
完事儿!接着直接把对象往 lstStudents 里面放就可以了。
list.h 简介
Linux 内核的源代码中有一个工具头文件 list.h,里面定义了一系列的宏(macro),帮助我们实现对任意类型结构体的串联。然而原始版本的 list.h 是供内核使用的,并不能直接被使用在用户空间(user space)的程序内。好在有网友对其进行了改写,使得其可以被使用在一般应用程序内。因为博客系统的限制,我无法将源代码一并贴上,大家可以自行下载:list.h 。较之 Github 上最新版的 list.h,该修改版提供的宏不是很全面,你可以根据自己的需要,从 Github 摘取、添加新宏。
list.h 的使用
为了利用 list.h 实现链表功能,首先我们需要对我们的结构体做一点小小的改动:
#include "list.h" typedef struct student { char first_name[MAX_STRING_LENGTH]; char last_name[MAX_STRING_LENGTH]; unsigned int age; struct list_head node_student; } student_t;
首先毫无悬念的,我们需要将 list.h 包含到我们的程序,接着在结构体内,我们添加一个类型为 struct list_head
的字段node_student
。这个字段就像是链条上的环扣,一环一环地将所有实例串联起来。接下去我们需要一个函数,来创建该结构体的实例:
student_t *make_student(const char *fn, const char *ln, unsigned int a) { student_t *stu = NULL; if ((stu = calloc(1, sizeof(struct student))) == NULL) { return NULL; /* Failed to allocate memory. */ } if (strlen(fn) > (MAX_STRING_LENGTH - 1)) { return NULL; /* First name too long. */ } if (strlen(ln) > (MAX_STRING_LENGTH - 1)) { return NULL; /* Last name too long. */ } strcpy(stu->first_name, fn); strcpy(stu->last_name, ln); stu->age = a; return stu; }
函数代码比较简单,就不费口舌解释了。
添加元素
现在在我们的 main 函数内,我们将创建一系列 student_t 结构体的实例,并把它们串联起来:
struct list_head class; INIT_LIST_HEAD(&class); student_t *stu = NULL; /* Create students. */ if ((stu = make_student("Pierre", "Dupont", 16)) == NULL) { fprintf(stderr, "Failed to create Pierre.\n"); return -1; } list_add_tail(&stu->node_student, &class); if ((stu = make_student("Hugot", "Badeaux", 18)) == NULL) { fprintf(stderr, "Failed to create Hugot.\n"); return -1; } list_add_tail(&stu->node_student, &class); if ((stu = make_student("Celine", "Brousseau", 17)) == NULL) { fprintf(stderr, "Failed to create Celine.\n"); return -1; } list_add_tail(&stu->node_student, &class);
这里我们首先创建一个链表 class,并用宏 INIT_LIST_HEAD 对其进行初始化。紧接着我们创建三名学生,分别叫:Pierre.Dupont,Hugot.Badeaux 和 Celine.Brousseau。每创建一名学生,我们就用宏 list_add_tail,将其添加到链表 class 内。list_add_tail 取两个参数,首先是结构体内用于串联实例的“环扣”node_student 的指针,其次是链表本身的指针,即 class。
链表的遍历
完成对链表的初始化之后,我们试着打印出链表内所有的学生信息:
/* Print all students in class. */ printf("All the students in class.\n"); list_for_each_entry(stu, &class, node_student) { printf("First name: %s\n", stu->first_name); printf("Last name: %s\n", stu->last_name); printf("Age: %u\n\n", stu->age); }
对链表的遍历是通过宏 list_for_each_entry
来实现的,它取三个参数,分别是:一个结构体类型的指针,链表的指针,以及结构内“环扣”的名称,即 node_student
。程序编译时,该宏会被替换成一个 for 循环,遍历链表内所有的元素,并且打印在终端上。
导出链表
现在学校组织一次春游,为此我们利用宏 LIST_HEAD
添加了另一个新的链表 bus。使用 LIST_HEAD
来创建空链表相对简单,不需要先声明,再用 INIT_LIST_HEAD
进行初始化。现在,我们需要把班级上所有的学生都移到公车 bus 内:
LIST_HEAD(bus); /* Moving all students into bus. */ printf("Moving all the students into the bus.\n"); list_splice_init(&class, &bus); if (list_empty(&class)) { printf("No more student in class.\n"); } printf("\n"); /* Print all student in bus. */ list_for_each_entry(stu, &bus, node_student) { printf("%s %s (%u) is in the bus.\n", stu->first_name, stu->last_name, stu->age); } printf("\n");
宏 list_splice_init
取两个参数,分别为原链表指针和新链表指针。使用该宏之后,class 链表内的所有元素就会被导出到 bus 链表内。接着我们再用宏 list_empty
测试链表 class 是否为空。顺利完成对学生的移动之后,我们再一次使用 list_for_each_entry
,逐一打印出新链表 bus 内的所有学生。不出意外的话,我们会发现 class 链表已为空,且所有学生都被成功转移到了新链表 bus 内。
删除元素
新的麻烦来了,在校车即将出发之际,妹子 Celine 突然决定不去了,要回家。那我们只能将 Celine 从校车中移出,然后再重新清点学生人数。
student_t *tmp = NULL; /* Celine wanna go home. */ printf("Celine wants to go home, remove her.\n"); list_for_each_entry_safe(stu, tmp, &bus, node_student) { if (strcmp(stu->first_name, "Celine") == 0) { list_del(&stu->node_student); free(stu); } } printf("\n"); /* Print remaining students in bus. */ printf("Remaining students in bus.\n"); list_for_each_entry(stu, &bus, node_student) { printf("%s %s (%u) is in the bus.\n", stu->first_name, stu->last_name, stu->age); } printf("\n");
我们首先用宏 list_for_each_entry_safe
遍历链表 bus 内的所有学生,当我们找到 Celine 的时候,则调用宏 list_del
将其从链表内删除。list_del
只取一个参数,即目标元素内“环扣”的指针。此外 list_for_each_entry
和 list_for_each_entry_safe
的唯一区别在于,list_for_each_entry_safe
可以在遍历的过程当中删除链表内的元素,且不会造成错误。较之 list_for_each_entry
,list_for_each_entry_safe
需多取一个参数 tmp,其同样为结构体类型的指针,用于在遍历过程,临时保存链表元素。在删除 Celine 之后,我们发现 Pierre 和 Hugot 还老老实实地坐在校车内。
程序的最后,校车成功抵达目的地,我们将所有剩余的学生(Pierre 和 Hugot)从校车内移出,并释放系统资源:
/* End of demo. */ printf("End of demo, free all resources.\n"); list_for_each_entry_safe(stu, tmp, &bus, node_student) { printf("Removing student: %s %s\n", stu->first_name, stu->last_name); list_del(&stu->node_student); free(stu); } return 0;
编译完成之后,试着运行程序,终端输出为:
All the students in class. First name: Pierre Last name: Dupont Age: 16 First name: Hugot Last name: Badeaux Age: 18 First name: Celine Last name: Brousseau Age: 17 Moving all the students into the bus. No more student in class. Pierre Dupont (16) is in the bus. Hugot Badeaux (18) is in the bus. Celine Brousseau (17) is in the bus. Celine wants to go home, remove her. Remaining students in bus. Pierre Dupont (16) is in the bus. Hugot Badeaux (18) is in the bus. End of demo, free all resources. Removing student: Pierre Dupont Removing student: Hugot Badeaux
一切正如我们预期的那样,我们实现了对链表的添加、遍历、导出和删除。
注意事项
最后,使用 list.h 时需要注意代码的可读性,先看看下面这个例子:
typedef struct school { struct list_head list_students; struct list_head list_teachers; struct list_head list_guards; struct list_head list_classrooms; struct list_head list_bus; } school_t;
这个结构体的本意很明显,就是想包含一所学校内所有的人员和器材等等。但是单凭这样一个结构体,我们无法知道每一个链表内元素的具体类型。尤其是当你把一个项目丢给新接手的开发人员时,他看到这样一个结构体,会比较头疼。而且很多项目内,结构体的名字并非都那么浅显易懂。
当他想使用宏 list_for_each_entry
或者 list_for_each_entry_safe
遍历链表时,如果没有具体注解,他甚至不知道该用什么结构体类型。所以,一定要有详细的注解:
typedef struct school { struct list_head list_students; /* struct student */ struct list_head list_teachers; /* struct teacher */ struct list_head list_guards; /* struct guard */ struct list_head list_classrooms; /* struct classroom */ struct list_head list_bus; /* struct bus */ } s
综上所述,我个人的意见:list.h 很好用,但是要慎用。
简单总结
typedef struct student{ char first_name[MAX_STRING_LENTH]; char last_name[MAX_STRING_LENTGH]; unsigned int age; struct list_head student_node; } student_t; //定义链表方法1: class student_t *class = NULL; struct list_head class; INIT_LIST_HEAD(&class); //定义链表方法2: bus student_t *bus = NULL; LIST_HEAD(bus);
原因如下:
#define LIST_HEAD_INIT(name) { &(name), &(name) } #define LIST_HEAD(name) struct list_head name = LIST_HEAD_INIT(name)
REF
MISC
Q1
当然,如果你想在用户态使用list.h 可能不行, 用户态只能使用/usr/include/ 下的lib
所以,如果你想在用户态使用list.h ,需要保证有 /usr/include/linux/list.h
#ll /usr/include/linux/list.h ls: cannot access /usr/include/linux/list.h: No such file or directory
如果你find到了, 你可以这样:
cp -r /usr/include/linux{,.bak} #ln -sf /usr/src/kernels/3.10.0-327.xxxxxx.x86_64/include/linux /usr/include/linux
但是,这不是一种科学的方法, 因为你还会遇到很多依赖:
#gcc -o stu stu.c -D__KERNEL__ -DMODULE -O -Wall In file included from /usr/include/linux/list.h:4:0, from stu.c:1: /usr/include/linux/types.h:5:30: fatal error: uapi/linux/types.h: No such file or directory #include <uapi/linux/types.h> ^ compilation terminated.
最终办法,在自己的project里放一个list.h : http://www.cnblogs.com/muahao/p/8109733.html
Q2
#cat simple_student.c #include "list.h" #include <stdio.h> #include <stdlib.h> #include <string.h> #define MAX_LENGTH 100 typedef struct student { char first_name[MAX_LENGTH]; char last_name[MAX_LENGTH]; unsigned int age; struct list_head student_node; } student_t; struct list_head class; INIT_LIST_HEAD(&class); //LIST_HEAD(class); student_t *stu = NULL; student_t *make_student (const char *fn, const char *ln, unsigned int a) { student_t *stu = NULL; if ((stu = calloc(1, sizeof(struct student))) == NULL) { return NULL; } if (strlen(fn) > (MAX_LENGTH - 1)) { return NULL; } if (strlen(ln) > (MAX_LENGTH - 1)) { return NULL; } strcpy(stu->first_name, fn); strcpy(stu->last_name, ln); stu->age = a; return stu; } int main(){ if ((stu = make_student("ahao","mu",22)) == NULL) { fprintf(stderr,"Failed to create muahao\n"); return -1; } }
方法1 : struct list_head class; INIT_LIST_HEAD(&class); 方法2: LIST_HEAD(class);
在这里,我总是,使用方式1 的时候会提示如下错误:
#gcc -o simple_student simple_student.c simple_student.c:16:16: error: expected declaration specifiers or ‘...’ before ‘&’ token INIT_LIST_HEAD(&class); ^
附录
#cat student.c #include "list.h" #include <stdlib.h> #include <string.h> #include <stdio.h> #define MAX_LENGTH 100 typedef struct student { char first_name[MAX_LENGTH]; char last_name[MAX_LENGTH]; unsigned int age; struct list_head node_student; } student_t; student_t *make_student (const char *fn, const char *ln, unsigned int a) { student_t *stu = NULL; if ((stu = calloc(1, sizeof(struct student))) == NULL) { return NULL; } if (strlen(fn) > (MAX_LENGTH - 1)) { return NULL; } if (strlen(ln) > (MAX_LENGTH - 1)) { return NULL; } strcpy(stu->first_name, fn); strcpy(stu->last_name, ln); stu->age = a; return stu; } int main() { // Method 1 //struct list_head class; //INIT_LIST_HEAD(&class); //Method 2 LIST_HEAD(class); student_t *stu = NULL; //////////////////create student if ((stu = make_student("ahao","mu",22)) == NULL) { fprintf(stderr,"Failed to create muahao\n"); return -1; } list_add_tail(&stu->node_student, &class); if ((stu = make_student("lianjie", "li", 23)) == NULL) { fprintf(stderr,"Failed to create li tom\n"); return -1; } list_add_tail(&stu->node_student, &class); if ((stu = make_student("xiaolong", "li", 12)) == NULL) { fprintf(stderr, "Failed to create lixuehua\n"); return -1; } list_add_tail(&stu->node_student, &class); ///////////// Print all students printf("------print all students-----\n"); list_for_each_entry(stu, &class, node_student) { printf("First name:%s\n", stu->first_name); printf("Last name:%s\n", stu->last_name); printf("Age:%d\n", stu->age); } //////////////// list bus LIST_HEAD(bus); printf("Moving all students to bus;\n"); list_splice_init(&class, &bus); if (list_empty(&class)) { printf("No students in class\n"); } printf("Print all bus students\n"); list_for_each_entry(stu, &bus, node_student) { printf("%s %s %d", stu->first_name, stu->last_name, stu->age); } student_t *tmp = NULL; printf("muahao do not want go\n"); list_for_each_entry_safe(stu, tmp, &bus, node_student) { if (strcmp(stu->first_name, "mu") == 0) { list_del(&stu->node_student); free(stu); } } printf("After muahao leave,students are: \n"); list_for_each_entry(stu, &bus, node_student) { printf("%s %s %d\n",stu->first_name, stu->last_name, stu->age); } // end begin clean all students; list_for_each_entry_safe(stu, tmp, &bus, node_student) { printf("Removing students: %s %s \n", stu->first_name, stu->last_name); list_del(&stu->node_student); free(stu); } return 0; }
#cat list.h /* * Copyright (C) 2012 Fusion-io. All rights reserved. * * This header was taken from the Linux kernel * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License v2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _LINUX_LIST_H #define _LINUX_LIST_H #define LIST_POISON1 ((void *) 0x00100100) #define LIST_POISON2 ((void *) 0x00200200) #undef offsetof #ifdef __compiler_offsetof #define offsetof(TYPE,MEMBER) __compiler_offsetof(TYPE,MEMBER) #else #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) #endif #define container_of(ptr, type, member) ({ \ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ (type *)( (char *)__mptr - offsetof(type,member) );}) /* * Simple doubly linked list implementation. * * Some of the internal functions ("__xxx") are useful when * manipulating whole lists rather than single entries, as * sometimes we already know the next/prev entries and we can * generate better code by using them directly rather than * using the generic single-entry routines. */ struct list_head { struct list_head *next, *prev; }; #define LIST_HEAD_INIT(name) { &(name), &(name) } #define LIST_HEAD(name) \ struct list_head name = LIST_HEAD_INIT(name) static inline void INIT_LIST_HEAD(struct list_head *list) { list->next = list; list->prev = list; } /* * Insert a new entry between two known consecutive entries. * * This is only for internal list manipulation where we know * the prev/next entries already! */ #ifndef CONFIG_DEBUG_LIST 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; } #else extern void __list_add(struct list_head *new, struct list_head *prev, struct list_head *next); #endif /** * 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. */ #ifndef CONFIG_DEBUG_LIST static inline void list_add(struct list_head *new, struct list_head *head) { __list_add(new, head, head->next); } #else extern void list_add(struct list_head *new, struct list_head *head); #endif /** * 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); } /* * 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. */ #ifndef CONFIG_DEBUG_LIST static inline void list_del(struct list_head *entry) { __list_del(entry->prev, entry->next); entry->next = LIST_POISON1; entry->prev = LIST_POISON2; } #else extern void list_del(struct list_head *entry); #endif /** * list_replace - replace old entry by new one * @old : the element to be replaced * @new : the new element to insert * Note: if 'old' was empty, it will be overwritten. */ static inline void list_replace(struct list_head *old, struct list_head *new) { new->next = old->next; new->next->prev = new; new->prev = old->prev; new->prev->next = new; } static inline void list_replace_init(struct list_head *old, struct list_head *new) { list_replace(old, new); INIT_LIST_HEAD(old); } /** * 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_is_last - tests whether @list is the last entry in list @head * @list: the entry to test * @head: the head of the list */ static inline int list_is_last(const struct list_head *list, const struct list_head *head) { return list->next == head; } /** * list_empty - tests whether a list is empty * @head: the list to test. */ static inline int list_empty(const struct list_head *head) { return head->next == head; } /** * list_empty_careful - tests whether a list is empty and not being modified * @head: the list to test * * Description: * tests whether a list is empty _and_ checks that no other CPU might be * in the process of modifying either member (next or prev) * * NOTE: using list_empty_careful() without synchronization * can only be safe if the only activity that can happen * to the list entry is list_del_init(). Eg. it cannot be used * if another CPU could re-list_add() it. */ static inline int list_empty_careful(const struct list_head *head) { struct list_head *next = head->next; return (next == head) && (next == head->prev); } 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_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) \ container_of(ptr, type, member) /** * list_for_each - iterate over a list * @pos: the &struct list_head to use as a loop cursor. * @head: the head for your list. */ #define list_for_each(pos, head) \ for (pos = (head)->next; pos != (head); \ pos = pos->next) /** * __list_for_each - iterate over a list * @pos: the &struct list_head to use as a loop cursor. * @head: the head for your list. * * This variant differs from list_for_each() in that it's the * simplest possible list iteration code, no prefetching is done. * Use this for code that knows the list to be very short (empty * or 1 entry) most of the time. */ #define __list_for_each(pos, head) \ for (pos = (head)->next; pos != (head); pos = pos->next) /** * list_for_each_prev - iterate over a list backwards * @pos: the &struct list_head to use as a loop cursor. * @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 cursor. * @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_entry - iterate over list of given type * @pos: the type * to use as a loop cursor. * @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_reverse - iterate backwards over list of given type. * @pos: the type * to use as a loop cursor. * @head: the head for your list. * @member: the name of the list_struct within the struct. */ #define list_for_each_entry_reverse(pos, head, member) \ for (pos = list_entry((head)->prev, typeof(*pos), member); \ &pos->member != (head); \ pos = list_entry(pos->member.prev, typeof(*pos), member)) /** * list_prepare_entry - prepare a pos entry for use in list_for_each_entry_continue * @pos: the type * to use as a start point * @head: the head of the list * @member: the name of the list_struct within the struct. * * Prepares a pos entry for use as a start point in list_for_each_entry_continue. */ #define list_prepare_entry(pos, head, member) \ ((pos) ? : list_entry(head, typeof(*pos), member)) /** * list_for_each_entry_continue - continue iteration over list of given type * @pos: the type * to use as a loop cursor. * @head: the head for your list. * @member: the name of the list_struct within the struct. * * Continue to iterate over list of given type, continuing after * the current position. */ #define list_for_each_entry_continue(pos, head, member) \ for (pos = list_entry(pos->member.next, typeof(*pos), member); \ &pos->member != (head); \ pos = list_entry(pos->member.next, typeof(*pos), member)) /** * list_for_each_entry_from - iterate over list of given type from the current point * @pos: the type * to use as a loop cursor. * @head: the head for your list. * @member: the name of the list_struct within the struct. * * Iterate over list of given type, continuing from current position. */ #define list_for_each_entry_from(pos, head, member) \ for (; &pos->member != (head); \ pos = list_entry(pos->member.next, typeof(*pos), member)) /** * 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 cursor. * @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)) /** * list_for_each_entry_safe_continue * @pos: the type * to use as a loop cursor. * @n: another type * to use as temporary storage * @head: the head for your list. * @member: the name of the list_struct within the struct. * * Iterate over list of given type, continuing after current point, * safe against removal of list entry. */ #define list_for_each_entry_safe_continue(pos, n, head, member) \ for (pos = list_entry(pos->member.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)) /** * list_for_each_entry_safe_from * @pos: the type * to use as a loop cursor. * @n: another type * to use as temporary storage * @head: the head for your list. * @member: the name of the list_struct within the struct. * * Iterate over list of given type from current point, safe against * removal of list entry. */ #define list_for_each_entry_safe_from(pos, n, head, member) \ for (n = list_entry(pos->member.next, typeof(*pos), member); \ &pos->member != (head); \ pos = n, n = list_entry(n->member.next, typeof(*n), member)) /** * list_for_each_entry_safe_reverse * @pos: the type * to use as a loop cursor. * @n: another type * to use as temporary storage * @head: the head for your list. * @member: the name of the list_struct within the struct. * * Iterate backwards over list of given type, safe against removal * of list entry. */ #define list_for_each_entry_safe_reverse(pos, n, head, member) \ for (pos = list_entry((head)->prev, typeof(*pos), member), \ n = list_entry(pos->member.prev, typeof(*pos), member); \ &pos->member != (head); \ pos = n, n = list_entry(n->member.prev, typeof(*n), member)) #endif
来源:https://www.cnblogs.com/muahao/p/7509629.html