linux 中断线程化

来源:互联网 发布:c语言求100以内素数和 编辑:程序博客网 时间:2024/06/05 20:31

为什么要进行中断线程化?
在 Linux 中,中断具有最高的优先级。不论在任何时刻,只要产生中断事件,内核将立即执行相应的中断处理程序,等到所有挂起的中断和软中断处理完毕后才能执行正常的任务,因此有可能造成实时任务得不到及时的处理。中断线程化之后,中断将作为内核线程运行而且被赋予不同的实时优先级,实时任务可以有比中断线程更高的优先级。这样,具有最高优先级的实时任务就能得到优先处理,即使在严重负载下仍有实时性保证。但是,并不是所有的中断都可以被线程化,比如时钟中断,主要用来维护系统时间以及定时器等,其中定时器是操作系统的脉搏,一旦被线程化,就有可能被挂起,这样后果将不堪设想,所以不应当被线程化。

中断线程化的实现方法是:对于IRQ,在内核初始化阶段init(该函数在内核源码树的文件init/main.c中定义)调用init_hardirqs(该函数在内核源码树的文件kernel/irq/manage.c中定义)来为每一个IRQ创建一个内核线程,IRQ号为0 的中断赋予实时优先级49,IRQ号为1的赋予实时优先级48,依次类推直到25,因此任何IRQ线程的最低实时优先级为25。原来的do_IRQ被分解成两部分,架构相关的放在类似于arch/*/kernel/irq.c的文件中,名称仍然为do_IRQ,而架构独立的部分被放在IRQ子系统的位置 kernel/irq/handle.c中,名称为_do_IRQ。当发生中断时,CPU将执行do_IRQ来处理相应的中断,do_IRQ将做了必要的架构相关的处理后调用_do_IRQ。函数_do_IRQ将判断该中断是否已经被线程化(如果中断描述符的状态字段不包含SA_NODELAY标志说明中断被线程化了),如果是将唤醒相应的处理线程,否则将直接调用handle_IRQ_event(在IRQ子系统位置的kernel/irq /handle.c文件中)来处理。对于已经线程化的情况,中断处理线程被唤醒并开始运行后,将调用do_hardirq(在源码树的IRQ子系统位置的文件kernel/irq/manage.c中定义)来处理相应的中断,该函数将判断是否有中断需要被处理(中断描述符的状态标志 IRQ_INPROGRESS),如果有就调用handle_IRQ_event来处理。handle_IRQ_event将直接调用相应的中断处理句柄来完成中断处理。

申请中断request_irq()request_threaded_irq()之间的区别?

static inline int __must_checkrequest_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,    const char *name, void *dev){return request_threaded_irq(irq, handler, NULL, flags, name, dev);}int request_threaded_irq(unsigned int irq, irq_handler_t handler,         irq_handler_t thread_fn, unsigned long irqflags,         const char *devname, void *dev_id)

从定义可以看出request_irqrequest_threaded_irq的一个wrapper,只是将其中的thread_fn置为空。

request_threaded_irq函数实现:

/** *  request_threaded_irq - allocate an interrupt line *  @irq: Interrupt line to allocate *  @handler: Function to be called when the IRQ occurs. *        Primary handler for threaded interrupts *        If NULL and thread_fn != NULL the default *        primary handler is installed *  @thread_fn: Function called from the irq handler thread *          If NULL, no irq thread is created *  @irqflags: Interrupt type flags *  @devname: An ascii name for the claiming device *  @dev_id: A cookie passed back to the handler function */int request_threaded_irq(unsigned int irq, irq_handler_t handler,             irq_handler_t thread_fn, unsigned long irqflags,             const char *devname, void *dev_id){    struct irqaction *action;    struct irq_desc *desc;    int retval;    if (irq == IRQ_NOTCONNECTED)        return -ENOTCONN;    /*     * Sanity-check: shared interrupts must pass in a real dev-ID,     * otherwise we'll have trouble later trying to figure out     * which interrupt is which (messes up the interrupt freeing     * logic etc).     *     * Also IRQF_COND_SUSPEND only makes sense for shared interrupts and     * it cannot be set along with IRQF_NO_SUSPEND.     */    if (((irqflags & IRQF_SHARED) && !dev_id) ||        (!(irqflags & IRQF_SHARED) && (irqflags & IRQF_COND_SUSPEND)) ||        ((irqflags & IRQF_NO_SUSPEND) && (irqflags & IRQF_COND_SUSPEND)))        return -EINVAL;    desc = irq_to_desc(irq);    if (!desc)        return -EINVAL;    if (!irq_settings_can_request(desc) ||        WARN_ON(irq_settings_is_per_cpu_devid(desc)))        return -EINVAL;    if (!handler) {        if (!thread_fn)            return -EINVAL;        handler = irq_default_primary_handler;    }    action = kzalloc(sizeof(struct irqaction), GFP_KERNEL);    if (!action)        return -ENOMEM;    action->handler = handler;    action->thread_fn = thread_fn;    action->flags = irqflags;    action->name = devname;    action->dev_id = dev_id;    chip_bus_lock(desc);    retval = __setup_irq(irq, desc, action);    chip_bus_sync_unlock(desc);    if (retval) {        kfree(action->secondary);        kfree(action);    }#ifdef CONFIG_DEBUG_SHIRQ_FIXME    if (!retval && (irqflags & IRQF_SHARED)) {        /*         * It's a shared IRQ -- the driver ought to be prepared for it         * to happen immediately, so let's make sure....         * We disable the irq to make sure that a 'real' IRQ doesn't         * run in parallel with our fake.         */        unsigned long flags;        disable_irq(irq);        local_irq_save(flags);        handler(irq, dev_id);        local_irq_restore(flags);        enable_irq(irq);    }#endif    return retval;}

其中调用了__setup_irq 函数,该函数内容如下:

/* * Internal function to register an irqaction - typically used to * allocate special interrupts that are part of the architecture. */static int__setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new){    struct irqaction *old, **old_ptr;    unsigned long flags, thread_mask = 0;    int ret, nested, shared = 0;    cpumask_var_t mask;    if (!desc)        return -EINVAL;    if (desc->irq_data.chip == &no_irq_chip)        return -ENOSYS;    if (!try_module_get(desc->owner))        return -ENODEV;    new->irq = irq;    /*     * Check whether the interrupt nests into another interrupt     * thread.     */    nested = irq_settings_is_nested_thread(desc);    if (nested) {        if (!new->thread_fn) {            ret = -EINVAL;            goto out_mput;        }        /*         * Replace the primary handler which was provided from         * the driver for non nested interrupt handling by the         * dummy function which warns when called.         */        new->handler = irq_nested_primary_handler;    } else {        if (irq_settings_can_thread(desc)) {            ret = irq_setup_forced_threading(new);            if (ret)                goto out_mput;        }    }    /*     * Create a handler thread when a thread function is supplied     * and the interrupt does not nest into another interrupt     * thread.     */    if (new->thread_fn && !nested) {        ret = setup_irq_thread(new, irq, false);        if (ret)            goto out_mput;        if (new->secondary) {            ret = setup_irq_thread(new->secondary, irq, true);            if (ret)                goto out_thread;        }    }    if (!alloc_cpumask_var(&mask, GFP_KERNEL)) {        ret = -ENOMEM;        goto out_thread;    }    /*     * Drivers are often written to work w/o knowledge about the     * underlying irq chip implementation, so a request for a     * threaded irq without a primary hard irq context handler     * requires the ONESHOT flag to be set. Some irq chips like     * MSI based interrupts are per se one shot safe. Check the     * chip flags, so we can avoid the unmask dance at the end of     * the threaded handler for those.     */    if (desc->irq_data.chip->flags & IRQCHIP_ONESHOT_SAFE)        new->flags &= ~IRQF_ONESHOT;    /*     * The following block of code has to be executed atomically     */    raw_spin_lock_irqsave(&desc->lock, flags);    old_ptr = &desc->action;    old = *old_ptr;    if (old) {        /*         * Can't share interrupts unless both agree to and are         * the same type (level, edge, polarity). So both flag         * fields must have IRQF_SHARED set and the bits which         * set the trigger type must match. Also all must         * agree on ONESHOT.         */        if (!((old->flags & new->flags) & IRQF_SHARED) ||            ((old->flags ^ new->flags) & IRQF_TRIGGER_MASK) ||            ((old->flags ^ new->flags) & IRQF_ONESHOT))            goto mismatch;        /* All handlers must agree on per-cpuness */        if ((old->flags & IRQF_PERCPU) !=            (new->flags & IRQF_PERCPU))            goto mismatch;        /* add new interrupt at end of irq queue */        do {            /*             * Or all existing action->thread_mask bits,             * so we can find the next zero bit for this             * new action.             */            thread_mask |= old->thread_mask;            old_ptr = &old->next;            old = *old_ptr;        } while (old);        shared = 1;    }    /*     * Setup the thread mask for this irqaction for ONESHOT. For     * !ONESHOT irqs the thread mask is 0 so we can avoid a     * conditional in irq_wake_thread().     */    if (new->flags & IRQF_ONESHOT) {        /*         * Unlikely to have 32 resp 64 irqs sharing one line,         * but who knows.         */        if (thread_mask == ~0UL) {            ret = -EBUSY;            goto out_mask;        }        /*         * The thread_mask for the action is or'ed to         * desc->thread_active to indicate that the         * IRQF_ONESHOT thread handler has been woken, but not         * yet finished. The bit is cleared when a thread         * completes. When all threads of a shared interrupt         * line have completed desc->threads_active becomes         * zero and the interrupt line is unmasked. See         * handle.c:irq_wake_thread() for further information.         *         * If no thread is woken by primary (hard irq context)         * interrupt handlers, then desc->threads_active is         * also checked for zero to unmask the irq line in the         * affected hard irq flow handlers         * (handle_[fasteoi|level]_irq).         *         * The new action gets the first zero bit of         * thread_mask assigned. See the loop above which or's         * all existing action->thread_mask bits.         */        new->thread_mask = 1 << ffz(thread_mask);    } else if (new->handler == irq_default_primary_handler &&           !(desc->irq_data.chip->flags & IRQCHIP_ONESHOT_SAFE)) {        /*         * The interrupt was requested with handler = NULL, so         * we use the default primary handler for it. But it         * does not have the oneshot flag set. In combination         * with level interrupts this is deadly, because the         * default primary handler just wakes the thread, then         * the irq lines is reenabled, but the device still         * has the level irq asserted. Rinse and repeat....         *         * While this works for edge type interrupts, we play         * it safe and reject unconditionally because we can't         * say for sure which type this interrupt really         * has. The type flags are unreliable as the         * underlying chip implementation can override them.         */        pr_err("Threaded irq requested with handler=NULL and !ONESHOT for irq %d\n",               irq);        ret = -EINVAL;        goto out_mask;    }    if (!shared) {        ret = irq_request_resources(desc);        if (ret) {            pr_err("Failed to request resources for %s (irq %d) on irqchip %s\n",                   new->name, irq, desc->irq_data.chip->name);            goto out_mask;        }        init_waitqueue_head(&desc->wait_for_threads);        /* Setup the type (level, edge polarity) if configured: */        if (new->flags & IRQF_TRIGGER_MASK) {            ret = __irq_set_trigger(desc,                        new->flags & IRQF_TRIGGER_MASK);            if (ret)                goto out_mask;        }        desc->istate &= ~(IRQS_AUTODETECT | IRQS_SPURIOUS_DISABLED | \                  IRQS_ONESHOT | IRQS_WAITING);        irqd_clear(&desc->irq_data, IRQD_IRQ_INPROGRESS);        if (new->flags & IRQF_PERCPU) {            irqd_set(&desc->irq_data, IRQD_PER_CPU);            irq_settings_set_per_cpu(desc);        }        if (new->flags & IRQF_ONESHOT)            desc->istate |= IRQS_ONESHOT;        if (irq_settings_can_autoenable(desc))            irq_startup(desc, true);        else            /* Undo nested disables: */            desc->depth = 1;        /* Exclude IRQ from balancing if requested */        if (new->flags & IRQF_NOBALANCING) {            irq_settings_set_no_balancing(desc);            irqd_set(&desc->irq_data, IRQD_NO_BALANCING);        }        /* Set default affinity mask once everything is setup */        setup_affinity(desc, mask);    } else if (new->flags & IRQF_TRIGGER_MASK) {        unsigned int nmsk = new->flags & IRQF_TRIGGER_MASK;        unsigned int omsk = irq_settings_get_trigger_mask(desc);        if (nmsk != omsk)            /* hope the handler works with current  trigger mode */            pr_warn("irq %d uses trigger mode %u; requested %u\n",                irq, nmsk, omsk);    }    *old_ptr = new;    irq_pm_install_action(desc, new);    /* Reset broken irq detection when installing new handler */    desc->irq_count = 0;    desc->irqs_unhandled = 0;    /*     * Check whether we disabled the irq via the spurious handler     * before. Reenable it and give it another chance.     */    if (shared && (desc->istate & IRQS_SPURIOUS_DISABLED)) {        desc->istate &= ~IRQS_SPURIOUS_DISABLED;        __enable_irq(desc);    }    raw_spin_unlock_irqrestore(&desc->lock, flags);    /*     * Strictly no need to wake it up, but hung_task complains     * when no hard interrupt wakes the thread up.     */    if (new->thread)        wake_up_process(new->thread);    if (new->secondary)        wake_up_process(new->secondary->thread);    register_irq_proc(irq, desc);    new->dir = NULL;    register_handler_proc(irq, new);    free_cpumask_var(mask);    return 0;mismatch:    if (!(new->flags & IRQF_PROBE_SHARED)) {        pr_err("Flags mismatch irq %d. %08x (%s) vs. %08x (%s)\n",               irq, new->flags, new->name, old->flags, old->name);#ifdef CONFIG_DEBUG_SHIRQ        dump_stack();#endif    }    ret = -EBUSY;out_mask:    raw_spin_unlock_irqrestore(&desc->lock, flags);    free_cpumask_var(mask);out_thread:    if (new->thread) {        struct task_struct *t = new->thread;        new->thread = NULL;        kthread_stop(t);        put_task_struct(t);    }    if (new->secondary && new->secondary->thread) {        struct task_struct *t = new->secondary->thread;        new->secondary->thread = NULL;        kthread_stop(t);        put_task_struct(t);    }out_mput:    module_put(desc->owner);    return ret;}

其中函数setup_irq_thread 为中断创建了线程,函数内容如下:

static intsetup_irq_thread(struct irqaction *new, unsigned int irq, bool secondary){    struct task_struct *t;    struct sched_param param = {        .sched_priority = MAX_USER_RT_PRIO/2,    };    if (!secondary) {        t = kthread_create(irq_thread, new, "irq/%d-%s", irq,                   new->name);    } else {        t = kthread_create(irq_thread, new, "irq/%d-s-%s", irq,                   new->name);        param.sched_priority -= 1;    }    if (IS_ERR(t))        return PTR_ERR(t);    sched_setscheduler_nocheck(t, SCHED_FIFO, &param);    /*     * We keep the reference to the task struct even if     * the thread dies to avoid that the interrupt code     * references an already freed task_struct.     */    get_task_struct(t);    new->thread = t;    /*     * Tell the thread to set its affinity. This is     * important for shared interrupt handlers as we do     * not invoke setup_affinity() for the secondary     * handlers as everything is already set up. Even for     * interrupts marked with IRQF_NO_BALANCE this is     * correct as we want the thread to move to the cpu(s)     * on which the requesting code placed the interrupt.     */    set_bit(IRQTF_AFFINITY, &new->thread_flags);    return 0;}

从函数中可以看到kthread_create 实现了中断线程的创建。

申请中断其它函数原型还有 devm_request_threaded_irq

/** *  devm_request_threaded_irq - allocate an interrupt line for a managed device */int devm_request_threaded_irq(struct device *dev, unsigned int irq,                  irq_handler_t handler, irq_handler_t thread_fn,                  unsigned long irqflags, const char *devname,                  void *dev_id){    struct irq_devres *dr;    int rc;    dr = devres_alloc(devm_irq_release, sizeof(struct irq_devres),              GFP_KERNEL);    if (!dr)        return -ENOMEM;    rc = request_threaded_irq(irq, handler, thread_fn, irqflags, devname,                  dev_id);    if (rc) {        devres_free(dr);        return rc;    }    dr->irq = irq;    dr->dev_id = dev_id;    devres_add(dev, dr);    return 0;}

可以看到devm_request_threaded 也是通过irqrequest_threaded_irq 来实现中断线程的。

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 大牙掉了一半疼怎么办 牙侧面掉了一半怎么办 后大牙掉了一半怎么办 9岁牙龈上长牙齿怎么办 2颗门牙中间黑了怎么办 牙表面有个小洞怎么办 两岁宝宝有龋齿怎么办 3岁宝宝门牙龋齿怎么办 牙齿又黑又黄怎么办 牙齿里面的黄垢怎么办 牙齿长蛀牙有洞怎么办 牙齿空了洞疼痛怎么办 牙齿破了个洞怎么办 后牙齿有个大洞怎么办 大牙烂了个洞,痛怎么办 牙黑了有洞怎么办 牙齿内侧掉了一小块怎么办 龋坏的牙齿痛怎么办 2岁半宝宝蛀牙怎么办 3岁宝宝门牙腐蚀怎么办 2岁宝宝门牙有洞怎么办 三岁宝宝有龋齿怎么办 牙龈肿痛脸肿了怎么办 孕妇牙疼的厉害怎么办 智齿引起的牙疼怎么办 蛀牙了有个大洞牙痛怎么办 长了蛀牙牙疼怎么办 面饼表面长黑点怎么办 牙结石自己掉了怎么办 狗狗得了牙结石怎么办 牙结石掉了有洞怎么办 内衣的钩子会掉怎么办? 新胸罩肩带老掉怎么办 牙齿少了一小块怎么办 儿童牙齿有黑斑要怎么办 牙齿又黄又臭怎么办 大牙烂空了很臭怎么办 牙齿已经烂没了怎么办 牙齿已经蛀没了怎么办 牙齿整个蛀掉了怎么办 小孩恒牙长歪了怎么办