Linux内核中在自旋锁,信号量及中断禁止之间的选择
一.linux内核控制路径的三种执行环境:
1>中断
中断的顶半部分,也就是用request_irq注册的中断例程
处中断上下文,不可睡眠
2>可延迟函数(软中断及基于软中断实现的其它处理函数,如tasklet, 定时器等)
被中断的顶半部分触发,并在稍后时间内执行的例程。
处中断上下文,不可睡眠
一般是:
由open_softirq注册,在中断处理中由raise_softirq触发的函数。
由tasklet_init初始化,在中断处理中由tasklet_schedule调度的函数。
3>异常
典型是系统调用(内核线程也算吧)。
处进程上下文,可睡眠
中断,异常及可延迟函数之间对cpu的争夺:
1>中断处理程序可以抢占其它中断处理程序
2>中断处理程序可以抢占异常
3>中断处理程序可以抢占可延迟函数
4>中断处理程序在退出前触发可延迟函数。
5>一个可延迟函数不能抢占中断处理程序。
6>一个可延迟函数不能抢占另一个可延迟函数。
7>相同类型的软中断可以并发的运行在多个cpu上
8>相同类型的tasklet只能被串行的执行,不同类型的tasklet可以在几个cpu上并发执行。tasklet基于软中断而实现(可视为加入约束的软中断?)。
9>软中断始终在触发它的同一个cpu上执行,tasklet也是。
10>异常不能抢占中断,不能抢占可延迟函数,不能抢占异常(缺页不在考虑之中)
二.自信号旋锁,信号量及关中断的之间的……
自旋锁:在竟争条件下,反复执行紧凑的循环指令,直到锁被释放。防止多cpu间的竟争。
信号量:在竟争条件下,不允许内核控制路径继续进行,相应的进程被挂起。因此中断处理,和可延迟函数都不能使用信号量。
关中断:禁用本地中断。关中断都是为了防止同一cpu下的中断抢占,关本地中断后,可延迟函数也被禁止了。
三.Linux内核中在自旋锁,信号量及中断禁止之间的选择
3.1仅异常时的数据访问保护
1>单处理器:此时的CPU运行在内核态为用户进程提供服务。此竟争条件可通过信号量避免。
2>多处理器:与单处理器环境时相同
3>访问per-cpu变量时,要禁用抢占。
3.2仅中断时的数据访问保护
单处理器
1>只有一个中断的“上半部分”访问时,中断都相对自己串行地执行,无需同步。
2>多个中断的“上半部分”访问时,要关中断。
多处理器
3>只有一个中断的“上半部分”访问时,中断都相对自己串行地执行,无需保护。
4>多个中断可访问时,要关中断,并加上自旋锁。
3.3仅可延迟函数(软中断和tasklet)中的数据访问保护
单处理器
1>上不存在竞争问题
多处理器
2>软中断访问的数据使用自旋锁,防止多cpu竟争
3>仅由一种tasklet访问的数据结构不需要保护
4>被多种tasklet访问需要用自旋锁保护
3.4.异常和中断时的数据访问保护(只考虑一种中断与一种异常)
单处理器
1>在异常的访问中关闭中断;中断的访问中不需要保护
多处理器
2>在异常的访问中关闭本地中断,并加上自旋锁;中断的访问中加上自旋锁即可(可用紧循环和down_trylock代替自旋的功能)
3.5.异常和可延迟函数中的数据访问保护(只考虑一种异常与一种可延迟函数)
单处理器
1>在异常访问中关闭中断(禁止可延迟函数更合适);可延迟函数无需保护
多处理器
2>在异常访问中关闭中断(禁止可延迟函数更合适)并加上自旋锁;可延迟函数加上自旋锁
3.6.中断和可延迟函数中的数据访问保护(只考虑一种中断与一种可延迟函数)
单处理器
1>可延迟函数访问中关闭中断;中断的访问中不需要保护
多处理器
2>可延迟函数访问中关闭中断,并加上自旋锁;中断的访问中加上自旋锁
3.7.异常,中断和可延迟函数中的数据访问保护(只考虑一种中断一种可延迟函数一种异常)
单处理器
1>在异常访问中关闭中断; 可延迟函数访问中关闭中断 ;中断的访问中不需要保护
多处理器
2>在异常访问中关闭中断并加上自旋锁; 可延迟函数访问中关闭中断并加上自旋锁 ;中断的访问中加上自旋锁
其它的场景都可看作以上场景的组合
如:异常,中断和可延迟函数中的数据访问保护。可看作中断和可延迟函数,中断和异常的组合。可同时使用相应的保护方式。
阅读(2839) | 评论(0) | 转发(0) |