linux内核中的锁

0、说明

        很多时候可能会访问一个资源。比如,多个进程同时打开字符设备,通过模拟SPI读取数据的时候,同一时刻只能进行一个模拟时序,这个时候需要对资源的访问进行控制,保证同一时刻只有一个在执行,很多时候需要对临界资源进行保护,确保只有一个在执行中。

        内核提供了很多同步互斥的锁,常用的有 原子操作,自旋锁,信号量,互斥量。

1、原子操作

        如下,还有很多在arch\arm\include\asm\atomic.h中定义

atomic_add(i,v)
atomic_sub(i,v)

        分析其中一个的实现

ATOMIC_OPS(add, +=, add)

         将如上宏展开

#define ATOMIC_OP(add, +=, add)                 \                   
static inline void atomic_add(int i, atomic_t *v)          \           
{                                   \                                   
    unsigned long tmp;                      \                           
    int result;                         \                               
                                    \                                   
    prefetchw(&v->counter);                     \                       
    __asm__ __volatile__("@ atomic_" #op "\n"           \               
"1: ldrex   %0, [%3]\n"                     \                           
"   add %0, %0, %4\n"                   \                       
"   strex   %1, %0, [%3]\n"                     \                       
"   teq %1, #0\n"                       \                               
"   bne 1b"                         \                                   
    : "=&r" (result), "=&r" (tmp), "+Qo" (v->counter)       \           
    : "r" (&v->counter), "Ir" (i)                   \                   
    : "cc");                            \                               
}   

        独占指令ldrex、和strex是实现原子操作的基础,也是实现其他锁的基础。ldrex读取的时候将变量标记为独占,写入的时候判断独占标记,若标记在,则变量未被修改,可以正常修改使用。若标记不在则重新尝试。

        以上为>=ARMV6,在ARMV6之前,不支持多个CPU,且非抢占,则直接关闭中断后对变量操作即可:

#define ATOMIC_OP(op, c_op, asm_op)                 \                         
static inline void atomic_##op(int i, atomic_t *v)          \                 
{                                   \                                         
    unsigned long flags;                        \                             
                                    \                                         
    raw_local_irq_save(flags);                  \                             
    v->counter c_op i;                      \                                 
    raw_local_irq_restore(flags);                   \                         
}   

2、自旋锁

        自旋锁,名字上看自旋二字,有点while(1)的感觉,但比while(1)还狠。名字可以推断,请求自旋锁的时候不会休眠,会自旋在那里,直到获取成功,因此可以用于中断上下文中。

        自旋锁的出现主要是为SMP环境设计的,只有在SMP环境下才会“自旋”起来。在不同平台不同架构下的实现完全不一样。

数据结构与函数

//定义锁
spinlock_t splock;
//初始化锁
spin_lock_init(&testlock);
//请求锁
spin_lock/spin_lock_irq/spin_lock_irqsave
//释放锁
spin_unlock/spin_unlock_irq/spin_unlock_restore

单核中的自旋锁

        单核系统只有一个处理器,不存在严格意义上的并发,当进程A通过系统调用进入进程上下文中,获取临界区锁,在未释放锁之前,处于非抢占状态,其他进程无法得到执行,因此适合处理一些时间较短的事情。

        spin_lock锁会禁止抢占,也就是进行不会被调度走,但是没有禁止中断,如果中断函数中使用了同一个锁,此时中断获取锁失败带来的自旋,会导致死锁。

        因此出现了spin_lock_irq,来关闭中断。在临界区执行期间不会被中断程序所抢占。来unlock的时候总会开中断,那么问题来了,当首先请求了两个锁,此时释放了一个锁之后,中断就会被打开,而又可能导致第一个锁陷入中断。 当然中断中不使用同一个锁的时候,完全可以在临界区被中断,因为不会带来死锁的问题。

        因此出现了spin_lock_irqsave,记录中断的状态,释放锁时总是回到之前的中断状态,于此同时带来的是性能上的消耗。

        单核下并不存在多个进程并发一个锁资源的状态,因为自旋锁直接关闭了抢占,根本不给其他进程执行的机会。在不支持抢占的系统中,单处理器自旋锁直接是一个空函数。

多核自旋锁

        与单核不同,进程B在访问临界资源的时候,可能会由于进程A拿着锁,导致进程B处于自旋状态,也就是等待锁的释放,从而达到真正意义上的并发锁效果。

        自旋的特点,决定了临界区的任务不适合太多,不然自旋上花费的时间太多,而又禁止抢占是cpu资源的一种浪费。

        内核实现类似于银行排队系统,next是分配给每个申请的单元,owner是当前获取锁的对象,当next和owner的时候可以进入临界区。

3、semaphore信号量

        lock依赖于spinlock,count表明有多少资源,wait_list是等待队列,获取不到资源的加入到这这个链表,等待有资源的时候唤醒。

struct semaphore {                                              
    raw_spinlock_t      lock;                                   
    unsigned int        count;                                  
    struct list_head    wait_list;                             
};  

4、互斥量

        特殊的信号量进行优化,count只有0、1两种情况。通过原子操作对值进行判断,如果一切顺利,走fastpath路径,如果不顺利走slowpath,在死循环中判断count值,直到自己获取到锁。

5、总结

        各种锁底层都依赖 独占指令 ldrex、strex来实现,无非是对标志标量的运算和判断。

  • spin_lock会禁止抢占,因此临界区处理任务不宜过多,不然浪费cpu资源
  • 自旋锁不会休眠,因此可以用于中断上下文,但要防止死锁
  • 根据临界区的位置合理选择合适的锁,带来性能上的最优
  • 自旋锁适合多处理器之间的互斥
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值