关于LKM中从RCU保护列表返回指针的疑问
RCU读端解锁后持有列表元素指针的有效性问题
struct a { // ... struct list_head some_list; // ... }; struct e { struct list_head next; // ... }; static void *traverse(struct a *a) { struct e *e; rcu_read_lock(); list_for_each_entry_rcu(e, &a->some_list, next) { if (...) { rcu_read_unlock(); return e; } } rcu_read_unlock(); return NULL; }
在上述traverse函数中,我们先获取rcu_read_lock,遍历RCU保护的列表直到满足某个条件,之后立即调用rcu_read_unlock并返回匹配到的e指针。
我的核心疑问是:解锁RCU读端临界区后仍持有这个列表元素指针,如果写端此时删除该元素,这个指针是否会失效?
我原以为RCU保护的指针仅在rcu_read_lock()与rcu_read_unlock()之间的读端临界区内有效,这个理解是否正确?
补充说明:调用traverse时未持有任何其他锁。
核心结论
你的理解存在偏差,RCU的核心保障并非"指针仅在临界区内有效",而是读端临界区能安全访问被RCU保护的对象,且写端在删除对象后会等待所有活跃的读端临界区结束,才会真正释放对象内存。
详细解释
RCU的内存释放逻辑
当写端要删除RCU保护的列表元素时,不会直接释放内存:先将元素从列表中移除,接着调用synchronize_rcu()(或异步版本的call_rcu())等待所有当前正在运行的RCU读端临界区完成,最后才会释放该元素的内存。指针有效性的本质
你返回的e指针指向的内存,只要写端还没完成"移除+等待+释放"的完整流程,就仍然是有效的。但存在明确风险:- 解锁后,RCU机制不再为该指针提供保护,无法阻止写端后续完成内存释放操作;
- 如果写端已经完成了完整的删除+释放流程,返回的指针会直接变成野指针,访问会触发未定义行为。
当前代码的问题
这段代码的隐患在于,返回指针后没有任何机制阻止写端释放对应内存。调用traverse的上层代码若直接使用返回的指针,可能会遭遇内存已被释放的情况。
正确处理方式
若需要在RCU读端临界区外使用该指针,必须通过以下方式保障安全:
- 写端删除元素时,不直接释放内存,改用引用计数等其他机制管理对象生命周期;
- 上层代码拿到指针后,立即通过自旋锁、增加引用计数等方式,将其转为非RCU保护的有效引用,确保内存不会被释放。
内容的提问来源于stack exchange,提问作者k1r1t0
相关产品推荐
相关产品推荐

