Lab4: Preemptive Kernel
Kernel provides system calls for user tasks. When a low priority task call a system call with long execution time. High priority task would still be blocked even it becomes runnanble from sleep. A preemptive kernel could be preempted after interrupt handling. Hence, when an interrupt handler put a higher priority task from wait queue to runqueue, it can be immediately be scheduled. It’s an important trait for real time tasks.
這裡的 interrupt 就是指 irq interrupt。
原先在 kernel 運行 kernel task 或是處理 syscall (指 user 呼叫 syscall 後切到 kernel 進行必要的程序),即使有 interrupt handler 介入,也只是作 task counter 的增減以供 reschedule() 時檢查,亦或將 wait queue 上的 task 移至 run queue。在 user task 遇到 interrupt handler 介入,在返回 user 時才會觸發 reschedule() 去 context switch。
現在需要的,就是需要 kernel 介入的 interrupt handler 在返回到 kernel 時觸發 reschedule()。
1 | void reschedule() |
Protect critical section
Preemtive kernel could be easily implemented by checking reschedule flag at the end of ISR. However, the tricky part of preemptive kernel is you should be awared about critical region. Otherwise, your data may be corrupted by another kernel routine.
當然不僅僅是在 interrupt handler 末端加上 reschedule() 而已。
先前實作 wait queue 時已經對存取 ring buffer 的 critical section 進行保護,方法是在 kernel task/syscall 內 enable/disable irq 去實作。那時 schedule() 只有兩種情況被呼叫
- user task 被 irq interrupt,回到 user 前
- kernel task/syscall 內主動呼叫 schedule
兩者的差別在於第1種 CPSR 中的 IRQ Mask bit 是 1,以此來區別是否被 irq handler 所呼叫
1 | // schedule() |
現在多了一種情況
- user task 被 irq interrupt,回到 user 前
- kernel task 被 irq interrupt,回到 kernel 前
- kernel task/syscall 內主動呼叫 schedule
第1, 2種 CPSR 中的 IRQ Mask bit 還是 1,還是能用 CPSR 來識別…咦?這麼簡單
再來是不希望 context switch 被中斷,所以也將其列入 critical section 之內,大功告成
1 | // schedule() |
Demo

1 | void task1() |
加入 preemptive kernel 後,不用再自行呼叫 reschedule(),唯一的缺點是 irq handler 切換過來時可能設上 IRQ Mask bit。
意外設上的 IRQ Mask bit
之前是從 user task → irq handler → kernel task,沒走完 irq handler 全程所以 IRQ Mask bit 還在。
現在多了保護 critical section 特意 disable irq,也會造成同樣情況。例如 kernel task 主動呼叫第一次執行的 kernel task,少了 enable irq 這步。
1 | if (!irq_disable_bit) |
解法:
- kernel task 用 wrapper 包裝起來,初次執行
enable irq() - 維持原來的作法,在 kernel task 裡加入
enable_irq()
這邊採用 2.,最省力的作法