linux deepin内核头文件解析(二)——WRITE_ONCE函数和list.h
前文书道:linux deepin内核头文件解析(一)——list.h前文代码static inline void __list_add(struct list_head *new,struct list_head *prev,struct list_head *next){if (!__list_add_valid(new, prev, n...
前文书道:linux deepin内核头文件解析(一)——list.h
前文代码
static inline void __list_add(struct list_head *new,
struct list_head *prev,
struct list_head *next)
{
if (!__list_add_valid(new, prev, next))
return;
next->prev = new;
new->next = next;
new->prev = prev;
WRITE_ONCE(prev->next, new);
}
出现了函数WRITE_ONCE()来将prev->next和new链接,现在来仔细研究WRITE_ONCE函数。
函数定义在文件include/linux/compile.h
#define WRITE_ONCE(x, val) \
({ \
union { typeof(x) __val; char __c[1]; } __u = \
{ .__val = (__force typeof(x)) (val) }; \
__write_once_size(&(x), __u.__c, sizeof(x)); \
__u.__val; \
})
c源码为了高效率使用了宏定义的写法,现在为了提高可读性我来改写代码(降低了效率):
template<class T>
void WRITE_ONCE(T x, T var) {
union {
typeof(x) __val;
char __c[1];
} __u = { .__val = (__force typeof(x)) (val) };
__write_once_size(&(x), __u.__c, sizeof(x));
__u.__val;
}
这里居然又出现了函数__write_once_size(),追根溯源把,转到__write_once_size的定义:
static __always_inline void __write_once_size(volatile void *p, void *res, int size)
{
switch (size) {
case 1: *(volatile __u8 *)p = *(__u8 *)res; break;
case 2: *(volatile __u16 *)p = *(__u16 *)res; break;
case 4: *(volatile __u32 *)p = *(__u32 *)res; break;
case 8: *(volatile __u64 *)p = *(__u64 *)res; break;
default:
barrier();
__builtin_memcpy((void *)p, (const void *)res, size);
barrier();
}
}
这里很容易可以看出函数__write_once_size(void *a, void *b, int size)目的是为了安全地使指针b赋值给指针a,而且不会出现指针类型错误,而且用了volatile关键字保证编译器不回因为优化而忽略这些代码,这很鲁棒!
所以语句
WRITE_ONCE(prev->next, new);
的真正作用就是安全地将prev->next指向new!
下一个函数
static inline void list_add(struct list_head *new, struct list_head *head)
{
__list_add(new, head, head->next);
}
作用一目了然,也是链表添加节点,但参数只有两个,新节点地址和链表头emmmm,果然懒惰是人类进步的驱动力,双向链表嘛,不严谨的说每一个节点都可以是头节点。
下一个函数是往尾节点后添加新节点:
static inline void list_add_tail(struct list_head *new, struct list_head *head)
{
__list_add(new, head->prev, head);
}
下面进入节点的删除环节,注释是这么说的:
/*
* Delete a list entry by making the prev/next entries
* point to each other.
*
* 通过给出prev和next来删除他们互相指向的入口(节点)
* This is only for internal list manipulation where we know
* the prev/next entries already!
* 这是链表的内部操作,必须在我们已经知晓prev和next入口的前提下
*/
static inline void __list_del(struct list_head * prev, struct list_head * next)
{
next->prev = prev;
WRITE_ONCE(prev->next, next);
}
上图!
我执行代码__list_del(&n, &(n+2));
结果是什么呢,我把节点(n+2)的prev指向了节点n,把节点n的next指向了节点(n+2),现在节点(n+1)已经被孤立出链表的逻辑结构了!无论节点(n+1)还在不在内存里!
如下:
static inline void __list_del_entry(struct list_head *entry)
{
if (!__list_del_entry_valid(entry))
return;
__list_del(entry->prev, entry->next);
}
__list_del_entry()函数传入一个节点(入口)地址,其中又出现新函数__list_del_entry_valid(),定位它:
static inline bool __list_del_entry_valid(struct list_head *entry)
{
return true;
}
emmm,也就是说,如果传入的entry参数是list_head类型的指针,那么返回true,那上面的代码调用这个函数其实是确保安全性,保证不会传入野指针。
下一个函数
static inline void list_del(struct list_head *entry)
{
__list_del_entry(entry);
entry->next = LIST_POISON1;
entry->prev = LIST_POISON2;
}
这个函数其实是对__list_del()函数的完善,__list_del函数虽然将节点entry孤立出链表体系之外,但是entry的prev和next依然指向链表,斩草要除根,这里的LIST_POISON1和LIST_POISON2定义在poison.h里,这里不展开讲,可以理解为在程序不会占用到的内存地址,把entry的头和脚都放逐到了程序之外,这才是斩草除根嘛。
未完,下面还有list.h的查找和更改部分,涉及到了linux内核设计最精彩的算法部分,下章再说
更多推荐
所有评论(0)