第七章 中断和中断处理

来源:互联网 发布:购买已备案的域名 编辑:程序博客网 时间:2024/05/14 09:39

什么是中断

中断本质上是一种特殊的电信号,由硬件设备发向处理器,处理器接收到中断后,会马上向操作系统反应此信号到来。硬件设备产生中断的时候并不考虑处理器的时钟同步,因此中断是异步的,换句话说中断随时可能产生。

什么是异常

异常一般由处理器本身引起(异常是同步中断),比如运算中的除0操作。

中断处理程序——中断上下文、原子上下文

中断处理程序与其他内核函数的真正区别在于,中断处理程序是被内核调用来相应中断的,而它们运行于我们称之为中断上下文的特殊上下文中,中断上下文偶尔也被称作原子上下文。

上半部和下半部

中断可能随时发生,因此中断处理程序也就随时可能执行,所以必须保证中断处理程序能够快速执行,这样才能保证尽快恢复被中断代码的执行。因此,对硬件而言,操作系统能够迅速对其中断进行服务非常重要;当然对于系统的其它部分而言,让中断处理程序在尽可能短的时间内完成也同样重要。
又想中断处理的快,同时也想在中断处理程序中完成的工作量多该如何处理?两者显然相抵触,所以我们一般把中断切为两部分。中断处理程序是上半部(top half)——接收到一个中断它就立刻执行,但只做严格时限的工作。能够被允许稍后完成的工作会推迟到下半部(bottom half)去,在合适的时机,下半部会执行。

中断相关函数

注册中断

/* * irg     - 表示要分配的中断号 * handler - 实际的中断处理程序 * flags   - 标志位,表示此中断的具有特性 * name    - 中断设备名称的ASCII 表示,这些会被/proc/irq和/proc/interrupts文件使用 * dev     - 用于共享中断线,多个中断程序共享一个中断线时(共用一个中断号),依靠dev来区别各个中断程序 * 返回值: * 执行成功:0 * 执行失败:非0 */int request_irq(unsigned int irq,                irq_handler_t handler,                unsigned long flags,                const char* name,                void *dev)

注意:
request_irq()函数可能会睡眠(根本原因是调用了kmalloc(),kmalloc()是可以睡眠的),绝不能再中断上下文或其它不允许阻塞的代码中调用该函数。

释放中断

void free_irq(unsigned int irq, void *dev)
  • 如果不是共享中断线,则直接删除irq对应的中断线。
  • 如果是共享中断线,则判断此中断处理程序是否中断线上的最后一个中断处理程序,
    • 是最后一个中断处理程序 -> 删除中断线和中断处理程序
    • 不是最后一个中断处理程序 -> 删除中断处理程序

重入和中断处理程序

Linux 中的中断处理程序是无须重入的。当一个给定的中断处理程序正在执行时,相应的中断线在所有的处理器上都会被屏蔽掉,以防止在同一个中断线上接收另一个新的中断。通常情况下,所有的其他的中断都是打开的,所以这些不同中断线上的其它中断都能被处理,但是当前中断线总是被禁止的。由此可见,同一个中断处理程序绝对不会被同时调用以处理嵌套的中断,简化了中断处理程序的编写。

禁止中断

禁止本处理器上的全部中断

local_irq_disable()local_irq_enable()

以上两个函数会无条件的禁止和使能中断,如果本身中断已经禁止,可能会带来潜在的危险。下面这两个函数会更安全一些。

unsigned long flags;local_irq_save(flags);//禁止中断,保存状态local_irq_restore(flags);//恢复状态

注意:
flags 为 unsigned long 类型,不能传递给另一个函数,
local_irq_save(flags);local_irq_restore(flags);必须成对的在同一个函数中运行,它们既可以在中断上下文使用,也可以在进程上下文使用。

禁止指定的中断线

void disable_irq(unsigned int irq)void disable_irq_nosync(unsigned int irq)void enable_irq(unsigned int irq)void synchronize_irq(unsigned int irq)

disable_irq()只有在相应的中断处理程序完成后才能返回,如果在当前中断线的处理程序中使用disable_irq()来关闭当前中断线,一方面没必要,第二也会造成内核死掉。
disable_irq_nosync()不会等待对应的中断处理程序执行完毕就返回。
关闭中断和使能中断最好成对的使用,如果你连续两次调用禁止中断,那么你必须也得同样调用两次使能中断才能真正的使能。

void disable_irq(unsigned int irq)
void disable_irq_nosync(unsigned int irq)
void enable_irq(unsigned int irq)
以上三个函数可以在中断上下文和进程上下文中使用

void synchronize_irq(unsigned int irq)
会休眠等待,因此不能用在中断上下文和任何不允许阻塞的上下文中。

中断系统状态

in_interrupt() 是判断当前内核是否处于中断上下文,这个中断上下文包括底半部和硬件中断处理过程,函数实现:

#define in_interrupt() ({ const int __cpu = smp_processor_id(); /          (local_irq_count(__cpu) + local_bh_count(__cpu) != 0); })

判断中断计数和底半部计数是否〉0,如果只希望判断是否在硬件中断上下文,则可以使用:in_irq()。

1 0
原创粉丝点击