首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

透过Linux内核看无锁编程 04

透过Linux内核看无锁编程 04

3。Lock-free应用场景三——RCU
    在2。6内核中,开发者还引入了一种新的无锁机制-RCU(Read-Copy-Update),允许多个读者和写者并发执行。RCU技术的核心是写操作分为写和更新两步,允许读操作在任何时候无阻碍的运行,换句话说,就是通过延迟写来提高同步性能。RCU主要应用于WRRM场景,但它对可保护的数据结构做了一些限定:RCU只保护被动态分配并通过指针引用的数据结构,同时读写控制路径不能有睡眠。以下数组动态增长代码摘自2。4。34内核:
    清单7。2。4。34RCU实现代码
    其中ipc_lock是读者,grow_ary是写者,不论是读或者写,都需要加spinlock对被保护的数据结构进行访问。改变数组大小是小概率事件,而读取是大概率事件,同时被保护的数据结构是指针,满足RCU运用场景。以下代码摘自2。6。10内核:
    清单8。2。6。10RCU实现代码
    #definercu_read_lock()preempt_disable()
    #definercu_read_unlock()preempt_enable()
    #definercu_assign_pointer(p,v)({
    smp_wmb();
    (p)=(v);
    })
    structkern_ipc_perm*ipc_lock(structipc_ids*ids,intid)
    {
    ……
    rcu_read_lock();
    entries=rcu_dereference(ids->entries);
    if(lid>=entries->size){
    rcu_read_unlock();
    returnNULL;
    }
    out=entries->p[lid];
    if(out==NULL){
    rcu_read_unlock();
    returnNULL;
    }
    ……
    returnout;
    }
    staticintgrow_ary(structipc_ids*ids,intnewsize)
    {
    structipc_id_ary*new;
    structipc_id_ary*old;
    ……
    new=ipc_rcu_alloc(sizeof(structkern_ipc_perm*)*newsize+
    sizeof(structipc_id_ary));
    if(new==NULL)
    returnsize;
    new->size=newsize;
    memcpy(new->p,ids->entries->p,sizeof(structkern_ipc_perm*)*size
    +sizeof(structipc_id_ary));
    for(i=size;inew->p[i]=NULL;
    }
    old=ids->entries;
    /*
    *Usercu_assign_pointer()tomakesurethememcpyedcontents
    *ofthenewarrayarevisiblebeforethenewarraybecomesvisible。
    */
    rcu_assign_pointer(ids->entries,new);
    ipc_rcu_putref(old);
    returnnewsize;
    }
    纵观整个流程,写者除内核屏障外,几乎没有一把锁。当写者需要更新数据结构时,首先复制该数据结构,申请new内存,然后对副本进行修改,调用memcpy将原数组的内容拷贝到new中,同时对扩大的那部分赋新值,修改完毕后,写者调用rcu_assign_pointer修改相关数据结构的指针,使之指向被修改后的新副本,整个写操作一气呵成,其中修改指针值的操作属于原子操作。在数据结构被写者修改后,需要调用内存屏障smp_wmb,让其他CPU知晓已更新的指针值,否则会导致SMP环境下的bug。当所有潜在的读者都执行完成后,调用call_rcu释放旧副本。同Spinlock一样,RCU同步技术主要适用于SMP环境。
返回列表