第7章 中断和中断处理

7.9 中断控制

Linux内核提供了一组接口用于操作机器上的中断状态。这些接口为我们提供了能够禁止当前处理器的中断系统,或屏蔽掉整个机器的一条中断线的能力,这些例程都是与体系结构相关的,可以在<asm/system.h>和<asm/irq.h>中找到。

一般来说,控制中断系统的原因归根结底是需要提供同步。通过禁止中断,可以确保某个中断处理程序不会抢占当前的代码。此外,禁止中断还可以禁止内核抢占。然而,不管是禁止中断还是禁止内核抢占,都没有提供任何保护机制来防止来自其他处理器的并发访问。Linux支持多处理器,因此,内核代码一般都需要获取某种锁,防止来自其他处理器对共享数据的并发访问。获取这些锁的同时也伴随着禁止本地中断。锁提供保护机制,防止来自其他处理器的并发访问,而禁止中断提供保护机制,则是防止来自其他中断处理程序的并发访问。

7.9.1 禁止和激活中断

用于禁止当前处理器上的本地中断,随后又激活它们的语句为:

local_irq_disable();

/*禁止中断*/

local_irq_enable();

这两个函数通常以单个汇编指令来实现。

如果在调用local_irq_disable()例程之前已经禁止了中断,那么该例程往往会带来潜在的危险;同样相应的local_irq_enable()例程也存在潜在危险,因为它将无条件地激活中断,尽管这些中断可能在开始时就是关闭的。所以我们需要一种机制把中断恢复到以前的状态而不是简单地禁止或激活。内核普遍关心这点是因为,内核中一个给定的代码路径既可以在中断激活的情况下达到,也可以在中断禁止的情况下达到,这取决于具体的调用链。例如,想象一下前面的代码片段是一个大函数的组成部分。这个函数被另外两个函数调用:其中一个函数禁止中断,而另一个函数不禁止中断。因为随着内核的不断增长,要想知道到达这个函数的所有代码路径将变得越来越困难,因此,在禁止中断之前保存中断系统的状态会更加安全一些。相反,在准备激活中断时,只需把中断恢复到它们原来的状态。

unsigned long flags;

local_irq_save(flags);

/**/

local_irq_restore(flags);

这些方法至少部分要以宏的形式实现,因此表面上flags参数是以值传递的。该参数包含具体体系结构的数据,也就是包含中断系统的状态。至少有一种体系结构把栈信息与值相结合(SPARC),因此flags不能传递给另一个函数。基于这个原因,对local_irq_save()和对local_irq_restore()的调用必须在同一个函数中进行。

备注:前面的所有函数既可以在中断中调用,也可以在进程上下文中调用。

7.9.2 禁止指定中断线

在前面的内容中,看到了禁止整个处理器上所有中断的函数。在某些情况下,只禁止整个系统中一条特定的中断线就够了。这就是所谓的屏蔽掉(masking out)一条中断线。为此,Linux提供了如下接口:

void disable_irq(unsigned int irq);

void disable_irq_nosync(unsigned int irq);

void enable_irq(unsigned int irq);

disable_irq和disable_irq_nosync禁止中断控制器上指定的中断号,即禁止给定中断向系统中所有处理器的传递。另外,函数只有在当前正在执行的所有处理程序完成后,disable_irq()才能返回。因此,调用者不仅要确保不在指定线上传递新的中断,同时还要确保所有已经开始执行的处理程序已全部退出。disable_irq_nosync()不会等待当前中断处理程序执行完毕。

对这些函数的调用可以嵌套。但要记住在一条指定的中断线上,对disable_irq或disable_irq_nosync的每次调用,都需要相应地调用一次enable_irq。只有在对enable_irq完成最后一次调用后,才真正重新激活了中断线。例如,如果disable_irq被调用了两次,那么直到第二次调用enable_irq后,才能真正地激活中断线。

所有者三个函数可以从中断或进程上下文中调用,而且不会睡眠。但如果从中断上下文中调用,要特别小心。例如,当你正在处理一条中断线时,并不想激活它。

禁止多个中断处理程序共享的中断线是不合适的。禁止中断线也就禁止了这条线上所有设备的中断传递。因此,用于新设备的驱动程序应该倾向于不使用这些接口。根据规范,PCI设备必须支持中断线共享,因此,它们根本不应该使用这些接口。

7.9.3 中断系统的状态

通常有必要了解中断系统的状态(如中断是禁止的还是激活的),或者当前是否正处于中断上下文的执行状态中。如果本地处理器上的中断系统被禁止,则它返回非0;否则返回0。

在<linux/hardirq.h>中定义的两个宏提供一个用来检查内核的当前上下文的接口,它们是:

#define in_interrupt()          (irq_count())

#define in_irq()                (hardirq_count())

in_interrupt宏最有用:如果内核处于任何类型的中断处理中,它返回非0,说明内核此刻正在执行中断处理程序,或者正在执行下半部处理程序。宏in_irq只有在内核确实正在执行中断处理程序时才返回非0。

通常情况下,要检查自己是否处于进程上下文中。也就是说,希望确保自己不在中断上下文中。这种情况很常见,因为代码要做一些像睡眠这样只能从进程上下文中做的事。如果in_interrupt()返回0,则此刻内核处于进程上下文中。

表7-2 中断控制方法的列表

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值