据我了解,内核不是一个进程,而是一组可以从另一个进程的运行时调用的处理程序(或者由内核本身通过计时器或类似的东西调用?)
如果一个程序遇到一些异常处理程序,需要长时间运行的同步处理才能再次开始运行(例如,遇到需要读取磁盘的页面错误),内核如何识别应该切换上下文?为了实现这一目标,似乎需要运行另一个进程?
内核是否会生成一个进程,通过间歇性检查处于此状态的进程来处理此问题?调用长时间运行的同步处理程序的进程是否让内核知道它应该切换上下文直到处理程序完成(例如磁盘读取完成)?
答案1
“内核不是一个进程。”
这是纯粹的术语。 (术语很重要。)内核不是一个进程,因为根据定义,进程存在于用户空间中。但内核确实有线程。
“如果程序遇到一些异常处理程序,需要长时间运行的同步处理才能再次开始运行(例如遇到需要磁盘读取的页面错误)”。
如果用户态进程执行引用未映射内存页的机器指令,则:
处理器生成陷阱并转换到环 0/管理员模式。 (这发生在硬件中。)
陷阱处理程序是内核的一部分。假设确实必须从磁盘调入内存页,它会将进程置于不可中断睡眠状态(这意味着它将进程 CPU 状态保存在进程表中,并修改进程表中进程条目中的状态字段)进程),找到受害者内存页面,启动 I/O 以调出受害者和所请求页面中的页面,并调用调度程序(内核的另一部分)将用户态上下文切换到准备运行的另一个进程。
最终,I/O 完成。这会产生一个中断。为了响应中断,处理器调用处理程序并转换到 Ring 0/管理程序模式。 (这发生在硬件中。)
中断处理程序是内核的一部分。它清除正在等待内存页的进程的等待 I/O 状态,并将其标记为准备运行。然后,它调用调度程序将用户态上下文切换到准备运行的进程。
一般来说,内核运行:
响应硬件陷阱或中断;这包括定时器中断。
响应来自用户进程的显式系统调用。
大多数时候,处理器处于环 3/用户模式并执行来自某些用户态进程的指令。当用户态进程进行系统调用(例如,因为它想要执行某些输入/输出操作)或当硬件生成陷阱(无效的内存访问、除以零等)或从硬件收到中断请求时(I/O 完成、定时器中断、鼠标移动、数据包到达网络接口等)
为了回答标题中的问题,“内核调度程序如何知道如何抢占进程”:内核处理定时器中断。如果,当定时器中断到达时,调度程序注意到当前正在运行的用户态进程已经耗尽了它的资源。量子然后该进程被放入运行队列的末尾,并恢复另一个进程。 (一般来说,调度程序会注意确保所有准备好运行的用户态进程公平地接收处理器时间。)
答案2
如果一个程序遇到一些异常处理程序,需要长时间运行的同步处理才能再次开始运行(例如,遇到需要读取磁盘的页面错误),内核如何识别应该切换上下文?为了实现这一目标,似乎需要运行另一个进程?
如果页面错误需要磁盘读取,则内核页面错误处理程序将调用内核调度程序。
https://elixir.bootlin.com/linux/v4.17/source/mm/filemap.c#L2470
filemap_fault()
wait_on_page_locked() - usually via __lock_page_or_retry()
wait_on_page_bit()
wait_on_page_bit_common()
io_schedule()
schedule()
我还没有完全弄清楚你的这些问题的意思,但我认为第二个问题的答案是“不,不需要”。
忽略优化,内核总是会在排队读取后立即切换上下文。(即,这发生在filemap_fault()
调用之前__lock_page_or_retry()
。那里的逻辑有点复杂,但你可以毫不费力地点击它)。
如果没有其他可运行的进程,内核会将CPU切换到空闲任务。 (Linux 内核有每个 cpu空闲线程。但您看不到它们,例如top
,与 Windows“系统空闲进程”不同)。