美文网首页
kernel 中断

kernel 中断

作者: henry_zeng | 来源:发表于2016-08-09 22:02 被阅读0次

    中断#

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

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

    程序架构##

    • 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 中断

          本文链接:https://www.haomeiwen.com/subject/dovdsttx.html