中断#
中断分 向量中断 和 非向量中断##
- 向量中断由硬件提供中断服务程序入口地址
- 非向量中断由软件提供中断服务程序入口地址
程序架构##
- top half: 紧急的硬件操作(调度 bottom half),不可中断
- bottom half: 延缓的耗时操作,可响应新的中断
request & free##
flags若设置 IRQF_SHARED ,则表示多个设备共享中断
使能和屏蔽中断##
disable_irq_nosync() 与 disable_irq() 的区别在于前者立即返回,而后者等待目前的中断处理完成。由于 disable_irq() 会等待指定的中断号被处理完,因此如果在n号中断顶半部调用 disable_irq(n) ,会引起系统死锁
底半部##
- tasklet
- 工作队列
- 软中断
- 线程化irq
tasklet###
执行上下文是软中断
void xxx_do_tasklet(unsigned long);
DECLEAR_TASKLET(xxx_tasklet, xxx_do_tasklet, NULL);
void xxx_do_tasklet(unsigned long)
{
...
}
irqreturn_t xxx_interrupt(int irq, void *dev_id)
{
...
tasklet_schedule(&xxx_tasklet);
...
}
int __init xxx_init(void)
{
...
result = request_irq(xxx_irq, xxx_interrupt, 0, "xxx", NULL);
...
return IRQ_HANDLED;
}
void __exit xxx_exit(void)
{
...
free_irq(xxx_irq, xxx_interrupt);
...
}
工作队列###
执行上下文是内核线程,可以调度和睡眠
struct work_struct xxx_wq;
void xxx_do_work(struct work_struct *work);
void xxx_do_work(struct work_struct *work)
{
...
}
irqreturn_t irqreturn_t xxx_interrupt(int irq, void *dev_id)
{
...
schedule_work(&xxx_wq);
...
return IRQ_HANDLED;
}
int __init xxx_init(void)
{
...
result = request_irq(xxx_irq, xxx_interrupt, 0, "xxx", NULL);
INIT_WORK(&xxx_wq, xxx_do_work);
...
return IRQ_HANDLED;
}
void __exit xxx_exit(void)
{
...
free_irq(xxx_irq, xxx_interrupt);
...
}
软中断###
- 执行上下文是软中断
- 内核采用 softirq 的地方包括:
HI_SOFTIRQ TIMER_SOFTIRQ NET_RX_SOFTIRQ NET_TX_SOFTIRQ SCSI_SOFTIRQ TASKLET_SOFTIRQ
硬件中断 软中断 信号 的区别
硬中断是外部设备对CPU的中断
软中断是中断底半部的一种处理机制
信号是由内核(或其他进程)对某个进程的中断
中断优先级高于软中断,软中断又高于任何一个线程。故软中断适度线程化,可以缓解高负载下系统的响应。
threaded_irq###
- request_threaded_irq() 参数handler对应的函数执行于中断上下文,thread_fn对应的函数执行于内核线程
- 如果handler结束时,返回值是 IRQ_WAKE_THREAD ,内核会调度对应线程执行thread_fn
中断共享##
- 共享中断 IRQF_SHARED 申请成功的前提是,该中断未被申请,或该中断虽然被申请,但之前也以 IRQF_SHARED 标志申请
- request_irq() 参数dev_id以设备结构体指针最佳
- 在中断到来时,会遍历执行共享此中断的所有中断处理程序,直到某一个程序返回 IRQ_HANDLED ;故在中断处理函数中,应根据硬件寄存器的信息比对dev_id参数,迅速判断是否为本设备中断,若不是,返回 IRQ_NONE
irqreturn_t xxx_interrupt(int irq, void *dev_id)
{
int status = read_int_status(); /* 获知中断源 */
if (!is_myint(dev_id, status))
return IRQ_NONE;
...
return IRQ_HANDLED;
}
int xxx_init(void)
{
result = request_irq(sh_irq, xxx_interrupt, IRQF_SHARED, "xxx", xxx_dev);
...
}
void xxx_exit(void)
{
free_irq(xxx_irq, xxx_interrupt);
...
}
内核定时器##
- timer_list
内核定时器
struct xxx_dev {
struct cdev cdev;
...
timer_list xxx_timer;
};
xxx_func1(...)
{
struct xxx_dev *dev = filp->private;
...
init_timer(&dev->xxx_timer);
dev->xxx_timer.function = &xxx_do_timer;
dev->xxx_timer.data = (unsigned long)dev;
dev->xxx_timer.expires = jiffies + delay;
add_timer(&dev->xxx_timer);
...
}
xxx_func2(...)
{
del_timer(&dev->xxx_timer);
...
}
static void xxx_do_timer(unsigned long arg)
{
struct xxx_device *dev = (struct xxx_device *)arg;
dev->xxx_timer.expires = jiffies + delay;
add_timer(&dev->xxx_timer);
...
}
- delayed_work
对于周期性任务,除了定时器以外, delayed_work 利用工作队列和定时器实现
内核延时##
短延时###
实现原理本质上是忙等待
- ndelay()
- udelay()
- mdelay()
引起进程睡眠,类似函数的精度受系统HZ以及进程调度的影响(实质调用 schedule_timeout())
- msleep()
- msleep_interruptible()
- ssleep()
长延时###
time_before() & time_after() 判断调用时的jiffies和传入的jiffies之前或之后
防止jiffies被编译器优化,内核将其定义为volatile,避免读合并
睡着延迟###
msleep() 实质调用 schedule_out() ,实现原理是向系统添加一个定时器,在定时器处理函数 process_timeout() 中唤醒与参数对应的进程
sleep_on_timeout() & interruptible_sleep_on_timeout() 将当前进程添加到等待队列,超时发生时,进程唤醒
网友评论