Board logo

标题: 透过Linux内核看无锁编程 03 [打印本页]

作者: samwalton    时间: 2013-8-26 08:23     标题: 透过Linux内核看无锁编程 03

对于应用程序来说,希望任何时候都能获取到锁,也就是期望lock->lock为1,那么用CAS原语来描述_raw_spin_trylock(lock)就是CAS(lock->lock,1,0);
    如果同步操作总是能在数条指令内完成,那么使用SpinLock会比传统的mutexlock快一个数量级。SpinLock多用于多核系统中,适合于锁持有时间小于将一个线程阻塞和唤醒所需时间的场合。
    pthread库已经提供了对spinlock的支持,所以用户态程序也能很方便的使用spinlock了,需要包含pthread。h。在某些场景下,pthread_spin_lock效率是pthread_mutex_lock效率的一倍多。美中不足的是,内核实现了读写spinlock锁,但pthread未能实现。
    2。Lock-free应用场景二——Seqlock
    手表最主要最常用的功能是读时间,而不是校正时间,一旦后者成了最常用的功能,消费者肯定不会买账。计算机的时钟也是这个功能,修改时间是小概率事件,而读时间是经常发生的行为。以下代码摘自2。4。34内核:
    清单5。2。4。34seqlock实现代码
    443voiddo_gettimeofday(structtimeval*tv)
    444{
    ……
    448read_lock_irqsave(&xtime_lock,flags);
    ……
    455sec=xtime。tv_sec;
    456usec+=xtime。tv_usec;
    457read_unlock_irqrestore(&xtime_lock,flags);
    ……
    466}
    468voiddo_settimeofday(structtimeval*tv)
    469{
    470write_lock_irq(&xtime_lock);
    ……
    490write_unlock_irq(&xtime_lock);
    491}
    不难发现获取时间和修改时间采用的是spinlock读写锁,读锁和写锁具有相同的优先级,只要读持有锁,写锁就必须等待,反之亦然。
    Linux2。6内核中引入一种新型锁——顺序锁(seqlock),它与spinlock读写锁非常相似,只是它为写者赋予了较高的优先级。也就是说,即使读者正在读的时候也允许写者继续运行。当存在多个读者和少数写者共享一把锁时,seqlock便有了用武之地,因为seqlock对写者更有利,只要没有其他写者,写锁总能获取成功。根据lock-free和时钟功能的思想,内核开发者在2。6内核中,将上述读写锁修改成了顺序锁seqlock,代码如下:
    清单6。2。6。10seqlock实现代码
    staticinlineunsignedread_seqbegin(constseqlock_t*sl)
    {
    unsignedret=sl->sequence;
    smp_rmb();
    returnret;
    }
    staticinlineintread_seqretry(constseqlock_t*sl,unsignediv)
    {
    smp_rmb();
    return(iv&1)|(sl->sequence^iv);
    }
    staticinlinevoidwrite_seqlock(seqlock_t*sl)
    {
    spin_lock(&sl->lock);
    ++sl->sequence;
    smp_wmb();
    }
    voiddo_gettimeofday(structtimeval*tv)
    {
    unsignedlongseq;
    unsignedlongusec,sec;
    unsignedlongmax_ntp_tick;
    ……
    do{
    unsignedlonglost;
    seq=read_seqbegin(&xtime_lock);
    ……
    sec=xtime。tv_sec;
    usec+=(xtime。tv_nsec/1000);
    }while(read_seqretry(&xtime_lock,seq));
    ……
    tv->tv_sec=sec;
    tv->tv_usec=usec;
    }
    intdo_settimeofday(structtimespec*tv)
    {
    ……
    write_seqlock_irq(&xtime_lock);
    ……
    write_sequnlock_irq(&xtime_lock);
    clock_was_set();
    return0;
    }
    Seqlock实现原理是依赖一个序列计数器,当写者写入数据时,会得到一把锁,并且将序列值加1。当读者读取数据之前和之后,该序列号都会被读取,如果读取的序列号值都相同,则表明写没有发生。反之,表明发生过写事件,则放弃已进行的操作,重新循环一次,直至成功。不难看出,do_gettimeofday函数里面的while循环和接下来的两行赋值操作就是CAS操作。
    采用顺序锁seqlock好处就是写者永远不会等待,缺点就是有些时候读者不得不反复多次读相同的数据直到它获得有效的副本。当要保护的临界区很小,很简单,频繁读取而写入很少发生(WRRM---WriteRarelyReadMostly)且必须快速时,就可以使用seqlock。但seqlock不能保护包含有指针的数据结构,因为当写者修改数据结构时,读者可能会访问一个无效的指针。




欢迎光临 电子技术论坛_中国专业的电子工程师学习交流社区-中电网技术论坛 (http://bbs.eccn.com/) Powered by Discuz! 7.0.0