| 
	#define rcu_dereference(p) ({ typeof(p) _________p1 = ACCESS_ONCE(p);
 smp_read_barrier_depends();
 (_________p1);
 })
 #define rcu_assign_pointer(p, v)
 ({
 if (!__builtin_constant_p(v) || ((v) != NULL))
 smp_wmb();
 (p) = (v);
 })
 它们的实现也很简单.因为它们本身都是原子操作。只是为了cache一致性,插上了内存屏障。可以让其它的读者/写者可以看到保护指针的最新值.
 
	l synchronize_rcu()
 
	synchronize_rcu()在RCU中是一个最核心的函数,它用来等待之前的读者全部退出.我们后面的大部份分析也是围绕着它而进行.实现如下: void synchronize_rcu(void)
 {
 struct rcu_synchronize rcu;
 init_completion(&rcu.completion);
 
 
	/* Will wake me after RCU finished */ call_rcu(&rcu.head, wakeme_after_rcu);
 
 /* Wait for it */
 wait_for_completion(&rcu.completion);
 }
 我们可以看到,它初始化了一个本地变量,它的类型为struct rcu_synchronize.调用call_rcu()之后.一直等待条件变量rcu.competion的满足。
 
 在这里看到了RCU的另一个核心API,它就是call_run()。它的定义如下:
 void call_rcu(struct rcu_head *head,  void (*func)(struct rcu_head *rcu))
 {
 unsigned long flags;
 struct rcu_data *rdp;
 
 head->func = func;
 head->next = NULL;
 local_irq_save(flags);
 rdp = &__get_cpu_var(rcu_data);
 *rdp->nxttail = head;
 rdp->nxttail = &head->next;
 if (unlikely(++rdp->qlen > qhimark)) {
 rdp->blimit = INT_MAX;
 force_quiescent_state(rdp, &rcu_ctrlblk);
 }
 local_irq_restore(flags);
 }
 该函数也很简单,就是将参数传入的回调函数fun赋值给一个struct rcu_head变量,再将这个struct rcu_head加在了per_cpu变量rcu_data的nxttail 链表上。
 rcu_data定义如下,是个每cpu变量:
 DEFINE_PER_CPU(struct rcu_data, rcu_data) = { 0L };
 接着我们看下call_rcu注册的函数,我们也可以看到,在synchronize_rcu()中,传入call_rcu的函数为wakeme_after_rcu(),其实现如下:
 static void wakeme_after_rcu(struct rcu_head *head)
 {
 struct rcu_synchronize *rcu;
 
 rcu = container_of(head, struct rcu_synchronize, head);
 complete(&rcu->completion);
 }
 我们可以看到,该函数将条件变量置真,然后唤醒了在条件变量上等待的进程。
 由此,我们可以得知,每一个CPU都有一个rcu_data.每个调用call_rcu()/synchronize_rcu()进程的进程都会将一个rcu_head都会挂到rcu_data的nxttail链表上(这个rcu_head其实就相当于这个进程在RCU机制中的体现),然后挂起。当读者都完成读操作后(经过一个grace period后)就会触发这个rcu_head上的回调函数来唤醒写者。整个过程如下图所示:
 
	
		 看到这里,也就到了问题的关键,内核是如何判断当前读者都已经完成读操作了呢(经过了一个grace period)?又是由谁来触发这个回调函数wakeme_after_rcu呢?下一小节再来分析。 
	从RCU的初始化说起 
	那究竟怎么去判断当前的读者已经操作完了呢?我们在之前看到,不是读者在调用rcu_read_lock()的时候要禁止抢占么?因此,我们只需要判断所有的CPU都进过了一次上下文切换,就说明所有读者已经退出了。要彻底弄清楚这个问题,我们得从RCU的初始化说起。RCU的初始化开始于start_kernel()-->rcu_init()。而其主要是对每个cpu调用了rcu_online_cpu函数。
 
	l rcu_online_cpu
 (编辑:南平站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |