zoukankan      html  css  js  c++  java
  • 内核并发管理---spin lock

     自旋锁最初是为了在smp系统上使用而设计。


    1.在单处理器非抢占模式下,自旋锁不做任何事情。
    #ifdef CONFIG_PREEMPT_COUNT     //支持抢占模式

    #define preempt_disable()
    do {
        inc_preempt_count();
        barrier();
    } while (0)

    #define sched_preempt_enable_no_resched()
    do {
        barrier();
        dec_preempt_count();
    } while (0)

    #define preempt_enable_no_resched() sched_preempt_enable_no_resched()

    #define preempt_enable()
    do {
        preempt_enable_no_resched();
        barrier();
        preempt_check_resched();
    } while (0)

    /* For debugging and tracer internals only! */
    #define add_preempt_count_notrace(val)         
        do { preempt_count() += (val); } while (0)
    #define sub_preempt_count_notrace(val)         
        do { preempt_count() -= (val); } while (0)
    #define inc_preempt_count_notrace() add_preempt_count_notrace(1)
    #define dec_preempt_count_notrace() sub_preempt_count_notrace(1)

    #define preempt_disable_notrace()
    do {
        inc_preempt_count_notrace();
        barrier();
    } while (0)
     
    #define preempt_enable_no_resched_notrace()
    do {
        barrier();
        dec_preempt_count_notrace();
    } while (0)

    /* preempt_check_resched is OK to trace */
    #define preempt_enable_notrace()
    do {
        preempt_enable_no_resched_notrace();
        barrier();
        preempt_check_resched();
    } while (0)

    #else /* !CONFIG_PREEMPT_COUNT */     //非抢占模式

    #define preempt_disable()       do { } while (0)
    #define sched_preempt_enable_no_resched()   do { } while (0)
    #define preempt_enable_no_resched() do { } while (0)
    #define preempt_enable()        do { } while (0)

    #define preempt_disable_notrace()       do { } while (0)
    #define preempt_enable_no_resched_notrace() do { } while (0)
    #define preempt_enable_notrace()        do { } while (0)

    #endif /* CONFIG_PREEMPT_COUNT */
     
    #ifdef CONFIG_DEBUG_LOCK_ALLOC
    # ifdef CONFIG_PROVE_LOCKING
    #  define spin_acquire(l, s, t, i)      lock_acquire(l, s, t, 0, 2, NULL, i)
    #  define spin_acquire_nest(l, s, t, n, i)  lock_acquire(l, s, t, 0, 2, n, i)
    # else
    #  define spin_acquire(l, s, t, i)      lock_acquire(l, s, t, 0, 1, NULL, i)
    #  define spin_acquire_nest(l, s, t, n, i)  lock_acquire(l, s, t, 0, 1, NULL, i)
    # endif
    # define spin_release(l, n, i)          lock_release(l, n, i)
    #else
    # define spin_acquire(l, s, t, i)       do { } while (0)
    # define spin_release(l, n, i)          do { } while (0)
    #endif
     
    #ifdef CONFIG_LOCK_STAT

    extern void lock_contended(struct lockdep_map *lock, unsigned long ip);
    extern void lock_acquired(struct lockdep_map *lock, unsigned long ip);

    #define LOCK_CONTENDED(_lock, try, lock)           
    do {                               
        if (!try(_lock)) {                 
            lock_contended(&(_lock)->dep_map, _RET_IP_);   
            lock(_lock);                   
        }                          
        lock_acquired(&(_lock)->dep_map, _RET_IP_);        
    } while (0)

    #else /* CONFIG_LOCK_STAT */

    #define lock_contended(lockdep_map, ip) do {} while (0)
    #define lock_acquired(lockdep_map, ip) do {} while (0)

    #define LOCK_CONTENDED(_lock, try, lock)
        lock(_lock)

    #endif /* CONFIG_LOCK_STAT */
     
    static inline void spin_lock(spinlock_t *lock)
    {
        raw_spin_lock(&lock->rlock);
     
    #define raw_spin_lock(lock) _raw_spin_lock(lock)
     
    #ifndef CONFIG_INLINE_SPIN_LOCK
    void __lockfunc _raw_spin_lock(raw_spinlock_t *lock)
    {
        __raw_spin_lock(lock);
    }
    EXPORT_SYMBOL(_raw_spin_lock);
    #endif
     
    static inline void __raw_spin_lock(raw_spinlock_t *lock)
    {
        preempt_disable();
        spin_acquire(&lock->dep_map, 0, 0, _RET_IP_);
        LOCK_CONTENDED(lock, do_raw_spin_trylock, do_raw_spin_lock);
    }   
    void do_raw_spin_lock(raw_spinlock_t *lock)
    {
        debug_spin_lock_before(lock);
        if (unlikely(!arch_spin_trylock(&lock->raw_lock)))     //当没加自旋锁时,条件判断为假,不会执行下面一句
            __spin_lock_debug(lock);
        debug_spin_lock_after(lock);
    }
     
    static inline int arch_spin_trylock(arch_spinlock_t *lock)
    {
        unsigned long tmp;

        __asm__ __volatile__(
    "   ldrex   %0, [%1] "                         //将lock存在tmp中,若没有人占用锁,则tmp=0;若有人占用锁,tmp=1,直接return0
    "   teq %0, #0 "                                //判断tmp=0就执行下面一句,否则不执行
    "   strexeq %0, %2, [%1]"                   //将lock=1,即加锁;写成功tmp=0;不成功tmp=1.
        : "=&r" (tmp)                                   //                                        ^                    ^
        : "r" (&lock->lock), "r" (1)                    //                                  return 1          return 0
        : "cc");

        if (tmp == 0) {
            smp_mb();
            return 1;
        } else {
            return 0;
        }
    }
     
    所以单核非抢占,第一次肯定是未加锁状态,调用trylock()后,若加锁成功则返回1,spin_lock()返回。即不会进入自旋状态。
    单核非抢占进入某个锁上的自旋状态,则会永远自旋下去;即,没有任何其他进程能够获得CPU来释放这个锁。出于对此原因的考虑,单核非抢占上的自旋锁被优化为不做任何事情。            
     
    2.在单核可抢占的系统中,可能会存在以下问题:
         当进程A对公共资源访问时,我们对临界区加锁:
              由于时间片或更高优先级进程,进程A临界区并没有执行完,cpu转而去执行进程B,而进程B中也涉及到对公共资源的访问,但此时获取不到锁,B可能会一直等锁释放,如果B一直循环等,那么系统会出现死机现象;如果进程以时间片调度,那么需要等到对其他进程都调度扫描一次并再次回到进程A,等到进程A临界区执行完,锁才会释放,那么系统响应的就会很慢。
              由于进程A在执行临界区时来了一个中断,而中断例程里也要访问公共资源, 此时中断例程一直等待锁的释放,而进程A的临界区的锁又没有任何机会被释放,那么单核处理器将一直等待下去,而没有任何响应。
     
    那么自旋锁的出现就解决了上述问题,自旋锁本身会处理禁止抢占,那么直到加锁的进程临界区执行完,其他进程才能加锁。那么问题来了,自旋锁不能长时间持有,否则其他等待锁释放的进程将不会被执行。
     
    3.多核可抢占系统
         cpu1运行进程A,进程A对公共资源访问,cpu2同时运行进程B,进程B也要对公共资源访问,那么就会存在公共资源被异常修改的问题;好,咋们当cpu1的进程A对公共资源访问时,先得加锁;当cpu2同时运行进程B在要对公共资源访问时,发现已经加锁了,有人在访问,自己必须等待解锁,那么解决了多cpu对公共资源的同时修改的问题。但是,但cpu1的进程c抢占进程A,使得进程c运行,而进程c中也恰好要对公共资源访问,由于进程A还没有解锁,辣么进程c也必须等着解锁,进程A还没有执行完前,不能让当前cpu的其他进程抢占啊,这就又蛋疼啦。
     
    但自旋锁能解决该问题,因为自旋锁既有锁的功能也有禁止抢占的功能。
     
     
    static inline void arch_spin_lock(arch_spinlock_t *lock)
    {
        unsigned long tmp;

        __asm__ __volatile__(
    "1: ldrex   %0, [%1] "          //
    "   teq %0, #0 "                    //若tmp=0,
        WFE("ne")
    "   strexeq %0, %2, [%1] "
    "   teqeq   %0, #0 "
    "   bne 1b"
        : "=&r" (tmp)
        : "r" (&lock->lock), "r" (1)
        : "cc");

        smp_mb();
    }   
  • 相关阅读:
    Cufon css3@font-face
    HTML5 Canvas
    HTML5 Canvas 的宽高
    :nth-child()
    new Image()
    ios有些机型input和fixed导致的页面错位问题
    使用performance进行前端性能监控
    throttle(节流)和debounce(防抖)
    object-fit/object-position
    flex布局与ellipsis冲突问题
  • 原文地址:https://www.cnblogs.com/black-mamba/p/4986283.html
Copyright © 2011-2022 走看看