可搶佔

可搶佔是從2.5.4開始支持的。 grep一下代碼,大致有這些文件添加了CONFIG_PREEMPT的代碼

$ grep -R CONFIG_PREEMPT *
arch/i386/kernel/entry.S:#ifdef CONFIG_PREEMPT
arch/i386/kernel/entry.S:#ifdef CONFIG_PREEMPT
include/linux/smp_lock.h:#if !defined(CONFIG_SMP) && !defined(CONFIG_PREEMPT)
include/linux/smp.h:#ifndef CONFIG_PREEMPT
include/linux/spinlock.h:#ifndef CONFIG_PREEMPT
include/linux/spinlock.h:#ifdef CONFIG_PREEMPT
include/asm/hw_irq.h:#ifdef CONFIG_PREEMPT
include/asm/smplock.h:#ifdef CONFIG_PREEMPT
include/asm/smplock.h:#ifdef CONFIG_PREEMPT
include/asm-i386/hw_irq.h:#ifdef CONFIG_PREEMPT
include/asm-i386/smplock.h:#ifdef CONFIG_PREEMPT
include/asm-i386/smplock.h:#ifdef CONFIG_PREEMPT
kernel/ksyms.c:#ifdef CONFIG_PREEMPT
kernel/sched.c:#ifdef CONFIG_PREEMPT
kernel/sched.c:#if CONFIG_SMP || CONFIG_PREEMPT
kernel/sched.c:#ifdef CONFIG_PREEMPT
kernel/sched.c:#endif /* CONFIG_PREEMPT */
kernel/fork.c:#ifdef CONFIG_PREEMPT
net/socket.c:#if defined(CONFIG_SMP) || defined(CONFIG_PREEMPT)

看一下PREEMPT在make config裡的註釋,大概知道可搶佔的作用。

CONFIG_PREEMPT
  This option reduces the latency of the kernel when reacting to
  real-time or interactive events by allowing a low priority process to
  be preempted even if it is in kernel mode executing a system call.

即使在內核空間,在運行系統調用,也是可以切換上下文。

分析arch/i386/kernel/entry.S文件

ENTRY(ret_from_intr)
        GET_THREAD_INFO(%ebx)
        init_ret_intr
ret_from_exception:
        movl EFLAGS(%esp),%eax          # mix EFLAGS and CS
        movb CS(%esp),%al
    testl $(VM_MASK | 3),%eax
        jz resume_kernel                # returning to kernel or vm86-space
ENTRY(resume_userspace)
        cli                             # make sure we don't miss an interrupt setting need_resched

從中斷返回或從異常返回
IOPL為0且非Virtual 8086 Mode,返回kernel空間
IOPL非0或在Virtual 8086 Mode,返回用戶空間
PREEMPT發生在內核空間,也就是resume_kernel

[SCHEDULE]每次中斷返回都會調度?還有沒其它產生調度的路徑?

[SIGNAL]發現應用程序信號處理的路徑:

resume_userspace -> work_pending -> work_notifysig -> do_notify_resume -> do_signal

#ifdef CONFIG_PREEMPT
ENTRY(resume_kernel)
    cmpl $0,TI_PRE_COUNT(%ebx)
    jnz restore_all
    movl TI_FLAGS(%ebx),%ecx
    testb $_TIF_NEED_RESCHED,%cl
    jz restore_all
    movl SYMBOL_NAME(irq_stat)+irq_stat_local_bh_count CPU_INDX,%ecx
    addl SYMBOL_NAME(irq_stat)+irq_stat_local_irq_count CPU_INDX,%ecx
    jnz restore_all
    incl TI_PRE_COUNT(%ebx)
    sti
    call SYMBOL_NAME(preempt_schedule)
    jmp ret_from_intr
#endif

thread_info的preempt_count等於0,flags中設置了TIF_NEED_RESCHED,local irq為0,才會開中斷並調度,用preempt_schedule切換上下文

#define spin_lock(lock) \
do { \
        preempt_disable(); \
        _raw_spin_lock(lock); \
} while(0)

從include/linux/spinlock.h我們可以知道,在spin_lock之前,可搶佔被禁止了。

preemp需要思考跟schedule的關係


书籍推荐