切换到用户模式时,非抢占式和抢占式内核有什么区别?

What is the difference between nonpreemptive and preemptive kernels, when switching to user mode?

我正在阅读 "Understanding the Linux Kernel, 3rd Edition",在第 5 章第 "Kernel Preemption" 节中,它说:

All process switches are performed by the switch_to macro. In both preemptive and nonpreemptive kernels, a process switch occurs when a process has finished some thread of kernel activity and the scheduler is invoked. However, in nonpreemptive kernels, the current process cannot be replaced unless it is about to switch to User Mode.

这里我还是看不出非抢占式和抢占式内核的区别,因为无论如何都需要等待当前进程切换到用户态。

假设有一个内核模式的进程p 运行,它的时间片到期,然后调用scheduler_tick(),它设置NEED_RESCHED p 的标志。 但是 schedule() 仅在 p 切换到用户模式时调用(对吗?)。

如果 p 永远不会切换到用户模式怎么办?

如果它切换到用户模式,但在 scheduler_tick() 设置 NEED_RESCHEDp 之间需要 "long" 时间实际上切换到用户模式 ​​- 然后它使用了超过它的量子?

切换进程有两种方式:

  1. 该过程产生 CPU;或
  2. 操作系统对进程说 "you're done for now."

第一种发生在进程执行某些不允许其继续的操作时。例如,执行 SLEEP 类型的函数或执行 I/O(例如到磁盘或终端并且必须等待用户响应)。

第二次发生在操作系统的内部计时器关闭时,作为处理计时器中断的一部分,O/S 确定另一个进程应该 运行。

只处理第一种上下文切换的内核是非抢占式的。处理这两种类型的上下文切换的内核是抢占式的。

请注意,屈服需要执行系统服务。这需要触发异常来调用内核模式系统服务处理程序。

抢占需要中断。在大多数非英特尔系统上,异常和中断的处理方式相同(英特尔提供了多种处理异常的方法)。在大多数系统上,return中断和异常的过程是相同的。

两种情况下的上下文切换都发生在进程 return 进入用户模式之前。当进程恢复执行时,首先要做的是 return 从内核模式到用户模式。

However, in nonpreemptive kernels, the current process cannot be replaced unless it is about to switch to User Mode.

这是定性陈述。正常的产量顺序是:

  1. 触发异常
  2. 进入内核模式
  3. 调度到系统服务处理程序
  4. 做事
  5. 告诉 O/S 让步。 上下文切换
  6. 发生了一些事件,告诉 O/S 进程可以再次 运行。
  7. OS 在内核模式下恢复进程。
  8. 进程退出内核模式
  9. 进程在用户模式下以其愉快的方式继续。

书中的说法是#7 和#8 之间没有或很少发生。这通常是正确的,但系统服务完全有可能在那里做更多的工作。它只是不会正常发生。

在非抢占式内核中,schedule() 在 return 进入用户空间时被调用(以及系统调用阻塞的任何地方,也在空闲任务上)。

在抢占式内核中,schedule() 也会在 return 从任何中断中调用时调用,并且还会在其他一些地方调用,例如在 mutex_unlock() 慢速路径上,在某些情况下接收网络数据包时,...

举个例子,假设一个进程 A 发出一个系统调用,该系统调用被设备生成的中断中断,然后被定时器中断中断:

 process A userspace → process A kernelspace → device ISR → timer ISR
                  syscall               device IRQ    timer IRQ

当计时器 ISR 结束时,它 returns 到另一个 ISR,然后 returns 到内核空间,然后 returns 到用户空间。抢占式内核检查是否需要在每个 return 重新安排进程。非抢占式内核仅在 return 访问用户空间时才进行检查。