LInux中ThreadInfo中的preempt_count字段


最近看各種上下文,發現和ThreadInfo中的preemption字段密切,於是便調查了下。

看下Linux源碼中的注釋:

/*
* We put the hardirq and softirq counter into the preemption
* counter. The bitmask has the following meaning:
*
* - bits 0-7 are the preemption count (max preemption depth: 256)
* - bits 8-15 are the softirq count (max # of softirqs: 256)
*
* The hardirq count can in theory reach the same as NR_IRQS.
* In reality, the number of nested IRQS is limited to the stack
* size as well. For archs with over 1000 IRQS it is not practical
* to expect that they will all nest. We give a max of 10 bits for
* hardirq nesting. An arch may choose to give less than 10 bits.
* m68k expects it to be 8.
*
* - bits 16-25 are the hardirq count (max # of nested hardirqs: 1024)
* - bit 26 is the NMI_MASK
* - bit 27 is the PREEMPT_ACTIVE flag
*
* PREEMPT_MASK: 0x000000ff
* SOFTIRQ_MASK: 0x0000ff00
* HARDIRQ_MASK: 0x03ff0000
* NMI_MASK: 0x04000000
*/

這里其實把preempt_count划分成了四部分:搶占計數器、軟中斷計數、硬件中斷計數、NMI計數。

搶占計數器:0-7位

軟中斷計數器:8-15位

硬中斷計數器:16-25位

NMI標識:26位

基於上面的信息,看下Linux內核中判斷各個上下文的宏:

#define hardirq_count()    (preempt_count() & HARDIRQ_MASK)
#define softirq_count()    (preempt_count() & SOFTIRQ_MASK)
#define irq_count()    (preempt_count() & (HARDIRQ_MASK | SOFTIRQ_MASK   | NMI_MASK))

 可以看到這里判斷硬件中斷/軟中斷/中斷上下文的實質,就是根據thread_info結構中的preempt_count字段,額忘記列出preempt_count()的實現了:#define preempt_count() (current_thread_info()->preempt_count),這時明白了吧!!!

不過后面幾個掩碼的計算有點繞,參考下源碼:

#define __IRQ_MASK(x)    ((1UL << (x))-1)

#define PREEMPT_MASK    (__IRQ_MASK(PREEMPT_BITS) << PREEMPT_SHIFT)
#define SOFTIRQ_MASK    (__IRQ_MASK(SOFTIRQ_BITS) << SOFTIRQ_SHIFT)
#define HARDIRQ_MASK    (__IRQ_MASK(HARDIRQ_BITS) << HARDIRQ_SHIFT)
#define NMI_MASK    (__IRQ_MASK(NMI_BITS)     << NMI_SHIFT)

 

實質上就是取對應的位。

 

*
 * Are we doing bottom half or hardware interrupt processing?
 * Are we in a softirq context? Interrupt context?
 * in_softirq - Are we currently processing softirq or have bh disabled?
 * in_serving_softirq - Are we currently processing softirq?
 */
#define in_irq()        (hardirq_count())
#define in_softirq()        (softirq_count())
#define in_interrupt()        (irq_count())
#define in_serving_softirq()    (softirq_count() & SOFTIRQ_OFFSET)

 

而這里只是對上面各種計數的包裝,只要對應的計數不為0,則表示在對應的上下文中。in_interrupt表示中斷上下文,包括了硬件中斷、軟中斷和NMI中斷。in_serving_softirq判斷是否當前正在處理軟中斷。

#define in_nmi()    (preempt_count() & NMI_MASK)

 

in_nmi判斷當前是否在NMI中斷上下文。

 

既然如此,咱們再結合內核搶占調度的情況,看下搶占調度函數

asmlinkage void __sched notrace preempt_schedule(void)
{
    struct thread_info *ti = current_thread_info();

    /*
     * If there is a non-zero preempt_count or interrupts are disabled,
     * we do not want to preempt the current task. Just return..
     */
    if (likely(ti->preempt_count || irqs_disabled()))
        return;

    do {
        add_preempt_count_notrace(PREEMPT_ACTIVE);
        __schedule();
        sub_preempt_count_notrace(PREEMPT_ACTIVE);

        /*
         * Check again in case we missed a preemption opportunity
         * between schedule and now.
         */
        barrier();
    } while (need_resched());
}

 

可以看到這里進入函數起始,便對當前線程的preempt_count進行了判斷,根據上面的介紹,當內核處理任何一個上下文中時,preempt_count均不可能為0,所以我們也可以根據此發現在軟中斷、硬件中斷、禁止內核搶占、NMI上下文中均不允許調度。當然,我們看到后面后又個irqs_disabled,意味着如果當前禁用了硬件中斷,則同樣不會發生搶占調度。

 補充:

關閉中斷的實質是設置EFLAGS寄存器的IF標志位,CLI指令可執行這一操作,當標志位被設置為0時,表示當前關閉中斷狀態,而STI指令開中斷,二者匹配使用。這里的中斷指的是可屏蔽的硬件中斷。

 

 

參考資料:

LInux3.10.1內核源碼

 


免責聲明!

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



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