#define container_of(ptr, type, member) ({ /
const typeof( ((type *)0)->member ) *__mptr = (ptr); /
(type *)( (char *)__mptr - offsetof(type,member) );})
container_of : 实现了根据一个结构体变量中的一个域成员变量的指针来获取指向整个结构体变量的指针的功能。
【0地址指针】
(type *)0 : 好比:假设type结构存放于0其实的内存地址中(在编译的时候换算,不会执行。)
((type *)0)->member : 0指针指向的type结构中的member成员,访问结构中的数据成员;
&((type *)0)->member : 0指针指向的type结构中的member成员的地址(相对于0地址,即得到member成员地址相对type结构体的相对偏移)
typeof (((type *)0)->member) 0指针指向的type结构中的member成员的类型
(TYPE *)0,将 0 强制转换为 TYPE 型指针,记 p = (TYPE *)0,p是指向TYPE的指针,它的值是0。那么 p->MEMBER 就是 MEMBER 这个元素了,而&(p->MEMBER)就是MENBER的地址,而基地址为0,这样就巧妙的转化为了TYPE中的偏移量。再把结果强制转换为size_t型的就OK了,size_t其实也就是int。
比如&*a,会直接把a的地址求出来,不会访问*a。
&a->member,会把访问a->member的操作取消,只会计算出a->member的地址
【offsetof()】
#define OFFSET(struct_type, member) ((size_t) &((struct_type *) 0)->member)
【typeof ()】
typeof关键字是C语言中的一个新扩展,其的参数可以是两种形式:表达式或类型。
比如:
extern int foo();
typeof(foo()) var; 等价于 int var;
例子2
typeof(int *) a,b; 等价于: int *a,*b;
例子3
typeof(int *) p1,p2; /* Declares two int pointers p1, p2 */
int *p1, *p2;
typeof(int) *p3,p4;/* Declares int pointer p3 and int p4 */
int *p3, p4;
typeof(int [10]) a1, a2;/* Declares two arrays of integers */
int a1[10], a2[10];
遍历链表最简单的方法是使用list_for_each()宏。
**
* list_for_each - iterate over a list
* @pos: the &struct list_head to use as a loop cursor.
* @head: the head for your list.
*/
#define list_for_each(pos, head) \
for (pos = (head)->next; pos != (head); pos = pos->next)
可以看出,使用了辅助指针pos,pos是从第一节点开始的,并没有访问头节点,直到pos到达头节点指针head的时候结束。
//遍历整个链表,每次遍历将数据打印出来
list_for_each(pos, &score_head)//这里的pos会自动被赋新值
{
tmp = list_entry(pos, struct score, list);
printk(KERN_WARNING"num: %d, English: %d, math: %d\n", tmp->num, tmp->English, tmp->math);
}
/**
* list_for_each_safe - iterate over a list safe against removal of list entry
* @pos:the &struct list_head to use as a loop cursor.
* @n:another &struct list_head to use as temporary storage
* @head:</span>the head for your list.
*/
#define list_for_each_safe(pos, n, head) \
for (pos = (head)->next, n = pos->next; pos != (head); \
pos = n, n = pos->next)
两个遍历链表的区别
当在遍历的过程中需要删除结点时,来看一下会出现什么情况:
list_for_each():list_del(pos)将pos的前后指针指向undefined state,导致kernel panic,list_del_init(pos)将pos前后指针指向自身,导 致死循环。
list_for_each_safe():首先将pos的后指针缓存到n,处理一个流程后再赋回pos,避免了这种情况发生。
因此之遍历链表不删除结点时,可以使用list_for_each(),而当由删除结点操作时,则要使用list_for_each_safe()。
其他带safe的处理也是基于这个原因。
关于kernel panic:
它表示Linux kernel走到了一个不知道怎么走下一步的状况,一旦出现这个情况,kernel就尽可能把它此时能获取的全部信息打印出来,至于能打印出多少信息,那就看哪种情况导致它panic了。
有两种主要类型的kernel panic:hard panic(也就是Aieee信息输出),soft panic(也就是Oops信息输出)
什么能导致kernel panic:
只有加载到内核空间的驱动模块才能直接导致kernel panic,可以在系统正常的情况下,使用lsmod查看当前系统加载了哪些模块。除此之外,内建在内核里的组建(比如memory map等)也能导致panic。
详细的就不说了。
/**
* 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)
/**
* list_for_each_entry - iterate over list of given type
* @pos: the type * to use as a loop cursor.
* @head: the head for your list.
* @member: the name of the list_struct within the struct.
*/
#define list_for_each_entry(pos, head, member) \
for (pos = list_entry((head)->next, typeof(*pos), member); \
&pos->member != (head); \
pos = list_entry(pos->member.next, typeof(*pos), member))
常用的Linux内核双向链表API介绍
linux link list结构图如下:
内核双向链表的在linux内核中的位置:/include/linux/list.h
使用双向链表的过程,主要过程包括创建包含struct link_head结构的结构体(item),建立链表头,向链表中添加item(自定义数据结构,双向链表数据单元),删除链表节点,遍历链表,判空等。
1、建立自定义链表数据结构
- struct kool_list{
- int to;
- struct list_head list; //包含链表头
- int from;
- };//自定义欲链接的数据额结构,并包含双向链表结构
- struct kool_list mylist;
- INIT_LIST_HEAD(&mylist.list);//初始化一个链表表头
static LIST_HEAD(adc_host_head);//初始化一个链表头adc_host_head
第二种创建链表头和第一种的区别在于,第二种在编译的时候才会被初始化。还有一点就是链表头是独立的还是位于自定义链表数据结构中的。就好比在《例说Linux内核链表(二)》中给出的链表结构图和本文给出的结构图的区别。
3、向链表添加item
list_add(struct list_head *new, struct list_head *head);
例如:
- struct kool_list *tmp;
-
- tmp= (struct kool_list *)malloc(sizeof(struct kool_list));
- printf("enter to and from:");
- scanf("%d %d", &tmp->to, &tmp->from); //初始化数据结构
- /* add the new item 'tmp' to the list of items in mylist */
- list_add(&(tmp->list), &(mylist.list));//项链表中添加新的元素节点,tmp中的list
list_add_tail(struct list_head *new, struct list_head *head);
在链表的尾部添加一个item,和list_add的区别在于,list_add把新的item加到了链表头的后面,list_add_tail把item加到了链表头的前面。
4、遍历链表
list_for_each_entry(type *cursor, struct list_head *list, member)
这并不是一个函数,它是一个for循环,依次列出要遍历的链表,三个元素代表的意义:type *cursor代表item的指针,struct list_head *list是链表头,member是item中包含的list_head数据项。通过这三个数据可以定位到链表的每一个数据元素。其中定位原理就是结构体偏移。
例如:
- list_for_each_entry(tmp, &mylist.list, list)
- printf("to= %d from= %d\n", tmp->to, tmp->from);
这个宏可以分为两步,第一步是遍历链表,pos依次指向链表中每个item的struct list_head 结构,第二步是获取pos指向的struct list_head所在的item。这里是tmp 。
list_for_each(pos, &mylist.list){//遍历链表,pos依次指向链表的元素
tmp= list_entry(pos, struct kool_list, list);//获得包含pos节点的数据结构指针
5、删除
删除链表中的某节点,首先要使用安全遍历,然后再删除。
例如:
- list_for_each_safe(pos, q, &mylist.list){
- tmp= list_entry(pos, struct kool_list, list);
- printf("freeing item to= %d from= %d\n", tmp->to, tmp->from);
- list_del(pos);
6、链表空
int list_empty(struct list_head *head);