在Linux内核中实现了几种常用的数据结构包括:链表、队列、映射、二叉树。
我们参看<linux/list.h>中的定义,并知道如何使用。
2. #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
一、链表
在一般需要遍历所有数据或需要动态的加入和删除数据时选用链表比较适合,当需要随机访问数据时一般不使用链表。Linux内核中链表的实现不同于通常的设计,它不是将数据塞入链表,而是将链表节点塞入数据结构。
通常的方法:
struct fox{
unsigned long tail_length;/*尾巴长度*/
unsigned long weight;/*重量*/
struct fox *next;/*指向下一个节点*/
struct fox *prev;/*指向前一个节点*/
};
而linux中的方法为:
struct list_head{
struct list_head *next;
struct list_head *prev;
};
struct fox{
unsigned long tail_length;
unsigned long weight;
stuct list_head list;
};
我们可以通过下图看出具体的差别,通过下面两个图,我们可以清楚的看出下面的方法即Linux内核中的实现的方法更具有拓展性。然后我们使用list来构建一个双向链表,但是这个链表的指针怎么访问struct fox的成员呢?下面我们看一下Linux内核中如何实现的。
Linux中通过下面两个宏来实现:
#define container_of(ptr,type,member) ({ \
const typeof(((type*)0)->member)*__ptr=(ptr);\
(type*)((char *)__mptr-offsetof(type,member));})
#define list_entry(ptr,type,member) container_of(ptr,type,member)
通过list_entry方法,内核提供了创建、操作以及其他链表管理的各种例程,所有方法不需要知道list_head所嵌入对象的数据结构。
我们参看<linux/list.h>中的定义,并知道如何使用。
/**
* 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)
下面分析一下这个宏:
1. const typeof( ((type *)0)->member ) *__mptr = (ptr);
是定义一个__mptr指针变量,类型和member的类型一样typeof是获得一个变量的类型,((type *)0)->member 则是tpye类型中的member 变量,一般type为结构体类型,member 则为其中的变量这里的0只是作为一个临时的指针地址用,任何可以表示地址的数字都可以代替0。2. #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
是获取TYPE类型中成员MEMBER的相对偏移量,如果基址为0,那么地址&((TYPE *)0)->MEMBER转换为size_t后就是此成员的偏移量了,这里的0作为起始地址用,来计算偏移量,如果用其它数字代替offsetof得到的数值要减去这个数字才是真正的偏移量,所以这里用0是最佳的选择。
下面使用简单的程序测试这个宏是否如上面分析一样。
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#define offsetof(TYPE,MEMBER) ((size_t)&((TYPE*)0)->MEMBER)
#define container_of(ptr,type,member) ({ \
const typeof(((type*)0)->member)*__mptr=(ptr);\
(type*)((char*)__mptr-offsetof(type,member));})
struct fox{
int length;
int weight;
int member;
};
int main()
{ int len;
struct fox test;
struct fox *ptest;
test.length=3;
test.weight=20;
test.member=1;
ptest=container_of(&test.member,struct fox,member);
printf("%d\n",ptest->length);
len=offsetof(struct fox,member);
assert(len==8);
printf("%d\n",len);
return 0;
}
输出结果为
3
8