我想知道 linux 内核链表的线程安全性。假设一个线程将项目添加到列表,而另一个线程读取列表。当然,在修改列表时我需要锁定/互斥锁。但是使用 list_empty
检查列表是否为空是否已经需要锁?
查看list_empty
的来源:
static inline int list_empty(const struct list_head *head)
{
return READ_ONCE(head->next) == head;
}
我们看到它使用了 READ_ONCE
。据我所知,这会阻止某些编译器优化,并且对于正确对齐/大小的变量也是原子的。因为没有内存屏障,所以我无法对其他内存访问进行任何排序,但在其他线程添加项目后,列表最终会变得不为空。所以我相信锁对于 list_empty
来说并不是绝对必要的。
我问是因为我想使用 list_empty
作为 wait_event_interruptible
的条件。
最佳答案
让我们使用一些伪汇编器。
为了读取head->next
,它需要读取head
。
mov ax, head (1)
然后它获取 next 的偏移量并得到 next
:
mov cx, [ax+offset(next)] (2)
现在它将它与 head
进行比较:
cmp ax,cx (3)
如果我们假设 head
不会改变并且不为空(例如全局变量),那么步骤 (2) 是原子的并且不需要锁。但是,如果 head
可以 更改,则第 2 步可能会获取错误数据,因为 ax
可能不再有效,例如因为调度程序暂停了步骤1之后的任务,这三个步骤必须在锁定状态下执行。
关于在没有锁的情况下检查 list_empty,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60720378/