kernel 中断

中断#

中断分 向量中断非向量中断##

  • 向量中断由硬件提供中断服务程序入口地址
  • 非向量中断由软件提供中断服务程序入口地址

程序架构##

  • top half: 紧急的硬件操作(调度 bottom half),不可中断
  • bottom half: 延缓的耗时操作,可响应新的中断

request & free##

flags若设置 IRQF_SHARED ,则表示多个设备共享中断

使能和屏蔽中断##

disable_irq_nosync()disable_irq() 的区别在于前者立即返回,而后者等待目前的中断处理完成。由于 disable_irq() 会等待指定的中断号被处理完,因此如果在n号中断顶半部调用 disable_irq(n) ,会引起系统死锁

底半部##

  1. tasklet
  2. 工作队列
  3. 软中断
  4. 线程化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() 将当前进程添加到等待队列,超时发生时,进程唤醒

你可能感兴趣的:(kernel 中断)