linux内核中有一个通用的双向循环list链表
为什么称他为通用的呢?首先我们还是来看一下内核中list中的结点是怎样定义的位于文件../list's中:
从此结构可以看出没有数据成员。
先看一下实际运用场合:
从上述例子:
疑问:是怎么访问结构体中的其他数据成员呢?怎么知道结构体的起始地址呢?
解决:通过下述的接口得出该结构体指针在结构体中的位置,从而得出结构体的首地址,有了起始位置,我们就可以访问其他数据成员。
/*解析offsetof(TYPE, MEMBER)宏定义*/
我们定义:
input handle * stInputhandle1;
再定义:
strict input handle * pstInputHandle1 = &stInputhandle1;
我们现在反向推算:
(size)&((type *)(0)->member);
=>>
((type *)(0)->member);
=>>
((type *)(0)->member - 0);
=>>
((0)->member - 0);
=>>
(pstInputHandle1 >member -pstInputHandle1 );
知道偏移地址,如何知道首地址呢?
#define container_of(ptr, type, member) ({ \
consttypeof(((type *)0)->member)*__mptr = (ptr); \
(type *)((char *)__mptr - offsetof(type,member)); })
在linux内中,提供了下方法,
对list进行操作插入到链表头:
void list add(struct list_head *new, struct list_head*head);
插入到链表尾:
void list_add_tail(struct list_head *new, structlist_head *head);
删除链表节点:
void list_del(struct list_head *entry);
将节点移动到另一链表:
void list_move(struct list_head *list, structlist_head *head);
将节点移动到链表尾:
void list_move_tail(struct list_head *list,structlist_head *head);
判断链表是否为空,返回1为空,0非空
int list_empty(struct list_head *head);
把两个链表拼接起来:
void list_splice(struct list_head *list, structlist_head *head);
取得节点指针:
#define list_entry(ptr, type, member)\
((type *)((char *)(ptr)-(unsignedlong)(&((type *)0)->member)))
遍历链表中每个节点:
#define list_for_each(pos, head)\
for (pos = (head)->next,prefetch(pos->next); pos != (head); \
pos = pos->next,prefetch(pos->next))
逆向循环链表中每个节点:
#define list_for_each_prev(pos, head)\
for (pos = (head)->prev,prefetch(pos->prev); pos != (head);\
pos = pos->prev, prefetch(pos->prev))
测试例子:
上面的这个列子,只做了一个简单的操作,就是自己实现了一个双向循环链表的插入操作。在主函数中,先是插入元素,然后打印元素。当然内核还提供了好多关于list的操作方法,读者可以自己去挖掘。