本文会记录一些linux内核实现中使用到的一些小技巧,工具等等,会根据学习进度不定时更新本文......
双向循环链表
第一个想写的是linux的双向循环链表(写这个的原因是因为最近学习epoll的内核代码实现,进而需要了解linux的等待队列,这其中也用到了双向循环链表,稍后也会分析linux的等待队列)
linux的双向循环链表之于传统的双向循环链表,其优点是:将其从具体的数据结构中提取出来构成一种通用的循环链表实现,在linux中表现形式为:
- struct list_head {
- struct list_head * prev , * next;
- }
其常见的使用方法为:若要定义某种特定类型的数据结构的双向循环链表,只需要在结构中包含该struct list_head ,就可以将各个对象链接起来;当对数据结构进行操作(增,删……)的时候,只需要设计针对struct list_head这种通用结构的通用接口即可,不用针对每种具体对象设计不同的接口
典型的结构如下(暂时不会作图,先盗用了等待队列的实现图):
一般实现一个具体对象的双向循环链表的时候会有一个头节点,只含有struct list_head(假设为struct list_head head;),没有具体对象的信息,若要判断是否队列为空,则只需判断head->prev==head (head->netx == head);具体实现如下:
- static inline int list_empty(const struct list_head * head){
- return head->next == head;
- }
另一个比较很重要的操作就是通过list_head成员获取宿主对象的指针了,这个主要用到了宏:offsetof,container_of
- #define offsetof(s,m) (size_t)&(((s *)0)->m)
- //s为结构体,m为结构体中的成员,将地址0强制转换为对应的结构体类型,
- //而成员m所在的地址减去结构体起始位置即为m在结构体中偏移量,
- //此处基地址为0,则成员m所在地址即为偏移量
- #define container_of(ptr, type, member) ({ \
- const typeof( ((type *)0)->member ) *__mptr = (ptr); \
- (type *)( (char *)__mptr - offsetof(type,member) );})
- //作用:获得某成员所在结构体的入口地址
- //实现:type为结构题的类型,member为结构体中的一个成员,
- // ptr为指向成员member所属变量类型的一个变量的指针
- // 故技重施,(type *)0将地址0强制转换为type结构体类型的地址,
- // typeof( ((type *)0->member)获得了member的类型,
- // 接着typeof( ((type *)0->member) * __mptr申明了一个该类型的变量并初始化为ptr的地址;
- // (char *)__mptr将刚刚的地址转换为char* ,offsetof计算出了member成员在结构体中的偏移量,
- // __mptr减去这个偏移刚好就是结构体的起始地址,然后用(type *)强制转换为相应的指针
l
有关等待队列的数据结构稍后补充……