自旋锁、读写锁、中断上下文(原子上下文)

4760阅读 0评论2019-08-09 可怜的猪头
分类:LINUX

自旋锁主要用于解决SMP环境下多个CPU并发访问共享资源的问题,按字面意思上理解,拿不到自旋锁的内核执行路径会一直自旋下去,直到拿到这个自旋锁。自旋锁有以下几个特点:

a)      只有在多CPU的系统上,自旋锁才有存在的价值,因此,单CPU系统上自旋锁为空实现;

b)      内核实现的自旋锁是排队锁,先进入自旋锁的内核执行路径会先拿到自旋锁,这样做是为了防止某些内核执行路径一直拿不到自旋锁致其长期饥饿;

c)      自旋锁内保护的代码需具备一致性和完整性,除非被高优先级的内核执行路径打断,否则不应在自旋锁内保护的执行段内主动结束执行;

 

 使用自旋锁资源时,应当注意以下问题:

a)        自旋锁保护的代码应当尽量简短,执行时间不能太长,以免其它CPU等待时间过长,降低系统的吞吐量;

b)        内核执行路径执行自旋锁内代码时不能主动让出CPU,否则会引起死锁等问题;

c)        自旋锁保护的全局资源被中断上下文引用时,应当在加锁的同时关闭中断(软中断或硬中断);

上面讨论的几点中,重点需要强调的是第二点,在执行自旋锁内代码时主动让出CPU主要指在自旋锁内的代码中执行了睡眠或调度函数,这包括:

1) 调用msleep()函数;

2)直接调用schedule()schedule_timeout()等主动调度函数;

3) semaphore信号量执行down操作;

4) 在进程上下文中,因为等待某资源,而将当前进程加入等待队列;

另外,除了不能直接调用上面的函数外,也要注意不能间接执行上面的函数,比如不能在自旋锁保护的代码内调用if_dev_close()来关闭接口,因为该函数里面会调用msleep()。同时,由于内核信号量会引起进程挂起,引发调度行为,所以,自旋锁内是不允许存在信号量操作的,反之,信号量保护的代码里面是可以有自旋锁操作的。

 

  读写锁的使用

读写锁也是用于解决多个CPU并发访问共享资源的问题,主要用于对共享资源读多写少的情况。和自旋锁一样,读写锁也只有在多核系统上才会被实现,但是它与自旋锁有以下几点区别:

a)      读写锁中相同的读锁可以与读锁嵌套,而相同的自旋锁不能嵌套使用;

b)      读写锁没有排队机制,各个CPU随机抢锁,容易造成个别CPU 饥饿;

使用读写锁与使用自旋锁有同样的要求,所以凡是使用自旋锁中注意的问题,使用读写锁时也应当注意,另外,由于读写锁没有排队机制,需要防止饥饿问题,特别是多个CPU同时频繁的加上某个读写锁时,有可能因某个CPU长期饥饿引起设备狗叫,这个时候,模块设计需要使用其它代替方案,比如每CPU变量等。


 硬中断与软中断

             中断上下文应当注意些什么

内核执行路径正在执行中断处理函数或软中断处理函数时,我们将 内核执行路径所处的环境称为中断上下文。中断执行路径的优先级高于进程执行路径,所以它可以打断进程的执行。

中断上下文有下面的特点:

a)      中断上下文不会有进程抢占,只会被高优先级中断抢占,这仅仅在系统支持中断抢占的情况下才会发生,当前系统不支持中断抢占;

b)      中断上下文又称为原子上下文,需要全部执行完成才能退出,不能被打断,也不能在没有完成之前主动让出处理器;

c)      软中断也是中断上下文, 通常的定时器处理函数、tasklet任务函数都处于软中断上下文,系统在处理完硬中断后,在退出中断上下文之前会执行软中断相关过程函数;

 

由于中断上下文的特殊性,在中断上下文编写代码时需要注意以下几点:

a)      中断上下文的代码必须要简短,在中断上下文中执行冗长的代码会导致进程长时间无法调度,影响系统均衡;

b)      中断上下文代码需要保证原子性,不能执行睡眠或者能引起调度的函数,否则会导致系统异常甚至是挂死;

这里的第二点与自旋锁上下文的情况是类似的,需要注意在中断上下文中不能执行前面提到的四类函数。

上一篇:普通ARP和免费ARP格式
下一篇:异常问题汇总--(调用栈)