1. 为何一般用INIT_LIST_HEAD,而不用LIST_HEAD,因为链表通常需要添加一些属性,例如下面互斥属性。
INIT_LIST_HEAD(&c->infos_list);
mutex_init(&c->umount_mutex);
2.为何看到有的数据结构体含有多个list_head链表,相当增加不同的索引,将数据分类到几个链表
struct ubifs_lprops {
int free; //链表属性
int dirty;
int flags;
int lnum;
struct list_head list;
}lprops;
struct ubifs_info {
struct list_head uncat_list;
struct list_head empty_list;
.....................
}
case LPROPS_UNCAT:
list_add(&lprops->list, &c->uncat_list);
break;
case LPROPS_EMPTY:
list_add(&lprops->list, &c->empty_list);
break;
最近看linux 链表,网上很多代码的讲解,总有点知其然不知其所以然的感觉,我看了下有点自己想法,特记录如下:
我们之前学习双链表结构体一般如下
typedef struct __Node{
int data; 数据
struct __Node *pre; 指向前一个结点指针
struct __Node *next; 指向下一个结点指针
}Node;
而在linux下双链表结构体一般如下
struct list_head {
struct list_head *next, *prev;
};
struct i2c_dev {
struct list_head list;
struct i2c_adapter *adap;
struct device *dev;
};
区别很明显,第一个链表直接和结构体挂钩,链表指针指向的就是结构体本身,而第二个链表更纯粹,是单独,就是用来链接,没有特别的实际意义。显然第一种方式更容易理解,也更简单。为何linux不采用第一种方法,我猜想是为了更好的模块化。
试想一下如果采用第一种方法,不同的数据均要建立一个新链表结构,导致每一种链表都要实现一套插入、删除、遍历等功能函数,而函数实现其实都是一样,这样就很累赘,何不把链表单独提出来,因此有第二种方法。
让我们再看看第二种方式,它固定了双链表结构,以固定的成员变量出现(struct list_head list),不同的结构体都包含该同一类型链表,然后将链表链接起来。这也带来一个问题如何从链表这个成员变量获取该结构体中的其他成员变量(对应第一种很简单链表指针直接指向的就是结构体)???
如何从链表获取结构体变量,从而获取该结构体的真正需求的变量,例如上面例子中的struct i2c_adapter *adap;struct device *dev
linux 通过一个巧妙的函数实现了,container_of 实现通过结构中的某个变量获取结构本身的指针
#define container_of(ptr,type,member) ( {\ ---ptr指向的是type.member类型的数据,用来推算出以ptr指向的对象作为成员的结构体的起始地址
const typeof( ((type*)0)->member ) *__mptr=(ptr);\ ---typeof 是取变量的类型,例如 int a; typeof(a) b; //typeof(a) b 等同 int b
(type*)( (char*)__mptr - offsetof(type,member) );} )
#define offsetof(TYPE, MEMBER) ((size_t) & ((TYPE *)0)->MEMBER )
宏功能:获得一个结构体变量成员在此结构体中的偏移量。
1. ( (TYPE *)0 ) 将零转型为TYPE类型指针;
2. ((TYPE *)0)->MEMBER 访问结构中的数据成员;
3. &( ( (TYPE *)0 )->MEMBER )取出数据成员的地址,即相对于0的偏移量,要的就这个;
4.(size_t)(&(((TYPE*)0)->MEMBER))结果转换类型,size_t应该最终为unsigned int类型。
struct i2c_dev i2c_dev_test;
container_of(&i2c_devtest.list, struct i2c_dev, list )----通过i2c_devtest.list成员变量,返回i2c_dev_test的结构体的地址(指针)
........................................................................................
static LIST_HEAD(i2c_dev_list);
#define LIST_HEAD(name) /
struct list_head name = { &name, &name }
struct i2c_dev *i2c_dev;
list_for_each_entry(i2c_dev, &i2c_dev_list, list) //从链表头i2c_dev_list开始向后循环,一直循环到链表尾, 对链表中对应的所有的struct i2c_dev结构体变量轮询一遍处理
#define list_for_each_entry(pos, head, member) \ 链表是由pos.member为节点连接的。head是链表的头。搜寻整个链表,对pos类型的数据进行处理。
for (pos = list_entry((head)->next, typeof(*pos), member); \
prefetch(pos->member.next), &pos->member != (head); \
pos = list_entry(pos->member.next, typeof(*pos), member))
#define list_entry(ptr,type,member)\
container_of(ptr,type,member)
双链表是循环的条件:尾结点的后指针指向头指针(头结点), 头指针的前指针指向尾结点