Linux内核之禁止中断和禁止内核抢占

禁止中断指的是Linux内核停工了一组接口用于操作机器上的中断状态。这些接口为我们提供了能够禁止当前处理器的中断系统,或者屏蔽掉整个机器的一条中断线的能力。通过禁止中断,可以确保某个中断处理程序不会抢占当前的代码。控制中断系统在Linux的实现有很多,以local_irq_disable()和 local_irq_enable()函数

//我看不懂,反正就是依赖于体系结构,通过汇编调用实现
static inline void local_irq_enable(void)
{
	unsigned long tmpreg;
	__asm__ __volatile__(
		"mvfc	%0, psw;		\n\t"
		"or3	%0, %0, #0x0040;	\n\t"
		"mvtc	%0, psw;		\n\t"
	: "=&r" (tmpreg) : : "cbit", "memory");
}

static inline void local_irq_disable(void)
{
	unsigned long tmpreg0, tmpreg1;
	__asm__ __volatile__(
		"ld24	%0, #0	; Use 32-bit insn. \n\t"
		"mvfc	%1, psw	; No interrupt can be accepted here. \n\t"
		"mvtc	%0, psw	\n\t"
		"and3	%0, %1, #0xffbf	\n\t"
		"mvtc	%0, psw	\n\t"
	: "=&r" (tmpreg0), "=&r" (tmpreg1) : : "cbit", "memory");
}

禁止内核抢占就比较简单了,就是防止当前进程不会突然被另一个进程抢占。在Linux的实现就是preempt_disable()和preempt_enable()函数

#define preempt_disable() \
do { \
	//增加preempt_count
	inc_preempt_count(); \
	//保证先加了preempt_count才进行以后的操作
	barrier(); \
} while (0)

#define preempt_enable() \
do { \
	preempt_enable_no_resched(); \
	barrier(); \
	//检查当前进程是否可抢占
	preempt_check_resched(); \
} while (0)

不管是禁止中断还是禁止内核抢占,都是为了提供内核同步,但是他们都没有提供任何保护机制来防止其它处理器的并发访问。Linux支持多处理器,因此,内核代码一般都需要获取某种锁,防止来自其他处理器对共享数据的并发访问,而禁止中断提供保护机制,这是防止来自其他中断处理程序的并发访问。

前面说的都是概念,现在我们来讨论几个问题

1.在单处理器条件下,为什么禁止中断就可以禁止内核抢占?

这个问题困扰了我很久,我也只是说说个人的理解,先来回顾一下内核抢占发生在哪些时候:

1.  在中断返回内核空间的时候,这个没什么好说的,跟中断密切相关,没了中断就不会发生

2. 内核显式调用schedule()(可抢占或阻塞)

我们先搞清楚一件事,就是我们说禁止中断可以禁止内核抢占只是说禁止任何意外的抢占,如果进程自己要调用schedule函数,那谁也拦不住,事实上调用schedule这个函数本来就要禁止中断,所以剩下的就是考虑创建或者唤醒一个更高优先级的进程,或者调用信号量、完成量,所有的这些情况都要通过try_to_wake_up函数唤醒另一个进程,但是这个函数真正干的事只是设置了一下need_resched这个函数,并没有真的调用schedule函数,调用是在系统调用返回用户空间的时候进行的,所以跟内核抢占也没啥关系,具体可以看这篇博客http://blog.csdn.net/wishfly/article/details/264663,所以从这些方面来说,禁止中断是可以禁止内核抢占的

2.自旋锁关中断后,为什么要再禁止抢占?

在百度上找了很久答案,贴一个靠谱的回答

假设有这么个情况:

  1. CPU-1在进程A的上下文调用了spin_lock_irqsave;
  2. CPU-2调用wake_up_process唤醒了CPU-1上的进程B,由于进程B的优先级高于进程A,进程A的TIF_NEED_RESCHED标记被设置。(CPU-2还会用IPI通知CPU-1进行resched,但是CPU-1禁用了中断而不会响应);
  3. CPU-1调用了某某函数,这个函数包含了preempt_disable和preempt_enable(没有规定关中断的情况下不能调用这样的函数吧~);
  4. 那么,如果spin_lock_irqsave没有preempt_disable,第3步中的preempt_enable将触发preempt_check_resched,从而让进程B抢占掉进程A。
总之就是只有关了抢占,才能保证在临界区成对出现的preempt_disable()/preempt_enable()(preempt_enable()也是一个潜在的主动调度的测试点)不会造成伤害。不然这种代码就不能放在临界区中了。



评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值