1.内核链表的定义
1)下面是链表结构体定义,由定义可知为双向链表
2)通过链表命名可知该链表没有头结节(链表头),没有头节点意味着每一也都是头节点。
3)由于没有头节点,该链表又实现循环链表功能。
struct list_head {
struct list_head *next, *prev;
};
2.链表初始化
链表使用首先要初始化,下面初始化代码。
static inline void INIT_LIST_HEAD(struct list_head *list)
{
list->next = list;
list->prev = list;
}
3.链表的添加
1).链表的添加分为两种,一种头部添加,另一种尾部添加。
2).list_add函数实现头部插入节点, 所以参数至少具有两个参数一个插入节点,一个是头部。
3).头部添加实现栈的功能(先进后出), 尾部插入实现是队列功能(先进先出)。
static inline void list_add(struct list_head *new, struct list_head *head)
{
__list_add(new, head, head->next);
}
static inline void __list_add(struct list_head *new,
struct list_head *prev,
struct list_head *next)
{
next->prev = new;
new->next = next;
new->prev = prev;
prev->next = new;
}
4.链表的删除
1).list_del函数实现节点删除,由于是双向链表,所以节点删除函数一个参数足以,即删除节点。
2).注意此函数只是删除该节点,并不释放内存。
static inline void list_del(struct list_head *entry)
{
__list_del(entry->prev, entry->next);
entry->next = LIST_POISON1;
entry->prev = LIST_POISON2;
}
static inline void __list_del(struct list_head * prev, struct list_head * next)
{
next->prev = prev;
prev->next = next;
}
5.获取包含list_head结构体的元素
1).获取包含list_head结构体的元素
#define list_entry(ptr, type, member) \
container_of(ptr, type, member)
#define list_entry(ptr, type, member) \
container_of(ptr, type, member)
2).寻找包含者,首先知道包含者大小,另外被包含者名称,被被包含者所处位置。 所以下面函数实际包含三个参数。
ptr:被包含者所处位置
member:被包含者名称
type:包含者大小
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
6.链表应用举例----等待队列
1)队列初始化
#define init_waitqueue_head(q) \
do { \
static struct lock_class_key __key; \
\
__init_waitqueue_head((q), &__key); \
} while (0)
void __init_waitqueue_head(wait_queue_head_t *q, struct lock_class_key *key)
{
spin_lock_init(&q->lock);
lockdep_set_class(&q->lock, key);
INIT_LIST_HEAD(&q->task_list);
}
2)队列入队操作
1)可能要问队列的入队操作为先进先出,应该采用list_add_tail函数。因为使用add_wait_queue函数时,不需要完全队列操作。
2)然而也提够__add_wait_queue_tail函数,该函数实现真正队列操作(先进先出)。
void add_wait_queue(wait_queue_head_t *q, wait_queue_t *wait)
{
unsigned long flags;
wait->flags &= ~WQ_FLAG_EXCLUSIVE;
spin_lock_irqsave(&q->lock, flags);
__add_wait_queue(q, wait);
spin_unlock_irqrestore(&q->lock, flags);
}
static inline void __add_wait_queue(wait_queue_head_t *head, wait_queue_t *new)
{
list_add(&new->task_list, &head->task_list);
}
3)队列出队操作
1)可能要问上面不是说删除一个参数就够了,为什么这里两个?主要原因链表属于共享数据,需要加锁保护,而这个锁保护整个链表。
2)顺便分析一下这个锁spin_lock_irqsave,
此锁为自旋锁,自旋锁目的是保护多处理器对数据访问与操作。
另外此锁需关闭中断,意味可能某处中断处理程序也会操作该结构。
最后保存中断状态,解锁时候恢复中断状态。
void remove_wait_queue(wait_queue_head_t *q, wait_queue_t *wait)
{
unsigned long flags;
spin_lock_irqsave(&q->lock, flags);
__remove_wait_queue(q, wait);
spin_unlock_irqrestore(&q->lock, flags);
}
static inline void __remove_wait_queue(wait_queue_head_t *head,
wait_queue_t *old)
{
list_del(&old->task_list);
}