linux内核之链表操作解析

来源:互联网 发布:windows相对路径写法 编辑:程序博客网 时间:2024/05/17 15:35

linux内核之链表操作解析

本文只是对linux内核中的链表进行分析。内核版本是linux-2.6.32.63。文件在:linux内核/linux-2.6.32.63/include/linux/list.h。本文对list.h文件进行简要分析,有必要的地方还会以图进行说明。

代码分析

链表结构体:

有前驱和后继,说明是双链表

struct list_head {      struct list_head *next, *prev;  }; 

链表头节点相关操作:

为head初始化,把head的next和prev都赋值为head的地址
因为定义的是宏,所以可以直接把后面的语句替换前面的宏直接看,
struct list_head name = {&(name),&(name)};,这样会更容易理解

#define LIST_HEAD_INIT(name) { &(name), &(name) }    #define LIST_HEAD(name) \  struct list_head name = LIST_HEAD_INIT(name)  

上面是使用宏进行的head初始化(静态初始化,因为宏会在程序预编译时期进行宏名替换)
下面这个是在运行时,内嵌到调用函数中。(因为这个是内联函数,调用时直接用函数体内嵌到被调函数中)

static inline void INIT_LIST_HEAD(struct list_head *list)  {      list->next = list;      list->prev = list;  }

链表节点插入操作:

这是一个增加插入的公用函数函数实现的是:
prev <<=>> new <<=>> next,new是要新增的节点,pre和next是相邻的节点
A <<=>> B 表示A的后继指向B,B的前驱指向A, 后面调用时,根据这个关系就更好理解了。
也可以直接看后面的list_add()函数,把结构体带入函数中也会好理解些
在内核中有很多这种函数类型:前面带有两个_的(即:__记住是两杠),一般来说这种类型的
函数都是不能直接调用的,一定要先通过包装这个函数,然后才能调用。这是个原始函数

#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(),实现从头节点到头结点的next之间插入元素  // head <<=>> new <<=>> head->next  static inline void list_add(struct list_head *new, struct list_head *head)  {      __list_add(new, head, head->next);  }    // 从链表尾部插入节点  // 包装了函数:__list_add(),实现从头节点的prev和头结点之间插入元素  // head-prev <<=>> new <<=>> head  static inline void list_add_tail(struct list_head *new, struct list_head *head)  {      __list_add(new, head->prev, head);  }  

链表节点删除操作:

// 这是个删除的通用函数,实现得: prev <<=>> next  // 这是让prev和next建立起链接来。可以联系list_del()函数来分析  // 和上面分析一样该函数前缀为__所以一般是用来被包装的原始函数  // 其实这个函数并没有删除这个节点,而是把这个节点从链表上卸下来而已  static inline void __list_del(struct list_head * prev, struct list_head * next)  {      next->prev = prev;      prev->next = next;  }    // 这是个删除的函数,参数则是将要删除的节点。  // 调用_list_del() 函数来让entry节点从链表中卸下来,并且让它的前后节点建立连接,  // 然后entry前后指针设置为个特殊的值,设置了这个值后的元素被访问时会引起页故障。  #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_del() 来让entry节点从链表中卸下来,并且让它的前后节点建立连接,  // 然后调用INIT_LIST_HEAD() 函数使得entry节点变成空节点。  static inline void list_del_init(struct list_head *entry)  {      __list_del(entry->prev, entry->next);      INIT_LIST_HEAD(entry);  }

链表节点移动操作:

// 这个函数首先调用__list_del()函数让list从链表上卸下了,并且让它的前后节点建立连接  // 然后调用list_add()函数 往头部插入该节点。函数的总体意思是:把某个位置上的节点移动到头节点后插入。   static inline void list_move(struct list_head *list, struct list_head *head)  {      __list_del(list->prev, list->next);// 把节点从链表中卸下来      list_add(list, head);// 把卸下来的链表插入打到头节点后面  }    // 这个函数和上个功能一样,这是插入的位置是在头节点的尾部  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);// 把卸下来的节点插入到链表头节点的尾部  }

链表节点替换操作:

// 这是个替换的通用函数。就是让new节点替换old节点,但  // old指针的前驱和后继都没有改变,就是old节点还是挂在链表上的  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;  }    // 这个函数首先调用list_replace() 函数用new替换了old的指针关系。  // 然后调用INIT_LIST_HEAD() 函数让old节点变成空节点  static inline void list_replace_init(struct list_head *old,          struct list_head *new)  {      list_replace(old, new);      INIT_LIST_HEAD(old);  }  

链表判空操作和判断是否唯一节点操作:

// 判断list节点是否是该链表中最后的一个节点。  // 因为是环链表,所以若是最后一个节点。则该节点的后继为头节点:list->next = head  static inline int list_is_last(const struct list_head *list,          const struct list_head *head)  {      return list->next == head;  }    // 判断该链表是否是空链表,只有一个head节点  static inline int list_empty(const struct list_head *head)  {      return head->next == head;  }     // 这个函数和上面的一样,是个判空函数。唯一不同的是这个函数可以防止该该链表   // 同时正在被另外一个cpu操作,以导致head的前驱和后续不一样。其实换个角度来看   // 该函数也可以用来判断该链表是否还在被其他CPU操作  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 int list_is_singular(const struct list_head *head)  {      return !list_empty(head) && (head->next == head->prev);  }  

链表分割操作:

// 单看这个函数是比较难看出怎么分割的。这有个前提是head 和 entry 是在同一个链表上的节点  //  第一步:....<<=>> head <<=>>......<<=>> entry <<=>>.....  //  第二步:设head的next为head_next,entry的next为entry_next    //  第三步:....<<=>> head <<=>> head_next <<=>>.....<<=>> entry <<=>> entry_next <<=>>....  //  第四步:经过函数分割后得两条链表:...<<=>> head <<=>> entry_next <<=>> .....     //  和  ....<<=>> entry <<=>> list <<=>> head_next <<=>> ....  // 函数功能:函数把head....entry这个链表分割成两条链表(这是个分割的原始函数)  static inline void __list_cut_position(struct list_head *list,          struct list_head *head, struct list_head *entry)  {      struct list_head *new_first = entry->next;      list->next = head->next;      list->next->prev = list;      list->prev = entry;      entry->next = list;      head->next = new_first;      new_first->prev = head;  }    // 这是个分割函数,与上面这个函数不同的是,  // 这个函数考虑到了空链表和一个节点的链表情况  static inline void list_cut_position(struct list_head *list,          struct list_head *head, struct list_head *entry)  {      if (list_empty(head))          return;      if (list_is_singular(head) &&          (head->next != entry && head != entry))          return;      if (entry == head)          INIT_LIST_HEAD(list);      else          __list_cut_position(list, head, entry);  } 

上面的原始链表拆分函数单看代码是比较难理解的,下面画了图,看图方便理解下:

链表整合操作:

// 这个函数的实现有点不好解释,如果要想理解这个函数的意思最好是根据后面的list_splice()函数来。  // 先说下前提:list是个单独的链表;prev和next是个链表中相邻的2个节点  // 而这个函数实现的是把list和prev这个链表相整合成一个链表。prev和next中断开连接list前后2个节点  // 但list节点前驱和后继还是没有修改。这也是个原始整合函数,需要包装才能使用  static inline void __list_splice(const struct list_head *list,          struct list_head *prev,          struct list_head *next)  {      struct list_head *first = list->next;      struct list_head *last = list->prev;        first->prev = prev;      prev->next = first;        last->next = next;      next->prev = last;  } 

原始链表整合操作图:

 // 这个函数是先考虑list是否为空表,然后调用上面的整合函数,从头部整合进去。   // 但这个list的前驱和后继都没有更改  static inline void list_splice(const struct list_head *list,          struct list_head *head)  {      if (!list_empty(list))          __list_splice(list, head, head->next);  }     // 同上个函数,只是从尾部整合进去  static inline void list_splice_tail(struct list_head *list,          struct list_head *head)  {      if (!list_empty(list))          __list_splice(list, head->prev, head);  }    // 这是解决 list_splice()函数中list的前驱和后继没有修改的问题。   // 该函数调用INIT_LIST_HEAD(list)来是list为空节点  static inline void list_splice_init(struct list_head *list,          struct list_head *head)  {      if (!list_empty(list)) {          __list_splice(list, head, head->next);          INIT_LIST_HEAD(list);      }  }    // 这个函数和list_splice_tail()这个函数功能是一样的,只是这个函数对list进行了处理。  // 让list变成了空节点。其实有点不理解的是list_splice_tail()函数为什么不对list进行处理  static inline void list_splice_tail_init(struct list_head *list,  struct list_head *head)  {      if (!list_empty(list)) {      __list_splice(list, head->prev, head);      INIT_LIST_HEAD(list);      }  }

链表节点访问数据项操作:

// 这个宏是list链表中一个精髓,访问包含节点的结构体中其他数据项  // 后面会详细的分析这个宏的具体使用//container_of宏用来根据成员的地址来获取结构体的地址。/** * 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)    #define list_first_entry(ptr, type, member) \  list_entry((ptr)->next, type, member)  

list_entry的理解

我们来看一下container_of的宏定义:

#define container_of(ptr, type, member)                   \({                                                        \    const typeof(((type *)0)->member) * __mptr = (ptr);   \    (type *)((char *)__mptr - offsetof(type, member));    \})

其次,offsetof的宏定义:

#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)

TYPE是结构体类型,例如:

struct TYPE{    //...    struct list_head member;    //...};

其次,MEMBER就是TYPE中的list_head变量member

那么:
(TYPE *)0是将0强制转换成TYPE型指针,则该指针一定指向0地址(数据段基址)。
&((TYPE *)0)->MEMBER这句话其实是&(((TYPE *)0)->MEMBER),通过该指针访问TYPE的MEMBER成员并得到其地址。
相对于结构体的起始地址0,那么&((TYPE *)0)->MEMBER就是相对于起始地址之间的偏移量,这个偏移量对于所有的TYPE型变量都是成立的。
offsetof(TYPE, MEMBER)就表示这个偏移量。

对于container_of中,

const typeof(((type *)0)->member) * __mptr = (ptr);

由于下面我们要对指针进行强制类型转换,所以这里我们又申请一个指针,指向和ptr相同的位置。
这里的ptr指的是实际list_head member的地址。

(char *)__mptr

由于offsetof()函数求得的是偏移字节数,所以这里(char *)__mptr使得指针的加减操作步长为1Byte,然后二者相减便可以得到TYPE变量的起始地址,最后通过(type *)类型转换,将该地址转换为TYPE类型的指针。

链表节点的遍历操作:

参数相关释义:
cpp /** * @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. */

// 这是个遍历宏,从头往后遍历,算是个比较简单的函数。  // prefetch()是个预取值指令,目的是提高运行效率  #define list_for_each(pos, head) \  for (pos = (head)->next; prefetch(pos->next), pos != (head); \          pos = pos->next)  
// 这个函数功能同上,只是没有prefetch()  #define __list_for_each(pos, head) \  for (pos = (head)->next; pos != (head); pos = pos->next)  
// 这是个遍历宏,从尾往头遍历,算是个比较简单的函数。  // prefetch()是个预取值指令,目的是提高运行效率  #define list_for_each_prev(pos, head) \  for (pos = (head)->prev; prefetch(pos->prev), pos != (head); \          pos = pos->prev)  
// 这是个设计比较巧妙的函数,同样也是遍历函数,只是这个函数考虑到了pos在遍历过程中有可能被删除掉  // 如果还是和上面的遍历函数一样,那假若pos被删除了,则整个程序就会出错停止运行。而现在用个临时变量n  // 可以把数据存放在n中,若pos被删除掉了,那pos = n 又会让pos有效。所以程序不会出错。  #define list_for_each_safe(pos, n, head) \  for (pos = (head)->next, n = pos->next; pos != (head); \  pos = n, n = pos->next)  
// 函数功能同上面那个,只是遍历是从head->prev(尾部)那端开始  #define list_for_each_prev_safe(pos, n, head) \  for (pos = (head)->prev, n = pos->prev; \      prefetch(pos->prev), pos != (head); \      pos = n, n = pos->prev)  
// 这是个有数据项的遍历,  //  typedef struct pos{  //         type date;  //     struct head_list member;  //         }pos;  //  list_entry(&ptr,typeof(pos),ptr);这是个由结构体变量中的某个成员而获取到  //  整个结构体变量的地址指针方法。typeof(pos)是获取到pos的类型  //  这里应该是在创建第一个节点时,让head = &pos->member  #define list_for_each_entry(pos, head, member) \  for (pos = list_entry((head)->next, typeof(*pos), member);\      prefetch(pos->member.next), &pos->member != (head);\      pos = list_entry(pos->member.next, typeof(*pos), member))  
// 函数功能同上,只是从member.prev(尾部)开始遍历  #define list_for_each_entry_reverse(pos, head, member)\  for (pos = list_entry((head)->prev, typeof(*pos), member);\      prefetch(pos->member.prev), &pos->member != (head);\      pos = list_entry(pos->member.prev, typeof(*pos), member))  
// 这是问号表达式,当问号后一个选项为空时,则不做任何操作。  // 所以这是个判空宏,若pos存在,则不做操作,不存在则通过head来虚拟个pos节点  #define list_prepare_entry(pos, head, member) \  ((pos) ? : list_entry(head, typeof(*pos), member))  
// 这也是遍历数据项的函数,和前面的函数不同的是,这个函数不是从head开始遍历,  // 而是从任意的节点处遍历,直到到达头节点  #define list_for_each_entry_continue(pos, head, member) \  for (pos = list_entry(pos->member.next, typeof(*pos), member);\      prefetch(pos->member.next), &pos->member != (head);\      pos = list_entry(pos->member.next, typeof(*pos), member))  
// 函数功能和上面的相同,只是遍历放向是从尾部开始遍历的  #define list_for_each_entry_continue_reverse(pos, head, member)\  for (pos = list_entry(pos->member.prev, typeof(*pos), member);\      prefetch(pos->member.prev), &pos->member != (head);\      pos = list_entry(pos->member.prev, typeof(*pos), member))  
// 这个函数功能和list_for_each_entry_continue()和像,只是遍历的起点不一样。  // list_for_each_entry_continue()是从该节点开始,这个函数则是从该节点的下个节点开始。  #define list_for_each_entry_from(pos, head, member) \  for (; prefetch(pos->member.next), &pos->member != (head);\      pos = list_entry(pos->member.next, typeof(*pos), member))  
// 这个和上个遍历删除节点的函数类似。多了个临时变量n,  // 所以可以防止pos在遍历时,被删除出现的错误。  #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))  
// 函数功能同上面那个,只是遍历是从某个节点开始  #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))  
// 函数功能同上面那个,只是遍历是从某个节点的下个节点开始  #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))  
// 同上个函数,只是从尾部开始  #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))  

linux内核之链表操作解析

本文只是对linux内核中的链表进行分析。内核版本是linux-2.6.32.63。文件在:linux内核/linux-2.6.32.63/include/linux/list.h。本文对list.h文件进行简要分析,有必要的地方还会以图进行说明。

代码分析

链表结构体:

有前驱和后继,说明是双链表

struct list_head {      struct list_head *next, *prev;  }; 

链表头节点相关操作:

为head初始化,把head的next和prev都赋值为head的地址
因为定义的是宏,所以可以直接把后面的语句替换前面的宏直接看,
struct list_head name = {&(name),&(name)};,这样会更容易理解

#define LIST_HEAD_INIT(name) { &(name), &(name) }    #define LIST_HEAD(name) \  struct list_head name = LIST_HEAD_INIT(name)  

上面是使用宏进行的head初始化(静态初始化,因为宏会在程序预编译时期进行宏名替换)
下面这个是在运行时,内嵌到调用函数中。(因为这个是内联函数,调用时直接用函数体内嵌到被调函数中)

static inline void INIT_LIST_HEAD(struct list_head *list)  {      list->next = list;      list->prev = list;  }

链表节点插入操作:

这是一个增加插入的公用函数函数实现的是:
prev <<=>> new <<=>> next,new是要新增的节点,pre和next是相邻的节点
A <<=>> B 表示A的后继指向B,B的前驱指向A, 后面调用时,根据这个关系就更好理解了。
也可以直接看后面的list_add()函数,把结构体带入函数中也会好理解些
在内核中有很多这种函数类型:前面带有两个_的(即:__记住是两杠),一般来说这种类型的
函数都是不能直接调用的,一定要先通过包装这个函数,然后才能调用。这是个原始函数

#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(),实现从头节点到头结点的next之间插入元素  // head <<=>> new <<=>> head->next  static inline void list_add(struct list_head *new, struct list_head *head)  {      __list_add(new, head, head->next);  }    // 从链表尾部插入节点  // 包装了函数:__list_add(),实现从头节点的prev和头结点之间插入元素  // head-prev <<=>> new <<=>> head  static inline void list_add_tail(struct list_head *new, struct list_head *head)  {      __list_add(new, head->prev, head);  }  

链表节点删除操作:

// 这是个删除的通用函数,实现得: prev <<=>> next  // 这是让prev和next建立起链接来。可以联系list_del()函数来分析  // 和上面分析一样该函数前缀为__所以一般是用来被包装的原始函数  // 其实这个函数并没有删除这个节点,而是把这个节点从链表上卸下来而已  static inline void __list_del(struct list_head * prev, struct list_head * next)  {      next->prev = prev;      prev->next = next;  }    // 这是个删除的函数,参数则是将要删除的节点。  // 调用_list_del() 函数来让entry节点从链表中卸下来,并且让它的前后节点建立连接,  // 然后entry前后指针设置为个特殊的值,设置了这个值后的元素被访问时会引起页故障。  #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_del() 来让entry节点从链表中卸下来,并且让它的前后节点建立连接,  // 然后调用INIT_LIST_HEAD() 函数使得entry节点变成空节点。  static inline void list_del_init(struct list_head *entry)  {      __list_del(entry->prev, entry->next);      INIT_LIST_HEAD(entry);  }

链表节点移动操作:

// 这个函数首先调用__list_del()函数让list从链表上卸下了,并且让它的前后节点建立连接  // 然后调用list_add()函数 往头部插入该节点。函数的总体意思是:把某个位置上的节点移动到头节点后插入。   static inline void list_move(struct list_head *list, struct list_head *head)  {      __list_del(list->prev, list->next);// 把节点从链表中卸下来      list_add(list, head);// 把卸下来的链表插入打到头节点后面  }    // 这个函数和上个功能一样,这是插入的位置是在头节点的尾部  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);// 把卸下来的节点插入到链表头节点的尾部  }

链表节点替换操作:

// 这是个替换的通用函数。就是让new节点替换old节点,但  // old指针的前驱和后继都没有改变,就是old节点还是挂在链表上的  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;  }    // 这个函数首先调用list_replace() 函数用new替换了old的指针关系。  // 然后调用INIT_LIST_HEAD() 函数让old节点变成空节点  static inline void list_replace_init(struct list_head *old,          struct list_head *new)  {      list_replace(old, new);      INIT_LIST_HEAD(old);  }  

链表判空操作和判断是否唯一节点操作:

// 判断list节点是否是该链表中最后的一个节点。  // 因为是环链表,所以若是最后一个节点。则该节点的后继为头节点:list->next = head  static inline int list_is_last(const struct list_head *list,          const struct list_head *head)  {      return list->next == head;  }    // 判断该链表是否是空链表,只有一个head节点  static inline int list_empty(const struct list_head *head)  {      return head->next == head;  }     // 这个函数和上面的一样,是个判空函数。唯一不同的是这个函数可以防止该该链表   // 同时正在被另外一个cpu操作,以导致head的前驱和后续不一样。其实换个角度来看   // 该函数也可以用来判断该链表是否还在被其他CPU操作  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 int list_is_singular(const struct list_head *head)  {      return !list_empty(head) && (head->next == head->prev);  }  

链表分割操作:

// 单看这个函数是比较难看出怎么分割的。这有个前提是head 和 entry 是在同一个链表上的节点  //  第一步:....<<=>> head <<=>>......<<=>> entry <<=>>.....  //  第二步:设head的next为head_next,entry的next为entry_next    //  第三步:....<<=>> head <<=>> head_next <<=>>.....<<=>> entry <<=>> entry_next <<=>>....  //  第四步:经过函数分割后得两条链表:...<<=>> head <<=>> entry_next <<=>> .....     //  和  ....<<=>> entry <<=>> list <<=>> head_next <<=>> ....  // 函数功能:函数把head....entry这个链表分割成两条链表(这是个分割的原始函数)  static inline void __list_cut_position(struct list_head *list,          struct list_head *head, struct list_head *entry)  {      struct list_head *new_first = entry->next;      list->next = head->next;      list->next->prev = list;      list->prev = entry;      entry->next = list;      head->next = new_first;      new_first->prev = head;  }    // 这是个分割函数,与上面这个函数不同的是,  // 这个函数考虑到了空链表和一个节点的链表情况  static inline void list_cut_position(struct list_head *list,          struct list_head *head, struct list_head *entry)  {      if (list_empty(head))          return;      if (list_is_singular(head) &&          (head->next != entry && head != entry))          return;      if (entry == head)          INIT_LIST_HEAD(list);      else          __list_cut_position(list, head, entry);  } 

上面的原始链表拆分函数单看代码是比较难理解的,下面画了图,看图方便理解下:

链表整合操作:

// 这个函数的实现有点不好解释,如果要想理解这个函数的意思最好是根据后面的list_splice()函数来。  // 先说下前提:list是个单独的链表;prev和next是个链表中相邻的2个节点  // 而这个函数实现的是把list和prev这个链表相整合成一个链表。prev和next中断开连接list前后2个节点  // 但list节点前驱和后继还是没有修改。这也是个原始整合函数,需要包装才能使用  static inline void __list_splice(const struct list_head *list,          struct list_head *prev,          struct list_head *next)  {      struct list_head *first = list->next;      struct list_head *last = list->prev;        first->prev = prev;      prev->next = first;        last->next = next;      next->prev = last;  } 

原始链表整合操作图:

 // 这个函数是先考虑list是否为空表,然后调用上面的整合函数,从头部整合进去。   // 但这个list的前驱和后继都没有更改  static inline void list_splice(const struct list_head *list,          struct list_head *head)  {      if (!list_empty(list))          __list_splice(list, head, head->next);  }     // 同上个函数,只是从尾部整合进去  static inline void list_splice_tail(struct list_head *list,          struct list_head *head)  {      if (!list_empty(list))          __list_splice(list, head->prev, head);  }    // 这是解决 list_splice()函数中list的前驱和后继没有修改的问题。   // 该函数调用INIT_LIST_HEAD(list)来是list为空节点  static inline void list_splice_init(struct list_head *list,          struct list_head *head)  {      if (!list_empty(list)) {          __list_splice(list, head, head->next);          INIT_LIST_HEAD(list);      }  }    // 这个函数和list_splice_tail()这个函数功能是一样的,只是这个函数对list进行了处理。  // 让list变成了空节点。其实有点不理解的是list_splice_tail()函数为什么不对list进行处理  static inline void list_splice_tail_init(struct list_head *list,  struct list_head *head)  {      if (!list_empty(list)) {      __list_splice(list, head->prev, head);      INIT_LIST_HEAD(list);      }  }

链表节点访问数据项操作:

// 这个宏是list链表中一个精髓,访问包含节点的结构体中其他数据项  // 后面会详细的分析这个宏的具体使用//container_of宏用来根据成员的地址来获取结构体的地址。/** * 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)    #define list_first_entry(ptr, type, member) \  list_entry((ptr)->next, type, member)  

list_entry的理解

我们来看一下container_of的宏定义:

#define container_of(ptr, type, member)                   \({                                                        \    const typeof(((type *)0)->member) * __mptr = (ptr);   \    (type *)((char *)__mptr - offsetof(type, member));    \})

其次,offsetof的宏定义:

#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)

TYPE是结构体类型,例如:

struct TYPE{    //...    struct list_head member;    //...};

其次,MEMBER就是TYPE中的list_head变量member

那么:
(TYPE *)0是将0强制转换成TYPE型指针,则该指针一定指向0地址(数据段基址)。
&((TYPE *)0)->MEMBER这句话其实是&(((TYPE *)0)->MEMBER),通过该指针访问TYPE的MEMBER成员并得到其地址。
相对于结构体的起始地址0,那么&((TYPE *)0)->MEMBER就是相对于起始地址之间的偏移量,这个偏移量对于所有的TYPE型变量都是成立的。
offsetof(TYPE, MEMBER)就表示这个偏移量。

对于container_of中,

const typeof(((type *)0)->member) * __mptr = (ptr);

由于下面我们要对指针进行强制类型转换,所以这里我们又申请一个指针,指向和ptr相同的位置。
这里的ptr指的是实际list_head member的地址。

(char *)__mptr

由于offsetof()函数求得的是偏移字节数,所以这里(char *)__mptr使得指针的加减操作步长为1Byte,然后二者相减便可以得到TYPE变量的起始地址,最后通过(type *)类型转换,将该地址转换为TYPE类型的指针。

链表节点的遍历操作:

参数相关释义:
cpp /** * @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. */

// 这是个遍历宏,从头往后遍历,算是个比较简单的函数。  // prefetch()是个预取值指令,目的是提高运行效率  #define list_for_each(pos, head) \  for (pos = (head)->next; prefetch(pos->next), pos != (head); \          pos = pos->next)  
// 这个函数功能同上,只是没有prefetch()  #define __list_for_each(pos, head) \  for (pos = (head)->next; pos != (head); pos = pos->next)  
// 这是个遍历宏,从尾往头遍历,算是个比较简单的函数。  // prefetch()是个预取值指令,目的是提高运行效率  #define list_for_each_prev(pos, head) \  for (pos = (head)->prev; prefetch(pos->prev), pos != (head); \          pos = pos->prev)  
// 这是个设计比较巧妙的函数,同样也是遍历函数,只是这个函数考虑到了pos在遍历过程中有可能被删除掉  // 如果还是和上面的遍历函数一样,那假若pos被删除了,则整个程序就会出错停止运行。而现在用个临时变量n  // 可以把数据存放在n中,若pos被删除掉了,那pos = n 又会让pos有效。所以程序不会出错。  #define list_for_each_safe(pos, n, head) \  for (pos = (head)->next, n = pos->next; pos != (head); \  pos = n, n = pos->next)  
// 函数功能同上面那个,只是遍历是从head->prev(尾部)那端开始  #define list_for_each_prev_safe(pos, n, head) \  for (pos = (head)->prev, n = pos->prev; \      prefetch(pos->prev), pos != (head); \      pos = n, n = pos->prev)  
// 这是个有数据项的遍历,  //  typedef struct pos{  //         type date;  //     struct head_list member;  //         }pos;  //  list_entry(&ptr,typeof(pos),ptr);这是个由结构体变量中的某个成员而获取到  //  整个结构体变量的地址指针方法。typeof(pos)是获取到pos的类型  //  这里应该是在创建第一个节点时,让head = &pos->member  #define list_for_each_entry(pos, head, member) \  for (pos = list_entry((head)->next, typeof(*pos), member);\      prefetch(pos->member.next), &pos->member != (head);\      pos = list_entry(pos->member.next, typeof(*pos), member))  
// 函数功能同上,只是从member.prev(尾部)开始遍历  #define list_for_each_entry_reverse(pos, head, member)\  for (pos = list_entry((head)->prev, typeof(*pos), member);\      prefetch(pos->member.prev), &pos->member != (head);\      pos = list_entry(pos->member.prev, typeof(*pos), member))  
// 这是问号表达式,当问号后一个选项为空时,则不做任何操作。  // 所以这是个判空宏,若pos存在,则不做操作,不存在则通过head来虚拟个pos节点  #define list_prepare_entry(pos, head, member) \  ((pos) ? : list_entry(head, typeof(*pos), member))  
// 这也是遍历数据项的函数,和前面的函数不同的是,这个函数不是从head开始遍历,  // 而是从任意的节点处遍历,直到到达头节点  #define list_for_each_entry_continue(pos, head, member) \  for (pos = list_entry(pos->member.next, typeof(*pos), member);\      prefetch(pos->member.next), &pos->member != (head);\      pos = list_entry(pos->member.next, typeof(*pos), member))  
// 函数功能和上面的相同,只是遍历放向是从尾部开始遍历的  #define list_for_each_entry_continue_reverse(pos, head, member)\  for (pos = list_entry(pos->member.prev, typeof(*pos), member);\      prefetch(pos->member.prev), &pos->member != (head);\      pos = list_entry(pos->member.prev, typeof(*pos), member))  
// 这个函数功能和list_for_each_entry_continue()和像,只是遍历的起点不一样。  // list_for_each_entry_continue()是从该节点开始,这个函数则是从该节点的下个节点开始。  #define list_for_each_entry_from(pos, head, member) \  for (; prefetch(pos->member.next), &pos->member != (head);\      pos = list_entry(pos->member.next, typeof(*pos), member))  
// 这个和上个遍历删除节点的函数类似。多了个临时变量n,  // 所以可以防止pos在遍历时,被删除出现的错误。  #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))  
// 函数功能同上面那个,只是遍历是从某个节点开始  #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))  
// 函数功能同上面那个,只是遍历是从某个节点的下个节点开始  #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))  
// 同上个函数,只是从尾部开始  #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))  
0 0
原创粉丝点击