一、前言 #
内核里面链表的实现有很多种
名称 | 用途 | 定义 |
---|---|---|
list_head | 双向链表 | include/linux/types.h |
hlist_head & hlist_node | 带头节点的双向链表 | include/linux/types.h |
hlist_bl_head & hlist_bl_node | 带自旋锁和头节点的双向链表 | include/linux/list_bl.h |
hlist_nulls_head & hlist_nulls_node | 使用ptr最后一位代表end的双向链表 | include/linux/list_nulls.h |
二、使用实例 #
1. 在链表中间插入 #
1void test() {
2 // 按照到期时间排序插入
3 struct TIMER *node;
4 struct TIMER *prev = NULL; // 记录要插入的节点前一个的节点
5
6 hlist_for_each_entry(node, &s_timer_list, entry) {
7 if (node->expires > timer->expires) {
8 break;
9 }
10 prev = node;
11 }
12 if (prev == NULL) {
13 // 没有前一个节点,说明要插入到第一个
14 // 1. 有节点,但是都不满足条件,插入最前面
15 // 2. 没有节点,直接插入最前面
16 hlist_add_head(&timer->entry, &s_timer_list);
17 } else {
18 // 有前一个节点,插入到前一个节点后面
19 hlist_add_behind(&timer->entry, &prev->entry);
20 }
21}
三、代码实现详解 #
3. hlist_bl_head & hlist_bl_node
带自旋锁和头节点的双向链表
#
和hlist实现差不多,定义都一样,但是在hlist_bl_head->first
中取了第一位作为自旋锁。原因自然是因为字节对齐的问题,内核里面最差也是4字节对齐,就算16位也是2字节对齐,指针的最后一位都是0,所以最后一位可以拿来作为自旋锁。下面讲一些和hlist不一样的使用
定义如下
1static inline void hlist_bl_lock(struct hlist_bl_head *b)
2{
3 bit_spin_lock(0, (unsigned long *)b);
4}
5
6static inline void hlist_bl_unlock(struct hlist_bl_head *b)
7{
8 __bit_spin_unlock(0, (unsigned long *)b);
9}
自旋锁使用hlist_bl_head
作为地址设置第0位,写的比较隐晦,实际修改的是first
,因为struct hlist_bl_head
就只有一个元素
1struct hlist_bl_head {
2 struct hlist_bl_node *first;
3};
由于first最后一位被使用了,所以所有取first的地方都要做特殊处理
1// 取first真实地址需要将锁的位排除掉
2static inline struct hlist_bl_node *hlist_bl_first(struct hlist_bl_head *h)
3{
4 return (struct hlist_bl_node *)
5 ((unsigned long)h->first & ~LIST_BL_LOCKMASK);
6}
7
8// 设置first时必须要加锁的状态
9static inline void hlist_bl_set_first(struct hlist_bl_head *h,
10 struct hlist_bl_node *n)
11{
12 LIST_BL_BUG_ON((unsigned long)n & LIST_BL_LOCKMASK);
13 LIST_BL_BUG_ON(((unsigned long)h->first & LIST_BL_LOCKMASK) !=
14 LIST_BL_LOCKMASK);
15 h->first = (struct hlist_bl_node *)((unsigned long)n | LIST_BL_LOCKMASK);
16}