zoukankan      html  css  js  c++  java
  • RCU学习总结

    前言:

      对于rcu平时写代码用到不是特别的多,可能是自己对rcu的理解不够深入透彻,不能发挥其强大的特性,写个博客学习一下,以便更深入的理解RCU的机制

    rcu简述:

      RCU(Read-Copy Update),是 Linux 中比较重要的一种同步机制。更新数据的时候,需要先复制一份副本,在副本上完成修改,再一次性地替换旧数据,读者并不需要直接与写者进行同步,读者与写者也能并发的执行

    原理分析

    Update操作分为两个部分:

    1)Removal移除:临界资源被多个读者读取,写者在拷贝副本修改后进行更新时,第一步需要先把旧的临界资源数据移除

    2)Reclamation回收:需要把旧的数据进行回

    分析角色: 

    Reader

      1)使用rcu_read_lockrcu_read_unlock来界定读者的临界区,访问受RCU保护的数据时,需要始终在该临界区域内访问;

      2)在访问受保护的数据之前,需要使用rcu_dereference来获取RCU-protected指针;

    Updater

      1)多个Updater更新数据时,需要使用互斥机制进行保护;

      2)Updater使用rcu_assign_pointer来移除旧的指针指向,指向更新后的临界资源;

    Reclaimer

      1)Reclaimer回收的是旧的临界资源;

      2)为了确保没有读者正在访问要回收的临界资源,Reclaimer需要等待所有的读者退出临界区

    内核实例源码分析:

     添加元素:

     1 #define list_next_rcu(list)     (*((struct list_head __rcu **)(&(list)->next)))
     2 
     3 static inline void __list_add_rcu(struct list_head *new,
     4                 struct list_head *prev, struct list_head *next)
     5 {
     6         new->next = next;
     7         new->prev = prev;
     8         rcu_assign_pointer(list_next_rcu(prev), new);
     9         next->prev = new;
    10 }
    11 #define __rcu_assign_pointer(p, v, space) 
    12     ({ 
    13         smp_wmb();    //内存屏障
    14         (p) = (typeof(*v) __force space *)(v); 
    15     })

    读取元素:

    rcu_read_lock();
    list_for_each_entry_rcu(pos, head, member) {
        // do something with `pos`
    }
    rcu_read_unlock();
    
    list_for_each_entry_rcu->rcu_dereference->__rcu_dereference_check
    #define __rcu_dereference_check(p, c, space) 
        ({ 
            typeof(*p) *_________p1 = (typeof(*p)*__force )ACCESS_ONCE(p); 
            rcu_lockdep_assert(c, "suspicious rcu_dereference_check()" 
                          " usage"); 
            rcu_dereference_sparse(p, space);    
            smp_read_barrier_depends();   //读内存屏障
            ((typeof(*p) __force __kernel *)(_________p1)); 
        })

    删除元素

    p = seach_the_entry_to_delete();
    list_del_rcu(p->list);
    synchronize_rcu();
    kfree(p);
    其中 list_del_rcu() 的源码如下,把某一项移出链表:
    
    /* list.h */
    static inline void __list_del(struct list_head * prev, struct list_head * next)
    {
        next->prev = prev;
        prev->next = next;
    }
    
    /* rculist.h */
    static inline void list_del_rcu(struct list_head *entry)
    {
        __list_del(entry->prev, entry->next);
        entry->prev = LIST_POISON2;
    }

    更新元素

    p = search_the_entry_to_update();
    q = kmalloc(sizeof(*p), GFP_KERNEL);
    *q = *p;
    q->field = new_value;
    list_replace_rcu(&p->list, &q->list);
    synchronize_rcu();
    kfree(p);
    
    static inline void list_replace_rcu(struct list_head *old,
                    struct list_head *new)
    {
        new->next = old->next;
        new->prev = old->prev;
        rcu_assign_pointer(list_next_rcu(new->prev), new);
        new->next->prev = new;
        old->prev = LIST_POISON2;
    }

    适用场景:

    RCU的特性是读者并不需要直接与写者进行同步,读者与写者也能并发的执行,减少了读的性能,对读者性能要求高的场景非常适用

    优缺点:

    优点:

      1)读者侧开销很少、不需要获取任何锁,不需要执行原子指令或者内存屏障;

      2)没有死锁问题;

      3)没有优先级反转的问题;

      4)没有内存泄露的危险问题;

      5)很好的实时延迟;

    缺点:

      1)写者的同步开销比较大,写者之间需要互斥处理;

      2)使用上比其他同步机制复杂;

    参考文献:

    https://lwn.net/Articles/262464/

    https://www.kernel.org/doc/Documentation/RCU/whatisRCU.rst

    https://www.kernel.org/doc/Documentation/RCU/rcu_dereference.txt

    https://www.kernel.org/doc/Documentation/RCU/rcuref.txt

  • 相关阅读:
    为什么说 LINQ 要胜过 SQL
    统一日志系统 Log4Net/ExceptionLess
    使用Advanced Installer制作IIS安装包(二:配置安装包依赖项和自定义dll)
    使用Advanced Installer制作IIS安装包(一:配置IIS和Web.config)
    安全的API接口解决方案
    任务调度及远端管理(基于Quartz.net)
    关于.NET参数传递方式的思考
    C# Parsing 类实现的 PDF 文件分析器
    .NET的弹性及瞬间错误处理库Polly
    Mybatis官方网站
  • 原文地址:https://www.cnblogs.com/mysky007/p/12688392.html
Copyright © 2011-2022 走看看