个人学习笔记---linux中断控制
2015-09-10 09:26
381 查看
一般来说,控制中断系统的原因归根到底是需要提供同步。通过禁止中断可以确保某个中断处理程序不会抢占当前的代码。此外,禁止中断可以可以禁止内核抢占。然而,不管是禁止中断还是禁止内核抢占,都没有提供任何保护机制来防止来自其他处理器的并发访问。因此,内核代码一般都需要获取某种锁来防止其他处理器对共享数据的访问。锁提供保护机制,防止来自其他处理器的并发访问,而禁止中断可以防止来自其他中断处理程序的并发访问。
1. 禁止和激活当前处理器上的中断
local_irq_disable() 禁止本地中断传递
local_irq_enable() 激活本地中断传递
如果调用在local_irq_disable()例程之前已经禁止了中断,那么例程往往会带来潜在的危险;同样local_irq_enable() 也存在危险,因为他将无条件激活中断,尽管这些中断可能在开始时就是关闭的。
2. 保存当前处理器上的中断状态并禁止中断和中断状态
local_irq_save(flages) 保存本地中断传递的当前状态,然后禁止本地中断传递
local_irq_restore(flages) 恢复本地中断状态到给定的状态
flages是以值传递的,参数必须定义为unsigned long 类型,flages包含中断状态。flages必须驻留在用一个栈帧中,所以这两个函数必须在用一个函数中调用。
3. 禁止指定中断线
disable_irq() 禁止给定中断线,函数只有在当前正在执行的所有处理程序执行完成之后才返回,所以要确保该函数返回之前在该中断线上没有处理程序在运行,
disable_irq_nosync() 禁止给定中断线,不会等待当前中断处理程序执行完毕
synchronize_irq() 等待一个特定的中断处理程序的退出才返回
enable_irq() 激活给定中断线
disable_irq可以被多次嵌套调用,要想重新打开irq,enable_irq必须也要被调用同样的次数,irq_desc结构中的depth字段专门用于这两个API嵌套深度的管理。disable_irq函数的开始使用异步方式的内部函数__disable_irq_nosync(),所谓异步方式就是不理会当前该irq是否正在被处理(有handler在运行或者有中断线程尚未结束)。
void disable_irq(unsigned int irq)
{
if (!__disable_irq_nosync(irq))
synchronize_irq(irq);
}
有些中断控制器可能挂在某个慢速的总线上,所以在进一步处理前,先通过irq_get_desc_buslock获得总线锁(最终会调用chip->irq_bus_lock)
static int __disable_irq_nosync(unsigned int irq)
{
unsigned long flags;
struct irq_desc *desc = irq_get_desc_buslock(irq, &flags);
if (!desc)
return -EINVAL;
__disable_irq(desc, irq, false);
irq_put_desc_busunlock(desc, flags);
return 0;
}
然后进入内部函数__disable_irq
void __disable_irq(struct irq_desc *desc, unsigned int irq, bool suspend)
{
if (suspend) {
if (!desc->action || (desc->action->flags & IRQF_NO_SUSPEND))
return;
desc->istate |= IRQS_SUSPENDED;
}
if (!desc->depth++)
irq_disable(desc);
}
前面几句是对suspend的处理,最后两句,只有之前的depth为0,才会通过irq_disable函数,调用中断控制器的回调chip->irq_mask,否则只是简单地把depth的值加1。irq_disable函数还会通过irq_state_set_disabled和irq_state_set_masked,设置irq_data.flag的IRQD_IRQ_DISABLED和IRQD_IRQ_MASK标志。
void irq_disable(struct irq_desc *desc)
{
irq_state_set_disabled(desc);
if (desc->irq_data.chip->irq_disable) {
desc->irq_data.chip->irq_disable(&desc->irq_data);
irq_state_set_masked(desc);
}
}
disable_irq的最后,调用了synchronize_irq,该函数通过SPARC_IRQ_INPROGRESS标志,确保action链表中所有的handler都已经处理完毕,然后还要通过wait_event等待该irq所有的中断线程退出。正因为这样,在中断上下文中,不应该使用该API来关闭irq,同时要确保调用该API的函数不能拥有该irq处理函数或线程的资源,否则就会发生死锁
void synchronize_irq(unsigned int irq)
{
unsigned int cpu_irq;
cpu_irq = irq & (NR_IRQS - 1);
while (sparc_irq[cpu_irq].flags & SPARC_IRQ_INPROGRESS)
cpu_relax();
}
4. 中断状态
in_interrupt() 如果在中断上下文中,则返回非0,如果在进程上下文中,则返回0
in_irq() 如果当前正在执行中断处理程序,则返回非0,否则,返回0
1. 禁止和激活当前处理器上的中断
local_irq_disable() 禁止本地中断传递
local_irq_enable() 激活本地中断传递
如果调用在local_irq_disable()例程之前已经禁止了中断,那么例程往往会带来潜在的危险;同样local_irq_enable() 也存在危险,因为他将无条件激活中断,尽管这些中断可能在开始时就是关闭的。
2. 保存当前处理器上的中断状态并禁止中断和中断状态
local_irq_save(flages) 保存本地中断传递的当前状态,然后禁止本地中断传递
local_irq_restore(flages) 恢复本地中断状态到给定的状态
flages是以值传递的,参数必须定义为unsigned long 类型,flages包含中断状态。flages必须驻留在用一个栈帧中,所以这两个函数必须在用一个函数中调用。
3. 禁止指定中断线
disable_irq() 禁止给定中断线,函数只有在当前正在执行的所有处理程序执行完成之后才返回,所以要确保该函数返回之前在该中断线上没有处理程序在运行,
disable_irq_nosync() 禁止给定中断线,不会等待当前中断处理程序执行完毕
synchronize_irq() 等待一个特定的中断处理程序的退出才返回
enable_irq() 激活给定中断线
disable_irq可以被多次嵌套调用,要想重新打开irq,enable_irq必须也要被调用同样的次数,irq_desc结构中的depth字段专门用于这两个API嵌套深度的管理。disable_irq函数的开始使用异步方式的内部函数__disable_irq_nosync(),所谓异步方式就是不理会当前该irq是否正在被处理(有handler在运行或者有中断线程尚未结束)。
void disable_irq(unsigned int irq)
{
if (!__disable_irq_nosync(irq))
synchronize_irq(irq);
}
有些中断控制器可能挂在某个慢速的总线上,所以在进一步处理前,先通过irq_get_desc_buslock获得总线锁(最终会调用chip->irq_bus_lock)
static int __disable_irq_nosync(unsigned int irq)
{
unsigned long flags;
struct irq_desc *desc = irq_get_desc_buslock(irq, &flags);
if (!desc)
return -EINVAL;
__disable_irq(desc, irq, false);
irq_put_desc_busunlock(desc, flags);
return 0;
}
然后进入内部函数__disable_irq
void __disable_irq(struct irq_desc *desc, unsigned int irq, bool suspend)
{
if (suspend) {
if (!desc->action || (desc->action->flags & IRQF_NO_SUSPEND))
return;
desc->istate |= IRQS_SUSPENDED;
}
if (!desc->depth++)
irq_disable(desc);
}
前面几句是对suspend的处理,最后两句,只有之前的depth为0,才会通过irq_disable函数,调用中断控制器的回调chip->irq_mask,否则只是简单地把depth的值加1。irq_disable函数还会通过irq_state_set_disabled和irq_state_set_masked,设置irq_data.flag的IRQD_IRQ_DISABLED和IRQD_IRQ_MASK标志。
void irq_disable(struct irq_desc *desc)
{
irq_state_set_disabled(desc);
if (desc->irq_data.chip->irq_disable) {
desc->irq_data.chip->irq_disable(&desc->irq_data);
irq_state_set_masked(desc);
}
}
disable_irq的最后,调用了synchronize_irq,该函数通过SPARC_IRQ_INPROGRESS标志,确保action链表中所有的handler都已经处理完毕,然后还要通过wait_event等待该irq所有的中断线程退出。正因为这样,在中断上下文中,不应该使用该API来关闭irq,同时要确保调用该API的函数不能拥有该irq处理函数或线程的资源,否则就会发生死锁
void synchronize_irq(unsigned int irq)
{
unsigned int cpu_irq;
cpu_irq = irq & (NR_IRQS - 1);
while (sparc_irq[cpu_irq].flags & SPARC_IRQ_INPROGRESS)
cpu_relax();
}
4. 中断状态
in_interrupt() 如果在中断上下文中,则返回非0,如果在进程上下文中,则返回0
in_irq() 如果当前正在执行中断处理程序,则返回非0,否则,返回0
相关文章推荐
- Linux socket 初步
- 10 篇对初学者和专家都有用的 Linux 命令教程
- Linux 与 Windows 对UNICODE 的处理方式
- Ubuntu12.04下QQ完美走起啊!走起啊!有木有啊!
- 解決Linux下Android开发真机调试设备不被识别问题
- 运维入门
- 运维提升
- Linux 自检和 SystemTap
- Ubuntu Linux使用体验
- c语言实现hashmap(转载)
- Linux 信号signal处理机制
- linux下mysql添加用户
- Scientific Linux 5.5 图形安装教程
- 基于 Linux 集群环境上 GPFS 的问题诊断
- 谁是桌面王者?Win PK Linux三大镇山之宝
- vivi下重新调整分区
- Linux VS Unix:Linux欲一统天下 Unix不死
- linux下设定环境变量
- Linux下修改MySQL编码的方法