内核链表
内核链表是双向循环链表;链表结点基本构成:数据、前向指针、后向指针;不同于传统链表,前向指针和后向指针都是指向指针域没有指向结点中的数据,想要读取一个结点的数据是通过该结点的指针域拿到结点的数据;
/*************************************************
*filename:mylist
*function:creat kernel list
*creater:dongry
*data:2019-04-02
*************************************************/
#include <linux/module.h>
#include <linux/list.h>
#include <linux/init.h>
/*module statement*/
MODULE_LICENSE("GPL");
int a,b;
extern int add(int a,int b);
/*reload parameter a and b*/
module_param(a,int,S_IRUGO | S_IWUSR);
module_param(b,int,S_IRUGO | S_IWUSR);
/*defines a struct*/
struct information
{
int high;
char *sex;
char *name;
struct list_head list;
};
/*statement struct variable*/
struct information child1,child2,child3;
struct information *tmp;
struct list_head information_head;
struct list_head *pos;
static int mylist_init(void)
{
/*creat list and init list*/
INIT_LIST_HEAD(&information_head);
child1.high=160;
child1.sex="girl";
child1.name="lyf";
/*in list tail insert node*/
list_add_tail(&(child1.list),&(information_head));
child2.high=105;
child2.sex="gay";
child2.name="cl";
/*in list top insert node*/
list_add(&(child2.list),&(information_head));
child3.high=175;
child3.sex="girl";
child3.name="bbh";
/*in list tail insert node*/
list_add_tail(&(child3.list),&(information_head));
/*list ergodic*/
list_for_each(pos,&information_head)
{
/*get list pointer's node*/
tmp=list_entry(pos,struct information,list);
/*printk srutct member*/
printk("High is %d,child sex is %s,Name is %s\n",tmp->high,tmp->sex,tmp->name);
}
return 0;
}
static void mylist_exit(void)
{
/*delete list node*/
list_del(&(child1.list));
list_del(&(child2.list));
list_del(&(child3.list));
}
module_init(mylist_init);
module_exit(mylist_exit);
1 分析遍历链表与取出结点
/*list ergodic*/
list_for_each(pos,&information_head)
{
/*get list pointer's node*/
tmp=list_entry(pos,struct information,list);
/*printk srutct member*/
printk("High is %d,child sex is %s,Name is %s\n",tmp->high,tmp->sex,tmp->name);
}
源代码
#define list_for_each(pos, head) for(pos=(head)->next;prefetch(pos->next),pos!=(head);pos=pos->next) #define list_entry(ptr,type,member) container_of(ptr,type,member) #define container_of(ptr,type,member) \
( \
{ \
const typeof(((type*)0)->member)*__mptr=(ptr); \
(type*)((char*)__mptr - offsetof(type, member)); \
} \
)
1 关键字:typeof
1 将参数写入typeof有两种方式:表达式或类型;下面是一个使用表达式的例子。
typeof (x[0](1))
假设x是一个函数指针数组,就可以到这个函数的返回值的类型了;
下面是一个类型名做参数的例子:
typeof(int *) m,n; 等价于 int *m,*n;
2 如果将typeof用于表达式,则该表达式不会执行。只会得到该表达式的类型。以下示例声明了int类型的var变量,因为表达式func()是int类型的。由于表达式不会被执行,所以不会调用func函数。
extern int func(int a,int b);
typeof(func()) var;
3 下面是两个等效声明,用于声明int类型的变量a。
typeof(int) a; /*int类型*/
typeof('b') a; /* GCC中这个表达式的类型是int(自动提升为int)。
注意typeof(char)和typeof('b')得到的结果是不一样的,可以用sizeof()可以看出来。
如果编写头文件,并且包含ISO C兼容的话,最好是用双下划线的形式:__typeof__;typeof和typedef很像,事实上,只要能用typedef的地方就可以用typeof。
4 使用typeof的例子:
typeof (*x) y; /*把y定义成x指向的数据类型*/
typeof (*x) y[4]; /*把y定义成x指向数据类型的数组*/
typeof (typeof(char *)[4]) y; /*把y定义为一个字符指针数组*/ 等价于 char *y[4];
5 宏定义使用例子:
#define pointer(T) typeof(T *)
#define array(T,N) typeof(T [N])
声明可以写为如下方式:
array (pointer(char),4) y; /*底线处:有4个指针的数组类型*/
6 使用typeof的声明示例
typeof(int *) a,b; /* 声明整型指针a, b */ 等价于 int *a,*b;
typeof(int) *a,b;/* 声明整型指针a,整型b*/ 等价于 int *a,b;
typeof(int [10]) a1, a2;/* 声明整型数组a,b */ 等价于 int a[10],b[10];
7 使用typeof的声明限制
typeof构造中的类型名不能包含存储类说明符,如extern或static。不过允许包含类型限定符,如const或volatile。例如,下列代码是无效的,因为它在typeof构造中声明了extern:
typeof(extern int) a; /*错误*/
使用外部链接来声明标识符b是有效的,表示一个int类型的对象。
extern typeof(int) b;
声明一个使用const限定字符的char类型指针,表示指针p不能被修改
typeof(char * const) p;
8 在宏声明中使用typeof
typeof构造的主要应用是用在宏定义中。可以使用typeof关键字来引用宏参数的类型。因此,在没有将类型名明确指定为宏实参的情况下,构造带所需类型的对象是可以的。
下面是一个交换两个变量的值的宏定义:
#define SWAP(a,b) {\
typeof(a) _t=a;\
a=b;\
b=_t;}
这个宏可以交换所有基本数据类型的变量(整数,字符,结构等)
https://gcc.gnu.org/onlinedocs/gcc/Typeof.html
2 #define list_entry(ptr,type,member) container_of(ptr,type,member)
const typeof(((type*)0)->member)*__mptr=(ptr); 带入参数
#define list_entry(pos,struct information,list) container_of(pos,struct information,list)
const typeof(((struct information*)0)->list)*__mptr=(pos);
以结构体struct information声明的child1为例,进行分析,假如child1的结构体成员内存地址如下(内存地址为随机编写,没有参考性)。
const typeof(((struct information*)0)->list)*__mptr=(pos);这句代码的作用是声明一个指针__mptr。typeof的作用是获取变量的类型,所以__mptr的类型就是struct list_head(结合内核列表读取结点的数据是通过结点的指针域,所以__mptr的类型要指向结点的指针域)。然后把pos的值赋给它,其实就是两个指针指向同一块内存。指向0x804a00c。
3 (type*)((char*)__mptr - offsetof(type, member))
offsetof源码:#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) 带入参数
#define offsetof(struct information, list) ((size_t) &((struct information *)0)->list)
(struct information*)((char*)__mptr - offsetof(struct information,list))
(struct information*)0)这代码的意思是把information结构体的起始地址强制制定为0,那么list的地址就是相对0地址的偏移量,然后取址,在上图中就是0x000000c。
(char)__mptr这句话的意思:把这个指针强制类型转换为(char),也就是这个指针现在是字节指针了。因为内存存储的最基本单位都是字节。这样的转换可以用来做加减法十分方便。
综上所述,0x804a00c-0x000000c=0x804a000.这个地址也就是child1结构体的起始地址了,然后转换为struct information类型的指针。
4 分析#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER):
((TYPE *)0)->MEMBER 访问结构体中的成员;
&(((TYPE *)0)->MEMBER )取出成员的地址; 这个实现相当于获取到了MEMBER成员相对于其所在结构体的偏移,也就是其在对应结构体中的什么位置。
(size_t)(&(((TYPE*)0)->MEMBER))结果转换类型。巧妙之处在于将0转换成(TYPE*),结构以内存空间首地址0作为起始地址,则成员地址自然为偏移地址;