1. 异常与中断(1) 异常是同步的,中断是异步的。
(2) 异常是因为一段错误的代码等软件层次的问题造成的,由内核负责处理,而中断是由硬件产生的,不同的设备与不同的中断通过一个独一无二的数字关联起来。
2. 中断上下文
内核响应中断后,会运行在一个特殊的上下文环境中,在该环境下,执行的代码不能如进程上下文那样阻塞或者睡眠。
3. 上半部和下半部
响应中断的逻辑被分成上半部和下半部两部分。上半部负责处理非常重要的,时间紧迫的,与体系结构相关的工作,其他相对可以延后的事情分给下半部去做。如网卡从网络上接收数据包,上半部需要立即响应硬件,将数据包从网卡的缓冲区中拷贝到主存,然后通知网卡可以接收更多的数据了。当将数据包拷贝到主存后,中断处理函数就可以返回了,从而将控制转为被中断的函数,而对数据包的处理在之后交给下半部来做。
内核资料参考: Due to various limitations, however, interrupt handlers can form only the first half of any interrupt processing solution. These limitations include:
(1) Interrupt handlers run asynchronously and thus interrupt other, potentially important code, including other interrupt handlers. Therefore, to avoid stalling the interrupted code for too long, interrupt handlers need to run as quickly as possible.
(2) Interrupt handlers run with the current interrupt level disabled at best, and at worst with all interrupts on the current processor disabled. As disabling interrupts prevents hardware from communicating with the operating systems, interrupt handlers need to run as quickly as possible.
(3) Interrupt handlers are often timing-critical because they deal with hardware.
(4) Interrupt handlers do not run in process context; therefore, they cannot block. This limits what they can do.
5. 共享中断线
外设发送给中断控制器的电信号,再经过中断控制器翻译成数字信号以后,发送给cpu的数值,这个数值就是中断号。
系统为每个中断请求线分配了一个号IRQ number,x86采用了两个8259A,共15个中断号。随着外设的增加,已有的中断线不能满足需求,这时需要多个外设共享一个中断线。
内核资料参考:when the kernel receives an interrupt, it invokes sequentially each registered handler on the line. Therefore, it is important that the handler be capable of distinguishing whether it genertated a given interrupt. The handler must quickly exit if its associated device did not generate the interrupt. This requires the hardware device to have a status register(or similar mechanism) that the handler can check. Most hardware does indeed have such a feature.
4. 注册与删除一个中断处理函数
- int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char *name, void *dev) //register a given interrupt handler on a given interrupt line
- void free_irq(unsigned int irq, void *dev) //unregister a given interrupt handler; if no
- //handlers remain on the line, the given interrupt //line is disabled.
- typedef irqreturn_t (*irq_handler_t)(int, void *)
Note: request_irq() can sleep
5. 中断处理函数(interrupt handler)
中断处理函数在linux中可以不是可重入的。因为当一个中断处理函数在执行时,它对应的中断线在所有处理器上都被屏蔽了,由此阻止了相同中断线上响应其他中断。
- static irqreturn_t intr_handler(int irq, void *dev)
irqreturn_t: 中断处理函数可以返回两个值,IRQ_NONE和IRQ_HANDLER。
- IRQ_RETVAL(val) //如果val非0,则返回IRQ_HANDLED,否则,返回IRQ_NONE
6. 中断栈(interrupt stack)
默认,中断处理函数共享被它们中断的进程的内核栈空间。内核栈为两个page大小。通过配置选项,可以将内核栈与中断栈分开,各分配一个page。
7. 中断处理流程
设备通过总线发送一个电信号给中断控制器,触发一个中断。如果中断线是enable的,中断控制会把中断发送给处理器。如果中断在处理器中没有被disable,这时处理器立即停止当前的工作,disable中断,同时跳转到一个预定义的内存地址,执行从那行开始的代码。这个预定义的位置是由内核创建的,被称为中断处理函数的entry point。对于每一个中断线,处理器会跳转到内存中独一无二的地址,执行那里的代码,通过这种方式,内核知道触发的中断的IRQ值。entry point就是保存这个IRQ值以及当前在中断栈上寄存器中值的。接着,内核开始执行do_IRQ()。
8. /proc/interrupts
Procfs is a virtual filesystem that exists only in kernel memory and is typically mounted at /proc.
9. 中断控制
(1) disabling interrupts also disables kernel preemption. Neither disabling interrupt delivery nor disabling kernel preemption provides any protection from concurrent access from another processor, however.
(2) The lock provides protection against concurrent access from another processor, whereas disabling interrupts provides protection against concurrent access from a possible interrupt handler.
10. 中断控制函数
(1) 禁止或者启用中断
- local_irq_disable(); //To diable interrupts locally for the only current processor
- /*...*/
-
local_irq_enable(); //unconditionally reenable local interrupts
- unsigned long flags;
- //the call to save and the call to restore interrupts must occur int the same function
-
local_irq_save(flags); //interrupts are now disabled
-
/*...*/
-
local_irq_restore(flags); //interrupts are restored to their previous state
(2) 禁止某一中断线(disabling a specific interrupt line or masking out an interrupt line)
- //前两个函数禁止给定的中断在所有处理器上传输,前者会等待当前执行的中断处理函数完成后返回,后者不会
- void disable_irq(unsigned int irq);
-
void disable_irq_nosync(unsigned int irq);
- //如果disable_irq调用两次,则enable_irq也要调用两次
-
void enable_irq(unsigned int irq);
- //等待某个特定的中断处理退出
-
void synchronize_irq(unsigned int irq);
(3) 中断系统的状态
- //Returns nonzero if in interrupt context and zero if in process context
- in_interrupt()
- //Returns nonzero if currently executing an interrupt handler and zero otherwise
-
in_irq()
阅读(2474) | 评论(0) | 转发(0) |