码迷,mamicode.com
首页 > 系统相关 > 详细

Linux RCU机制详解

时间:2016-08-12 13:35:07      阅读:317      评论:0      收藏:0      [点我收藏+]

标签:

关于rcu的几点声明:

1:RCU使用在读者多而写者少的情况.RCU和读写锁相似.但RCU的读者占锁没有任何的系统开销.写者与写写者之间必须要保持同步,且写者必须要等它之前的读者全部都退出之后才能释放之前的资源. 
2:RCU保护的是指针.这一点尤其重要.因为指针赋值是一条单指令.也就是说是一个原子操作.因它更改指针指向没必要考虑它的同步.只需要考虑cache的影响. 
3:读者是可以嵌套的.也就是说rcu_read_lock()可以嵌套调用. 
4:读者在持有rcu_read_lock()的时候,不能发生进程上下文切换.否则,因为写者需要要等待读者完成,写者进程也会一直被阻塞.

 

核心api:

对于reader,RCU的操作包括:

(1)rcu_read_lock,用来标识RCU read side临界区的开始。

(2)rcu_dereference,该接口用来获取RCU protected pointer。reader要访问RCU保护的共享数据,当然要获取RCU protected pointer,然后通过该指针进行dereference的操作。

(3)rcu_read_unlock,用来标识reader离开RCU read side临界区

对于writer,RCU的操作包括:

(1)rcu_assign_pointer。该接口被writer用来进行removal的操作,在witer完成新版本数据分配和更新之后,调用这个接口可以让RCU protected pointer指向RCU protected data。

(2)synchronize_rcu。writer端的操作可以是同步的,也就是说,完成更新操作之后,可以调用该接口函数等待所有在旧版本数据上的reader线程离开临界区,一旦从该函数返回,说明旧的共享数据没有任何引用了,可以直接进行reclaimation的操作。

(3)call_rcu。当然,某些情况下(例如在softirq context中),writer无法阻塞,这时候可以调用call_rcu接口函数,该函数仅仅是注册了callback就直接返回了,在适当的时机会调用callback函数,完成reclaimation的操作。这样的场景其实是分开removal和reclaimation的操作在两个不同的线程中:updater和reclaimer。

 

简单例子:

struct foo { 
int a; 
char b; 
long c; 
}; 
DEFINE_SPINLOCK(foo_mutex);

struct foo *gbl_foo; 
void foo_update_a(int new_a) 

struct foo *new_fp; 
struct foo *old_fp;

new_fp = kmalloc(sizeof(*new_fp), GFP_KERNEL); 
spin_lock(&foo_mutex); 
old_fp = gbl_foo; 
*new_fp = *old_fp; 
new_fp->a = new_a; 
rcu_assign_pointer(gbl_foo, new_fp); 
spin_unlock(&foo_mutex); 
synchronize_rcu(); 
kfree(old_fp); 
}

int foo_get_a(void) 

int retval;

rcu_read_lock(); 
retval = rcu_dereference(gbl_foo)->a; 
rcu_read_unlock(); 
return retval; 

如上代码所示,RCU被用来保护全局指针struct foo *gbl_foo. foo_get_a()用来从RCU保护的结构中取得gbl_foo的值.而foo_update_a()用来更新被RCU保护的gbl_foo的值. 
另外,我们思考一下,为什么要在foo_update_a()中使用自旋锁foo_mutex呢? 
假设中间没有使用自旋锁.那foo_update_a()的代码如下:

void foo_update_a(int new_a) 

struct foo *new_fp; 
struct foo *old_fp;

new_fp = kmalloc(sizeof(*new_fp), GFP_KERNEL);

old_fp = gbl_foo; 
1:------------------------- 
*new_fp = *old_fp; 
new_fp->a = new_a; 
rcu_assign_pointer(gbl_foo, new_fp);

synchronize_rcu(); 
kfree(old_fp); 

假设A进程在上图----标识处被B进程抢点.B进程也执行了goo_ipdate_a().等B执行完后,再切换回A进程.此时,A进程所持的old_fd实际上已经被B进程给释放掉了.此后A进程对old_fd的操作都是非法的.

另外,我们在上面也看到了几个有关RCU的核心API.它们为别是: 
rcu_read_lock() 
rcu_read_unlock() 
synchronize_rcu() 
rcu_assign_pointer() 
rcu_dereference() 
其中,rcu_read_lock()和rcu_read_unlock()用来保持一个读者的RCU临界区.在该临界区内不允许发生上下文切换. 
rcu_dereference():读者调用它来获得一个被RCU保护的指针. 
Rcu_assign_pointer():写者使用该函数来为被RCU保护的指针分配一个新的值.这样是为了安全从写者到读者更改其值.这个函数会返回一个新值

Linux RCU机制详解

标签:

原文地址:http://www.cnblogs.com/scottieyuyang/p/5764459.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!