优雅地使用链表
struct list_head {
struct list_head *next, *prev;
};
#define LIST_HEAD_INIT(name) { &(name), &(name) }
2) 链表使用者定义:
struct user_t {
data domain;
struct list_head node;
};
struct list_head g_user_list = LIST_HEAD_INIT(g_user_list);
#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 user_t* next = container_of(&(g_user_list.next->node), struct user_t, node);
这里用到了container_of,container_of又用到了offsetof。offsetof是通过将结构体起始地址强制对齐到0来计算出node和起始地址的偏移offset;而container_of在node地址基础上减去offset得到user_t结构体的地址并返回user_t。
Linux的内核链表不同与以上我们定义的链表,如果我们仔细观察,会发现在我们定义的链表结点中,包含了一个指向相同类型的结构体指针,这个指针存放得是直接前驱或直接后继结点的地址。Linux的内核链表设计得很巧妙,在Smack技术中涉及了两类链表分别是普通的链表list和受rcu锁保护的rculist
1. 首先我们来看普通的list
我们可以打开终端 ,切换到Linux源代码目录,cd /include/linux/, 这里有list.h头文件,链表的定义和操作就在这里了。我们先不去看里面的内容,我们先看看Smack中很重要的一个结构体smack_rule,它在 /smack/smack.h中定义
struct smack_rule {
struct list_head list;
char *smk_subject;
char *smk_object;
int smk_access;
};
注意这个结构体第一个成员是 list,它是list_head结构体类型,而struct list_head { struct list_head *prev,struct list_head *next },
struct list_head list,这里的list包含了两个指向自身类型的指针变量。而如果你语言基础不错的话,应该知道结构体第一个成员变量地址就是这个结构体的地址,那于是可以将另一个结构体的list赋给这个结构体的list,这样链表就串起来了。
如下图:
这其实等价于:
struct smack_rule sr1,sr2;
sr1.list.next=&sr2.list sr2.list=&sr1.list
Linux内核链表是通过镶嵌在结点内部list_head型的变量而组成的双向循环链表
那链表的操作,如建立链表,插入结点,遍历链表呢?这些操作都被封装在list.h这个头文件里了
如初始化链表 LIST_HEAD,遍历链表 list_for_each_entry, 获取链表结点的首地址 list_entry, 向链表加入结点 list_add等
Smack代码主要就是这几个操作,而下面我们选择2个操作来分析
第一个操作LIST_HEAD,它是一个宏,#define LIST_HEAD(name) LIST_HEAD_INIT(name)
#define LIST_HEAD_INIT(name) { &(name),&(name) }
这里定义的很清楚,也就是当使用LIST_HEAD(name),实际上是构造了一个空的双向循环链表,它只有一个指向自身的头结点。
第二个操作list_for_each_entry,它的定义是:
#define list_for_each_entry(pos,head,member)
/*代码省略*/
这个宏用来遍历链表
使用例子,仔细琢磨吧
#include <stdio.h>
struct list_head {
struct list_head *next, *prev;
};
#define list_for_each(pos, head) \
for (pos = (head)->next; pos != (head); pos = pos->next)
#define list_for_each_safe(pos, n, head) \
for (pos = (head)->next, n = pos->next; pos != (head); pos = n, n = pos->next)
void INIT_LIST_HEAD(struct list_head *list);
void list_add(struct list_head *node, struct list_head *head);
void list_del(struct list_head *entry);
int list_empty(const struct list_head *head);
void INIT_LIST_HEAD(struct list_head *list) {
list->next = list;
list->prev = list;
}
void list_add(struct list_head *node, struct list_head *head) {
node->next = head;
node->prev = head->prev;
head->prev->next = node;
head->prev = node;
}
void list_del(struct list_head *entry) {
entry->next->prev = entry->prev;
entry->prev->next = entry->next;
entry->next = 0;
entry->prev = 0;
}
int list_empty(const struct list_head *head) {
return head->next == head;
}
typedef struct {
struct list_head list;
int v;
} data;
int main() {
struct list_head list;
data d1, d2, d3;
d1.v = 1;
d2.v = 2;
d3.v = 3;
INIT_LIST_HEAD(&list);
list_add((struct list_head *) &d1, &list);
list_add((struct list_head *) &d2, &list);
list_add((struct list_head *) &d3, &list);
struct list_head* p = NULL;
struct list_head* n = NULL;
list_for_each_safe(p, n, &list) {
printf("%d\n", ((data *)p)->v);
}
return 0;
}
1009

被折叠的 条评论
为什么被折叠?



