LXR | KVM | PM | Time | Interrupt | Systems Performance | Bootup Optimization

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?\n",
                   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中

 

posted on 2018-05-14 00:41  ArnoldLu  阅读(6244)  评论(0编辑  收藏  举报

导航