加入收藏 | 设为首页 | 会员中心 | 我要投稿 PHP编程网 - 黄冈站长网 (http://www.0713zz.com/)- 数据应用、建站、人体识别、智能机器人、语音技术!
当前位置: 首页 > 运营中心 > 交互 > 正文

深入理解RCU实现

发布时间:2016-10-29 16:06:37 所属栏目:交互 来源:站长网
导读:副标题#e# 深入理解RCU实现 ——基于 内核2.6.21 RCU实现(lvyilong316) RCU(Read-Copy Update),顾名思义就是读-拷贝修改,它是基于其原理命名的。对于被RCU保护的共享数据结构, 读者不需要获得任何锁就可以访问它,但写者在访问它时首先拷贝一个副本,然

#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一致性,插上了内存屏障。可以让其它的读者/写者可以看到保护指针的最新值. 

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上的回调函数来唤醒写者。整个过程如下图所示:

深入理解RCU实现        看到这里,也就到了问题的关键,内核是如何判断当前读者都已经完成读操作了呢(经过了一个grace period)?又是由谁来触发这个回调函数wakeme_after_rcu呢?下一小节再来分析。

从RCU的初始化说起 

那究竟怎么去判断当前的读者已经操作完了呢?我们在之前看到,不是读者在调用rcu_read_lock()的时候要禁止抢占么?因此,我们只需要判断所有的CPU都进过了一次上下文切换,就说明所有读者已经退出了。要彻底弄清楚这个问题,我们得从RCU的初始化说起。
    RCU的初始化开始于start_kernel()-->rcu_init()。而其主要是对每个cpu调用了rcu_online_cpu函数。

rcu_online_cpu

(编辑:PHP编程网 - 黄冈站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

推荐文章
    热点阅读