中断详解(七)——软中断与微任务

来源:互联网 发布:php 伪静态 编辑:程序博客网 时间:2024/04/28 14:33
 我们知道,中断处理程序被分为上半部函数和下半部函数。而软中断、微任务、工作队列都是是下半部函数的机制。(至于工作队列的方式,她是以另一个线程的方式实现的,初始化时创建,调用时唤醒,其实request_thread_irq是将中断线程化,包括利用定时器实现延时处理,这都不是在中断上下文中)
    软中断和微任务都是通过do_softirq执行的。
    中断处理在do_IRQ中调用软中断 do_softirq
unsigned int __irq_entry do_IRQ(struct pt_regs *regs){    ......    irq_exit();} void irq_exit(void)                                                                                                                     {        ......        invoke_softirq();  //调用软irq处理}   #ifdef __ARCH_IRQ_EXIT_IRQS_DISABLED# define invoke_softirq()   __do_softirq()#else# define invoke_softirq()   do_softirq()#endif


软中断:
获取软中断向量表
asmlinkage void do_softirq(void){    __u32 pending;    unsigned long flags;     // 这个函数判断,如果当前有硬件中断嵌套,或有软中断正在执行时候,则马上返回    //入口判断主要是为了和 ksoftirqd 互斥。    if (in_interrupt())        return;     local_irq_save(flags);// 关中断执行以下代码       pending = local_softirq_pending();// 判断是否有 pending 的软中断需要处理。     if (pending) // 如果有则调用 __do_softirq() 进行实际处理          __do_softirq();     local_irq_restore(flags); // 开中断继续执行 }

asmlinkage void __do_softirq(void){    struct softirq_action *h;  // 软件中断处理结构,此结构中包括了 ISR 中 注册的回调函数。       __u32 pending;    unsigned long end = jiffies + MAX_SOFTIRQ_TIME;    int cpu;    int max_restart = MAX_SOFTIRQ_RESTART;     pending = local_softirq_pending();  // 得到当前所有 pending 的软中断。    account_system_vtime(current);      /* 执行到这里要屏蔽其他软中断,每个 CPU 上同时运行的软中断只能有一个。*/    __local_bh_disable((unsigned long)__builtin_return_address(0),                SOFTIRQ_OFFSET);    lockdep_softirq_enter();     cpu = smp_processor_id(); // 针对 SMP 得到当前正在处理的 CPU restart:     // 每次循环在允许硬件 ISR 强占前,首先重置软中断的标志位。       /* Reset the pending bitmask before enabling irqs */    set_softirq_pending(0);     // 开中断运行,(注意:以前运行状态一直是关中断运行)    //这里以后的代码才可能被硬件中断抢占。       local_irq_enable();    h = softirq_vec; //获取软中断向量表     do {        if (pending & 1) {  // 如果对应的软中断设置 pending 标志则表明需要进一步处理他所注册的函数。               int prev_count = preempt_count();            kstat_incr_softirqs_this_cpu(h - softirq_vec);             trace_softirq_entry(h, softirq_vec);            h->action(h); // 在这里执行了这个软中断所注册的回调函数。              trace_softirq_exit(h, softirq_vec);            if (unlikely(prev_count != preempt_count())) {                printk(KERN_ERR "huh, entered softirq %td %s %p"                       "with preempt_count %08x,"                       " exited with %08x?\n", h - softirq_vec,                       softirq_to_name[h - softirq_vec],                       h->action, prev_count, preempt_count());                preempt_count() = prev_count;            }             rcu_bh_qs(cpu);        }        h++;// 继续找,直到把软中断向量表中所有 pending 的软中断处理完成。           pending >>= 1;    // 从代码里能看出按位右移操作,表明一次循环只处理 32 个软中断的回调函数    } while (pending);     local_irq_disable(); // 再次关中断       pending = local_softirq_pending();    if (pending) {        if (time_before(jiffies, end) && !need_resched() &&            --max_restart)  // 如果刚才触发的硬件中断注册了软中断,并且重复执行次数没有到 max_restart个 次的话            goto restart;                 //超过max_restart个硬件中断注册了软中断,满负荷,需要另开线程处理软中断        // 此函数实际是调用 wake_up_process() 来唤醒 ksoftirqd           wakeup_softirqd();      }     lockdep_softirq_exit();     account_system_vtime(current);     // 到最后才开软中断执行环境,允许软中断执行。注意:这里    // 使用的不是 local_bh_enable(),不会再次触发 do_softirq()的调用。       __local_bh_enable(SOFTIRQ_OFFSET); }

void wakeup_softirqd(void){    /* Interrupts are disabled: no need to stop preemption */    struct task_struct *tsk = __get_cpu_var(ksoftirqd); //ksoftirqd 线程     if (tsk && tsk->state != TASK_RUNNING)        wake_up_process(tsk); //唤醒ksoftirqd线程                                                                                       } static int ksoftirqd(void * __bind_cpu){    // 设置当前进程状态为可中断的状态,     set_current_state(TASK_INTERRUPTIBLE);                                           current->flags |= PF_KSOFTIRQD; // 设置当前进程不允许被挂起       while (!kthread_should_stop()) { //循环判断当前进程是否会停止        preempt_disable();// 禁止当前进程被抢占。         if (!local_softirq_pending()) {  // 首先判断系统当前没有需要处理的 pending 状态的软中断           // 没有的话在主动放弃 CPU 前先要允许抢占,因为一直是在不允许抢占状态下执行的代码。               preempt_enable_no_resched();            // 主动放弃 CPU 将当前进程放入睡眠队列,并转换新的进程执行(调度器相关不记录在此),但此进程再次被唤醒时,直接执行下一语句。            schedule();            preempt_disable(); // 当进程再度被调度时,在以下处理期间内禁止当前进程被抢占。           }         __set_current_state(TASK_RUNNING); // 设置当前进程为运行状态。               /*         * 循环判断是否有 pending 的软中断,如果有则调用 do_softirq()来做具体处理。         * 注意:这里又是个 do_softirq() 的入口点,         * 那么在 __do_softirq() 当中循环处理 10 次软中断的回调函数后,         * 如果更有 pending 的话,会又调用到这里         * 那么在这里则又会有可能去调用 __do_softirq() 来处理软中断回调函数。         * 在__do_softirq()中,处理 10 次还处理不完的话说明系统正处于繁忙状态。         * 综上,在系统非常繁忙时,这个进程将会和 do_softirq() 相互交替执行,         * 这时此进程占用 CPU 应该会非常高,         */        while (local_softirq_pending()) {            /* Preempt disable stops cpu going offline.               If already offline, we'll be on wrong CPU:               don't process */            if (cpu_is_offline((long)__bind_cpu))// 如果当前被关联的 CPU 无法继续处理则跳转                 goto wait_to_die;  // 到 wait_to_die 标记出,等待结束并退出。              // 执行 do_softirq() 来处理具体的软中断回调函数。             //如果此时有一个正在处理的软中断的话,则会马上返回 注意in_interrupt()            do_softirq();            preempt_enable_no_resched();// 允许当前进程被抢占。            // 这个函数有可能间接的调用 schedule() 来转换当前进程,而且上面已允许当前进程可被抢占。            //也就是说在处理完一轮软中断回调函数时,有可能会转换到其他进程。            cond_resched();            preempt_disable(); // 禁止当前进程被抢占。            rcu_sched_qs((long)__bind_cpu);        }// 处理完所有软中断了吗?没有的话继续循环以上步骤           preempt_enable();// 允许当前进程被抢占        set_current_state(TASK_INTERRUPTIBLE); // 设置当前进程状态为可中断的状态,    }    __set_current_state(TASK_RUNNING);// 设置当前进程为运行状态    return 0; wait_to_die:// 一直等待到当前进程被停止    preempt_enable();    /* Wait for kthread_stop */    set_current_state(TASK_INTERRUPTIBLE);        // 判断当前进程是否会被停止,如果不是的话则设置进程状态为可中断状态并放弃当前 CPU 主动转换。    // 也就是说这里将一直等待当前进程将被停止时候才结束。       while (!kthread_should_stop()) {        schedule();        set_current_state(TASK_INTERRUPTIBLE);    }   // 如果将会停止则设置当前进程为运行状态后直接返回。调度器会根据优先级来使当前进程运行。    __set_current_state(TASK_RUNNING);    return 0;}







0 0