zoukankan      html  css  js  c++  java
  • Linux中断管理 (2)软中断和tasklet

     目录:

    Linux中断管理

    Linux中断管理 (1)Linux中断管理机制

    Linux中断管理 (2)软中断和tasklet

    Linux中断管理 (3)workqueue工作队列

    关键词:TASKLET_SOFTIRQ、HI_SOFTIRQ、softirq_action、ksoftirqd、tasklet、BH

    软中断以及基于软中断的tasklet、工作队列,包括中断线程化都属于下半部机制,为什么需要下半部机制呢?

    1.硬件中断处理程序以异步方式执行,会打断其它重要代码执行,因此为了避免打断事件太久,硬件中断程序需要尽快执行完成。

    2.硬件中断处理程序通常在关中断情况下执行,即关闭了本地CPU所有中断响应。关中断之后,本地CPU不能再响应中断,因此硬件中断处理程序必须尽快执行完成。

    1. SoftIRQ软中断

    1.1 软中断数据结构

    软中断是预留给系统中对时间要求最为严格最重要的下半部使用的,系统静态定义了若干软终端类型,并且Linux内核开发者不希望用户扩充新的软终端类型。

    这里的优先级对应在__do_softirq()中执行action的顺序,低位优先得到执行。

    enum
    {
        HI_SOFTIRQ=0,------------------------最高优先级的软中断类型
        TIMER_SOFTIRQ,-----------------------Timer定时器软中断
        NET_TX_SOFTIRQ,----------------------发送网络数据包软中断
        NET_RX_SOFTIRQ,----------------------接收网络数据包软中断
        BLOCK_SOFTIRQ,
        BLOCK_IOPOLL_SOFTIRQ,----------------块设备软中断
        TASKLET_SOFTIRQ,---------------------专门为tasklet机制准备的软中断
        SCHED_SOFTIRQ,-----------------------进程调度以及负载均衡软中断
        HRTIMER_SOFTIRQ,---------------------高精度定时器软中断
        RCU_SOFTIRQ,    /* Preferable RCU should always be the last softirq */----RCU服务软中断
    
        NR_SOFTIRQS
    };

    struct softirq_action数据结构用于描述软中断,并且定义了softirq_vec[]来表示每一个软中断对应的描述符,软中断所以号就是该数组的索引。

    NR_SOFTIRQS是系统支持的软中断最大数量。

    __cacheline_aligned_in_smp用于将softirq_vec数据结构和L1缓存行对齐。

    struct softirq_action
    {
        void    (*action)(struct softirq_action *);----------------------只有一个action函数指针,当触发了该软中断,就会调用action回调函数来处理这个软中断。
    };
    
    
    static struct softirq_action softirq_vec[NR_SOFTIRQS] __cacheline_aligned_in_smp;

    irq_cpustat_t用来描述软件中断状态信息,可以理解为“软中断状态寄存器”,其实是一个unsigned int类型变量__softirq_pending。

    irq_cpustat_t irq_stat[NR_CPUS]相当于每个CPU有一个软中断状态信息变量。

    local_softirq_pending()读取当前CPU软中断状态,如果不为0说明有软中断未处理。

    or_softirq_pending()用于设置当前CPU的特定软中断处于pending状态,在__raise_softirq_irqoff()中设置。

    set_softirq_pending()可以个整个CPU软中断状态复位,常在__do_softirq()函数中执行。

    typedef struct {
        unsigned int __softirq_pending;
    } ____cacheline_aligned irq_cpustat_t;
    
    
    extern irq_cpustat_t irq_stat[];        /* defined in asm/hardirq.h */
    #define __IRQ_STAT(cpu, member)    (irq_stat[cpu].member)
    
    
      /* arch independent irq_stat fields */
    #define local_softirq_pending() 
        __IRQ_STAT(smp_processor_id(), __softirq_pending)----------------------获取当前CPU的软中断状态

    #define set_softirq_pending(x) (local_softirq_pending() = (x))
    #define or_softirq_pending(x) (local_softirq_pending() |= (x))

    1.2 软中断注册和触发

    通过调用open_softirq()函数可以注册一个软中断,其中参数nr是软中断的序号。

    void open_softirq(int nr, void (*action)(struct softirq_action *))
    {
        softirq_vec[nr].action = action;
    }

    raise_softirq()函数主动触发一个软中断API接口函数,首先设置__softirq_pending置软中断对应位,然后如果in_interrupt()为0,则唤醒ksoftirqd内核线程。

    /*
     * This function must run with irqs disabled!
     */
    inline void raise_softirq_irqoff(unsigned int nr)
    {
        __raise_softirq_irqoff(nr);
        if (!in_interrupt())
            wakeup_softirqd();-------------------------------------如果不处于中断上下文中,则尽快执行软中断处理。
    }
    
    void raise_softirq(unsigned int nr)
    {
        unsigned long flags;
    
        local_irq_save(flags);
        raise_softirq_irqoff(nr);
        local_irq_restore(flags);
    }
    
    void __raise_softirq_irqoff(unsigned int nr)
    {
        trace_softirq_raise(nr);
        or_softirq_pending(1UL << nr);-----------------------------置位nr位的软中断,表示此软中断处于pending状态。
    }

      

    1.3 软中断执行

    软中断执行机会:

    一个是在irq_exit的时候:irq_exit()->invoke_softirq()->wakeup_softirq()->唤醒ksoftirqd内核线程

    一个是在local_bh_enable的时候:local_bh_enable()->__local_bh_enable()->do_softirq()->__do_softirq(CONFIG_PREEMPT_RT_FULL)-->wkeup_softirq(在长时间执行softirq后,启动ksoftirq)

    还有一种是ksoftirqd内核线程执行函数run_ksoftirqd()中调用__do_softirq(),一般有wake_up_process()唤醒。

    软中断的执行一个重要场景是在中断退出时irq_exit(),irq_exit()首先检查是否处于进程上下文中且有pending状态的软中断,然后将工作交给invoke_softirq()

    int __handle_domain_irq(struct irq_domain *domain, unsigned int hwirq,
                bool lookup, struct pt_regs *regs)
    {
    ...
        irq_enter();
    ...
        irq_exit();
        set_irq_regs(old_regs);
        return ret;
    }
    
    void irq_exit(void)
    {
    ...
        if (!in_interrupt() && local_softirq_pending())-------------------------in_interrupt()为0表示当前不处于中断上下文,处于进程上下文中。local_softirq_pending()非0,表示有pending软中断。
            invoke_softirq();
    ...
    }
    
    static inline void invoke_softirq(void)
    {
        if (!force_irqthreads) {
            /*
             * We can safely execute softirq on the current stack if
             * it is the irq stack, because it should be near empty
             * at this stage.
             */
            __do_softirq();-----------------------------------------------------首先遍历执行处于pending状态的软中断函数;如果超出一定条件,将工作交给ksoftirqd处理。
        } else {
            wakeup_softirqd();--------------------------------------------------强制线程化情况,唤醒ksoftirqd内核线程处理。
        }
    }

    __do_softirq是软中断处理的核心,主要分为两部分。

    第一部分,尽量处理pending状态的softirq函数。

    第二部分,在处理完当前pending状态softirq之后,在处理过程中又产生了新的软中断,会重新restart进行处理;但如果超出一定条件,则交给ksoftirqd内核线程去处理。

    asmlinkage __visible void __do_softirq(void)
    {
        unsigned long end = jiffies + MAX_SOFTIRQ_TIME;
        unsigned long old_flags = current->flags;
        int max_restart = MAX_SOFTIRQ_RESTART;
        struct softirq_action *h;
        bool in_hardirq;
        __u32 pending;
        int softirq_bit;
    
        /*
         * Mask out PF_MEMALLOC s current task context is borrowed for the
         * softirq. A softirq handled such as network RX might set PF_MEMALLOC
         * again if the socket is related to swap
         */
        current->flags &= ~PF_MEMALLOC;
    
        pending = local_softirq_pending();------------------------------获取当前CPU的软中断寄存器__softirq_pending值到局部变量pending。
        account_irq_enter_time(current);
    
        __local_bh_disable_ip(_RET_IP_, SOFTIRQ_OFFSET);----------------增加preempt_count中的softirq域计数,表明当前在软中断上下文中。
        in_hardirq = lockdep_softirq_start();
    
    restart:
        /* Reset the pending bitmask before enabling irqs */
        set_softirq_pending(0);-----------------------------------------清除软中断寄存器__softirq_pending。
    
        local_irq_enable();---------------------------------------------打开本地中断
    
        h = softirq_vec;------------------------------------------------指向softirq_vec第一个元素,即软中断HI_SOFTIRQ对应的处理函数。
    
        while ((softirq_bit = ffs(pending))) {--------------------------ffs()找到pending中第一个置位的比特位,返回值是第一个为1的位序号。这里的位是从低位开始,这也和优先级相吻合,低位优先得到执行。如果没有则返回0,退出循环。
            unsigned int vec_nr;
            int prev_count;
    
            h += softirq_bit - 1;---------------------------------------根据sofrirq_bit找到对应的软中断描述符,即软中断处理函数。
    
            vec_nr = h - softirq_vec;-----------------------------------软中断序号
            prev_count = preempt_count();
    
            kstat_incr_softirqs_this_cpu(vec_nr);
    
            trace_softirq_entry(vec_nr);
            h->action(h);-----------------------------------------------执行对应软中断函数
            trace_softirq_exit(vec_nr);
            if (unlikely(prev_count != preempt_count())) {
                pr_err("huh, entered softirq %u %s %p with preempt_count %08x, exited with %08x?
    ",
                       vec_nr, softirq_to_name[vec_nr], h->action,
                       prev_count, preempt_count());
                preempt_count_set(prev_count);
            }
            h++;-------------------------------------------------------h递增,指向下一个软中断
            pending >>= softirq_bit;-----------------------------------pending右移softirq_bit位
        }
    
        rcu_bh_qs();
        local_irq_disable();-------------------------------------------关闭本地中断
    
        pending = local_softirq_pending();-----------------------------再次检查是否有软中断产生,在上一次检查至此这段时间有新软中断产生。
        if (pending) {
            if (time_before(jiffies, end) && !need_resched() &&
                --max_restart)-----------------------------------------再次触发软中断执行的三个条件:1.软中断处理时间不超过2jiffies,200Hz的系统对应10ms;2.当前没有有进程需要调度,即!need_resched();3.这种循环不超过10次。
                goto restart;
    
            wakeup_softirqd();-----------------------------------------如果上面的条件不满足,则唤醒ksoftirq内核线程来处理软中断。
        }
    
        lockdep_softirq_end(in_hardirq);
        account_irq_exit_time(current);
        __local_bh_enable(SOFTIRQ_OFFSET);----------------------------减少preempt_count的softirq域计数,和前面增加计数呼应。表示这段代码处于软中断上下文。
        WARN_ON_ONCE(in_interrupt());
        tsk_restore_flags(current, old_flags, PF_MEMALLOC);
    }

    wakeup_softirq()首先获取当前CPU的ksoftirqd线程的task_struct。

    如果当前task不处于TASK_RUNNING,则去唤醒此进程。 

    static void wakeup_softirqd(void)
    {
        /* Interrupts are disabled: no need to stop preemption */
        struct task_struct *tsk = __this_cpu_read(ksoftirqd);
    
        if (tsk && tsk->state != TASK_RUNNING)
            wake_up_process(tsk);
    }

    1.4 ksoftirqd内核线程的创建

    spawn_ksoftirqd创建于SMP初始化之前,借助smpboot_register_percpu_thread创建了每CPU内核线程ksoftirqd/xx。

    static struct notifier_block cpu_nfb = {
        .notifier_call = cpu_callback
    };
    
    static struct smp_hotplug_thread softirq_threads = {
        .store            = &ksoftirqd,
        .thread_should_run    = ksoftirqd_should_run,
        .thread_fn        = run_ksoftirqd,
        .thread_comm        = "ksoftirqd/%u",
    };
    
    static __init int spawn_ksoftirqd(void)
    {
        register_cpu_notifier(&cpu_nfb);
    
        BUG_ON(smpboot_register_percpu_thread(&softirq_threads));
    
        return 0;
    }
    early_initcall(spawn_ksoftirqd);

    smpboot_thread_fn()函数中首先判断thread_should_run(),然后再决定是否需要执行thread_fn()。

    此处的thread_should_run()即为ksoftirqd_should_run(),返回1表示有softirq处于pending,那么就会执行run_ksoftirqd()。

    static int
    __smpboot_create_thread(struct smp_hotplug_thread *ht, unsigned int cpu)
    {
    ...
        tsk = kthread_create_on_cpu(smpboot_thread_fn, td, cpu,
                        ht->thread_comm);
    ...
    }
    
    static int smpboot_thread_fn(void *data)
    {
        struct smpboot_thread_data *td = data;
        struct smp_hotplug_thread *ht = td->ht;
    
        while (1) {
            set_current_state(TASK_INTERRUPTIBLE);
    ...
            if (!ht->thread_should_run(td->cpu)) {
                preempt_enable_no_resched();
                schedule();
            } else {
                __set_current_state(TASK_RUNNING);
                preempt_enable();
                ht->thread_fn(td->cpu);
            }
        }
    }

    run_ksoftirqd()在此判断是否有softirq处于pending状态,然后调用__do_softirq()处理软中断。

    static int ksoftirqd_should_run(unsigned int cpu)
    {
        return local_softirq_pending();
    }
    
    static void run_ksoftirqd(unsigned int cpu)
    {
        local_irq_disable();
        if (local_softirq_pending()) {
            /*
             * We can safely run softirq on inline stack, as we are not deep
             * in the task stack here.
             */
            __do_softirq();
            local_irq_enable();
            cond_resched_rcu_qs();
            return;
        }
        local_irq_enable();
    }

    2. tasklet

     tasklet是利用软中断实现的一种下半部机制,本质上是一个软中断变种,运行在软中断上下文中。

    2.1 tasklet数据结构

     struct tasklet_struct是tasklet描述符。

    struct tasklet_struct
    {
        struct tasklet_struct *next;------------------多个tasklet串成一个链表。
        unsigned long state;--------------------------TASKLET_STATE_SCHED表示tasklet已经被调度,正准备运行;TASKLET_STATE_RUN表示tasklet正在运行中。
        atomic_t count;-------------------------------0表示tasklet处于激活状态;非0表示该tasklet被禁止,不允许执行。
        void (*func)(unsigned long);------------------该tasklet处理程序
        unsigned long data;---------------------------传递给tasklet处理函数的参数
    };

     每个CPU维护着两个tasklet链表,tasklet_vec用于普通优先级,tasklet_hi_vec用于高优先级;它们都是per-CPU变量。

    struct tasklet_head {
        struct tasklet_struct *head;
        struct tasklet_struct **tail;
    };
    
    static DEFINE_PER_CPU(struct tasklet_head, tasklet_vec);
    static DEFINE_PER_CPU(struct tasklet_head, tasklet_hi_vec);

      

    2.2 tasklet初始化

     tasklet初始化在start_kernel()->softirq_init()中进行,初始化tasklet_vec和tasklet_hi_vec两个链表,并注册TASKLET_SOFTIRQ和HI_SOFTIRQ两个软中断。

    那么软中断TASKLET_SOFTIRQ/HI_SOFTIRQ和tasklet_vec/tasklet_hi_vec有什么关系呢?

    他们通过tasklet_action()/tasklet_hi_action()联系起来。

    asmlinkage __visible void __init start_kernel(void)
    {
    ...
        softirq_init();
    ...
    }
    
    void __init softirq_init(void)
    {
        int cpu;
    
        for_each_possible_cpu(cpu) {
            per_cpu(tasklet_vec, cpu).tail =
                &per_cpu(tasklet_vec, cpu).head;
            per_cpu(tasklet_hi_vec, cpu).tail =
                &per_cpu(tasklet_hi_vec, cpu).head;
        }
    
        open_softirq(TASKLET_SOFTIRQ, tasklet_action);
        open_softirq(HI_SOFTIRQ, tasklet_hi_action);
    }

    两种静态初始化、一种动态初始化方法

    #define DECLARE_TASKLET(name, func, data) 
    struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(0), func, data }----count初始化为0,表示tasklet处于激活状态
    
    #define DECLARE_TASKLET_DISABLED(name, func, data) --------------------count初始化为1,表示tasklet处于关闭状态
    struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(1), func, data }
    
    
    void tasklet_init(struct tasklet_struct *t,
              void (*func)(unsigned long), unsigned long data)
    {
        t->next = NULL;
        t->state = 0;
        atomic_set(&t->count, 0);-------------------------------------------这里count为0,表示tasklet处于激活状态
        t->func = func;
        t->data = data;
    }

      

    2.3 tasklet调度和执行

    tasklet_schedule()被调用的时机大多在中断上半部中,然后将工作交给__tasklet_schedule()处理。

    __tasklet_schedule()锁中断情况下插入当前taskelt到tasklet_vec中,并触发TASKLET_SOFTIRQ软中断。

    tasklet_scheduler()中设置了当前tasklet的TASKLET_STATE_SCHED标志位,只要该tasklet没有被执行,那么即使驱动程序多次调用tasklet_schedule()也不起作用。

    因此一旦该tasklet挂入到某个CPU的tasklet_vec后,就必须在该CPU的软中断上下文中执行,直到执行完毕并清除了TASKLET_STATE_SCHED标志位,才有机会到其他CPU上运行。

    static inline void tasklet_schedule(struct tasklet_struct *t)
    {
        if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state))-----------置TASKLET_STATE_SCHED位,如果原来未被置位,则调用__tasklet_schedule()。
            __tasklet_schedule(t);
    }
    
    void __tasklet_schedule(struct tasklet_struct *t)
    {
        unsigned long flags;
    
        local_irq_save(flags);
        t->next = NULL;
        *__this_cpu_read(tasklet_vec.tail) = t;-------------------------将t挂入到tasklet_vec链表中
        __this_cpu_write(tasklet_vec.tail, &(t->next));
        raise_softirq_irqoff(TASKLET_SOFTIRQ);
        local_irq_restore(flags);
    }

    软中断执行时会按照软中断状态__softirq_pending来依次执行pending状态的软中断,当执行到TASKLET_SOFTIRQ软中断时,调用tasklet_action()。

    static void tasklet_action(struct softirq_action *a)
    {
        struct tasklet_struct *list;
    
        local_irq_disable();
        list = __this_cpu_read(tasklet_vec.head);--------------------在关中断情况下读取tasklet_vec立案表头作为临时链表list
        __this_cpu_write(tasklet_vec.head, NULL);--------------------重新初始化tasklet_vec
        __this_cpu_write(tasklet_vec.tail, this_cpu_ptr(&tasklet_vec.head));
        local_irq_enable();
    
        while (list) {-----------------------------------------------开中断情况下遍历tasklet_vec链表,所以tasklet是开中断的
            struct tasklet_struct *t = list;
    
            list = list->next;
    
            if (tasklet_trylock(t)) {--------------------------------如果返回false,表示当前tasklet已经在其他CPU上运行,这一轮将会跳过此tasklet。确保同一个tasklet只能在一个CPU上运行。
                if (!atomic_read(&t->count)) {-----------------------表示当前tasklet处于激活状态
                    if (!test_and_clear_bit(TASKLET_STATE_SCHED,
                                &t->state))--------------------------清TASKLET_STATE_SCHED位;如果原来没有被置位,则返回0,触发BUG()。
                        BUG();
                    t->func(t->data);--------------------------------执行当前tasklet处理函数
                    tasklet_unlock(t);
                    continue;----------------------------------------跳到while继续遍历余下的tasklet
                }
                tasklet_unlock(t);
            }
    
            local_irq_disable();------------------------------------此种情况说明即将要执行tasklet时,发现该tasklet已经在别的CPU上运行。
            t->next = NULL;
            *__this_cpu_read(tasklet_vec.tail) = t;-----------------把当前tasklet挂入到当前CPU的tasklet_vec中,等待下一次触发时再执行。
            __this_cpu_write(tasklet_vec.tail, &(t->next));
            __raise_softirq_irqoff(TASKLET_SOFTIRQ);----------------再次置TASKLET_SOFTIRQ位
            local_irq_enable();
        }
    }

     HI_SOFTIRQ类型的tasklet和上面基本对称,只是tasklet_vec换成了tasklet_hi_vec,TASKLET_SOFTIRQ换成了HI_SOFTIRQ。

    static inline void tasklet_hi_schedule(struct tasklet_struct *t)
    {
        if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state))
            __tasklet_hi_schedule(t);
    }
    
    void __tasklet_hi_schedule(struct tasklet_struct *t)
    {
        unsigned long flags;
    
        local_irq_save(flags);
        t->next = NULL;
        *__this_cpu_read(tasklet_hi_vec.tail) = t;
        __this_cpu_write(tasklet_hi_vec.tail,  &(t->next));
        raise_softirq_irqoff(HI_SOFTIRQ);
        local_irq_restore(flags);
    }
    static void tasklet_hi_action(struct softirq_action *a)
    {
        struct tasklet_struct *list;
    
        local_irq_disable();
        list = __this_cpu_read(tasklet_hi_vec.head);
        __this_cpu_write(tasklet_hi_vec.head, NULL);
        __this_cpu_write(tasklet_hi_vec.tail, this_cpu_ptr(&tasklet_hi_vec.head));
        local_irq_enable();
    
        while (list) {
            struct tasklet_struct *t = list;
    
            list = list->next;
    
            if (tasklet_trylock(t)) {
                if (!atomic_read(&t->count)) {
                    if (!test_and_clear_bit(TASKLET_STATE_SCHED,
                                &t->state))
                        BUG();
                    t->func(t->data);
                    tasklet_unlock(t);
                    continue;
                }
                tasklet_unlock(t);
            }
    
            local_irq_disable();
            t->next = NULL;
            *__this_cpu_read(tasklet_hi_vec.tail) = t;
            __this_cpu_write(tasklet_hi_vec.tail, &(t->next));
            __raise_softirq_irqoff(HI_SOFTIRQ);
            local_irq_enable();
        }
    }

    3. local_bh_disable/local_bh_enable

    local_bh_disable和local_bh_enable是内核中提供的关闭软中断的锁机制,它们组成临界区禁止本地CPU在中断返回前夕执行软终端,这个临界区称为BH临界区(Bottom Half critical region)。

    由于local_bh_disable()和local_bh_enable()之间的区域属于软中断上下文,因此当在临界区发生了中断,中断返回前irq_exit()判断当前软中断上下文,因而不能调用和执行pending状态的软中断。

    这样驱动代码构造的BH临界区,就不会有新的软中断来骚扰。

    static inline void local_bh_disable(void)
    {
        __local_bh_disable_ip(_THIS_IP_, SOFTIRQ_DISABLE_OFFSET);-----------------增加softirq域计数,表示内核状态进入了软中断上下文(softirq context)
    }
    
    
    static __always_inline void __local_bh_disable_ip(unsigned long ip, unsigned int cnt)
    {
    	preempt_count_add(cnt);-----------------------------------------------增加softirq域计数
    	barrier();------------------------------------------------------------防止编译器做优化
    }
    #define SOFTIRQ_OFFSET (1UL << SOFTIRQ_SHIFT)
    #define SOFTIRQ_DISABLE_OFFSET (2 * SOFTIRQ_OFFSET)

    local_bh_enable关闭BH临界区,并判断是否可以执行软中断处理。

    static inline void local_bh_enable(void)
    {
        __local_bh_enable_ip(_THIS_IP_, SOFTIRQ_DISABLE_OFFSET);
    }
    
    void __local_bh_enable_ip(unsigned long ip, unsigned int cnt)
    {
        WARN_ON_ONCE(in_irq() || irqs_disabled());-----------------------------中断中不能构造BH临界区,irqs_disabled()返回true说明处于关中断状态,也不适合BH操作。
    #ifdef CONFIG_TRACE_IRQFLAGS
        local_irq_disable();
    #endif
        /*
         * Are softirqs going to be turned on now:
         */
        if (softirq_count() == SOFTIRQ_DISABLE_OFFSET)
            trace_softirqs_on(ip);
        /*
         * Keep preemption disabled until we are done with
         * softirq processing:
         */
        preempt_count_sub(cnt - 1);------计数减去SOFTIRQ_DISABLE_OFFSET-1,留1表示关闭本地CPU抢占,接下来调用do_softirq()时不希望被其他高优先级任务抢占了或者当前任务被迁移到其它CPU上。
    
        if (unlikely(!in_interrupt() && local_softirq_pending())) {
            /*
             * Run softirq if any pending. And do it in its own stack
             * as we may be calling this deep in a task call stack already.
             */
            do_softirq();-------------------------------------------------------非中断上下文环境中执行软中断
        }
    
        preempt_count_dec();----------------------------------------------------打开抢占
    #ifdef CONFIG_TRACE_IRQFLAGS
        local_irq_enable();
    #endif
        preempt_check_resched();
    }

    local_bh_disabled()/local_bh_enable()是关BH接口API,运行在进程上下文中。

    4. 小结 

    tasklet基于softirq,但是tasklet和softirq又存在一些区别。

      softirq tasklet
    分配 softirq是静态定义的 tasklet既可以静态定义,也可以通过tasklet_init()动态创建。
    并发性 softirq是可重入的,同一类型的软中断可以在多个CPU上并发执行。

    tasklet是不可重入的,tasklet必须串行执行,同一个tasklet不可能同时在两个CPU上运行。

    tasklet通过TASKLET_STATE_SCHED和TASKLET_STATE_RUN保证串行

    运行

    softirq运行在开中断环境下。

    软中断回调函数不能睡眠,因为软中断可能处于中断上下文中,睡眠导致Linux无法调度。

    软中断的执行时机可能在中断返回时,即退出中断上下文时。或者local_bh_enable()中。

    taskelt执行时机在softirq中
  • 相关阅读:
    使用Boost::ptime构建高精度计时器
    static和extern
    通用js地址选择器
    js模拟抛出球运动
    前端用Webpact打包React后端Node+Express实现简单留言版
    webpack 打包一个简单react组件
    img及父元素(容器)实现类似css3中的background-size:contain / background-size:cover
    通用js函数集锦<来源于网络> 【二】
    通用js函数集锦<来源于网络/自己> 【一】
    向上滚动或者向下滚动分页异步加载数据(Ajax + lazyload)[上拉加载组件]
  • 原文地址:https://www.cnblogs.com/arnoldlu/p/8659986.html
Copyright © 2011-2022 走看看