本文会记录一些linux内核实现中使用到的一些小技巧,工具等等,会根据学习进度不定时更新本文......

双向循环链表

  第一个想写的是linux的双向循环链表(写这个的原因是因为最近学习epoll的内核代码实现,进而需要了解linux的等待队列,这其中也用到了双向循环链表,稍后也会分析linux的等待队列)

 linux的双向循环链表之于传统的双向循环链表,其优点是:将其从具体的数据结构中提取出来构成一种通用的循环链表实现,在linux中表现形式为:

 
  
  1. struct list_head { 
  2.         struct list_head * prev , * next; 

其常见的使用方法为:若要定义某种特定类型的数据结构的双向循环链表,只需要在结构中包含该struct list_head ,就可以将各个对象链接起来;当对数据结构进行操作(增,删……)的时候,只需要设计针对struct list_head这种通用结构的通用接口即可,不用针对每种具体对象设计不同的接口

典型的结构如下(暂时不会作图,先盗用了等待队列的实现图):

 

一般实现一个具体对象的双向循环链表的时候会有一个头节点,只含有struct list_head(假设为struct list_head head;),没有具体对象的信息,若要判断是否队列为空,则只需判断head->prev==head   (head->netx == head);具体实现如下:

 
  
  1. static inline int list_empty(const struct list_head * head){ 
  2.         return head->next ==  head; 

另一个比较很重要的操作就是通过list_head成员获取宿主对象的指针了,这个主要用到了宏:offsetof,container_of

 
  
  1. #define offsetof(s,m) (size_t)&(((s *)0)->m) 
  2. //s为结构体,m为结构体中的成员,将地址0强制转换为对应的结构体类型,
  3. //而成员m所在的地址减去结构体起始位置即为m在结构体中偏移量,
  4. //此处基地址为0,则成员m所在地址即为偏移量 
  5.  
  6. #define container_of(ptr, type, member) ({             \ 
  7.          const typeof( ((type *)0)->member ) *__mptr = (ptr);     \ 
  8.          (type *)( (char *)__mptr - offsetof(type,member) );}) 
  9. //作用:获得某成员所在结构体的入口地址 
  10. //实现:type为结构题的类型,member为结构体中的一个成员,
  11. //   ptr为指向成员member所属变量类型的一个变量的指针
  12. //     故技重施,(type *)0将地址0强制转换为type结构体类型的地址,
  13. //   typeof( ((type *)0->member)获得了member的类型,
  14. //   接着typeof( ((type *)0->member) * __mptr申明了一个该类型的变量并初始化为ptr的地址;
  15. //   (char *)__mptr将刚刚的地址转换为char* ,offsetof计算出了member成员在结构体中的偏移量,
  16. //   __mptr减去这个偏移刚好就是结构体的起始地址,然后用(type *)强制转换为相应的指针 


有关等待队列的数据结构稍后补充……