• 顺序锁替换读写锁


    在内核中,顺序锁和读写锁比较相似,都是针对多读少写且快速处理的临界区的锁机制。

      对于 rwlock 而言,rwlock的全称是"reader-writer spin lock",和普通的spinlock不同,它对"read"和"write"的操作进行了区分。如果当前没有writer,那么多个reader可以同时获取这个rwlock。如果当前没有任何的reader,那么一个writer可以获取这个rwlock。rwlock限定了reader与writer之间,以及writer与writer之间的互斥,但它没有限定reader与reader之间的互斥。

      看起来rwlock比原生的spinlock控制更加精细,应用起来应该更加高效对不对;但rwlock存在一个问题,如果现在有多个reader在使用某个rwlock,那么writer需要等到所有的reader都释放了这个rwlock,才可以获取到,这容易造成writer执行的延迟,俗称饥饿(starve)。

      而且,在writer等待期间,reader还可以不断地加入进来执行,这对writer来说实在是太不公平了。即便writer的优先级更高,也不能先于优先级更低的reader执行,身份(是reader还是writer)决定一切。

    【seqlock】

      seqlock是由Stephen Hemminger负责开发,自Linux 2.6版本引入的,其全称是"sequential lock"。相比起rwlock,它进一步解除了reader与writer之间的互斥,只保留了writer与writer之间的互斥。只要没有其他的writer持有这个seqlock(即便当前存在reader持有该seqlock),那么第一个试图获取该seqlock的writer就可以成功地持有。

    • 那如果在reader读取共享变量期间,writer对变量进行了修改,岂不是会造成读取数据的不一致?
    typedef struct {
        struct seqcount seqcount;
        spinlock_t lock;
    } seqlock_t;

    数据结构存在两个成员,一个是锁变量 sequence,另一个是 spinlock 成员,该 spinlock 是用作写者之间的互斥的。 

    开始写入

    每当有writter持有seqlock之后,sequence number的值就会加1

    /*
     * Lock out other writers and update the count.
     * Acts like a normal spin_lock/unlock.
     * Don't need preempt_disable() because that is in the spin_lock already.
     */
    static inline void write_seqlock(seqlock_t *sl)
    {
        spin_lock(&sl->lock);
        s->sequence++;
        smp_wmb();
    }

    最后设置一个内存屏障以保证加锁操作对所有 CPU 可见(数据刷新到memory;barrier() 的作用就是告诉编译器,内存中变量的值已经改变了,之前保存与寄存器或cache中的变量副本无效,如果访问该变量需要直接去内存中读取)

     结束写入

     当writer释放seqlock之前,sequence number的值会再次加1:

    static inline void write_seqcount_end(seqcount_t *s)
    {
        smp_wmb();
        s->sequence++;
            spin_unlock(&s->lock);      
    }

    sequence number的初始值是一个偶数(even),因而当writer持有spinlock时,sequence number的值将是一个奇数(odd),释放后则又变成偶数。

    开始读取

    reader在读取一个共享变量之前,需要先读取一下sequence number的值,如果为奇数,说明现在有writer正在修改这个变量,需要等待,直到sequence number变为偶数,才可以开始读取变

    /**
     * __read_seqcount_begin - begin a seq-read critical section (without barrier)
     * @s: pointer to seqcount_t
     * Returns: count to be passed to read_seqcount_retry
     *
     * __read_seqcount_begin is like read_seqcount_begin, but has no smp_rmb()
     * barrier. Callers should ensure that smp_rmb() or equivalent ordering is
     * provided before actually loading any of the variables that are to be
     * protected in this critical section.
     *
     * Use carefully, only in critical code, and comment how the barrier is
     * provided.
     */
    static inline unsigned __read_seqcount_begin(const seqcount_t *s)
    {
        unsigned ret;
    
    repeat:
        ret = READ_ONCE(s->sequence);
        if (unlikely(ret & 1)) {
            cpu_relax();
            goto repeat;
        }
        return ret;
    }
    结束读取

    读取变量之后,reader需要再次读取一下sequence number的值,并和读取之前的sequence number的值进行比较,看是否相等,相等则说明在此期间没有writer的操作

    static inline int __read_seqcount_retry(const seqcount_t *s, unsigned start)
    {
        return unlikely(s->sequence != start);
    }
    适用场景

    理论上,reader可以随时读(相当于在读取一侧没有加锁)。在这一过程中,writer不会受到什么影响,但reader可能就需要多读几次。一个writer只会被其他writer造成starve,而不再会被reader造成starve。

    显然,其设计策略是倾向于writer的。它适用于reader数量较多,而writer数量较少的场景。其在Linux中的一个重要应用就是表示时间的jiffies(jiffies记录了系统启动后的时钟节拍的数目)。

    do {
        seq = read_seqbegin(&jiffies_lock);
        ret = jiffies_64;
    } while (read_seqretry(&jiffies_lock, seq));
        do {
            __skb_pull(skb, skb_network_offset(skb));
            seq = read_seqbegin(&neigh->ha_lock);
            err = dev_hard_header(skb, dev, ntohs(skb->protocol),
                          neigh->ha, NULL, skb->len);
        } while (read_seqretry(&neigh->ha_lock, seq));
    • spinlock的“一读或一写”
    • rwlock的“多读或一写”
    • seqlock的“多读和一写”
    • RCU“多读和多写”
     
  • 相关阅读:
    自己写的一个ASP.NET服务器控件Repeater和GridView分页类
    c#Udp分包组包方法
    利用反射写的,可以插件的俄罗斯方块
    冰之随笔一(c#反射、特性)
    Socket的简单例子
    HTTP状态码
    C# WebService 基础实例
    Win7上IIS发布网站系统部署项目
    FileUpload 简单上传+小预览
    .net 验证码
  • 原文地址:https://www.cnblogs.com/codestack/p/15984109.html
Copyright © 2020-2023  润新知