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中

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM