当进程在计时器中断之前退出时,如何在 Linux 内核中进行上下文切换?
我知道如果进程正在运行并且发生计时器中断,那么如果设置了标志schedule
则会自动调用该函数,调度函数然后选择下一个要运行的进程。基本上在这种情况下,调度函数在当前进程的上下文中运行,但是当进程甚至在计时器中断之前退出时会发生什么?在这种情况下,谁调用schedule
函数?它在什么上下文中运行?
重要的是要了解计时器中断只是schedule
可能被调用的数百个不同原因之一。 只有运行时由计算主导的程序(这比您想象的要少见(才会耗尽其时间片。 程序一次只运行几微秒(是的,微秒(的情况更为常见,在系统调用中的"阻塞"、等待用户输入或其他之间。
当进程以任何方式退出时,最终总是在该进程的(内核(上下文中do_exit
调用。 do_exit
调用schedule
作为其最后一个操作,schedule
永远不会返回到该上下文。 请注意,在do_exit
的最后,有一个对schedule
的调用,紧接着是BUG();
和无限循环。
在此之前,do_exit
调用exit_notify
,它负责将SIGCHLD
发送到父进程和/或将其从对wait
的调用中释放出来。 因此,很多时候,当调用schedule
时,父进程将准备好运行,并将被选中。
do_exit
还会释放所有用户空间状态以及与进程关联的大部分内核状态,释放内存,关闭文件描述符等。 task_struct
本身必须存活到有人调用wait
,我无法确切地弄清楚内核如何决定现在可以解除分配它;此代码太复杂了。
如果进程调用_exit
,内核调用链只是sys_exit_group
到do_group_exit
do_exit
。 如果它需要致命的同步信号(例如 SIGSEGV
(,调用链要长得多,并且有一个棘手的转移。 硬件陷阱由特定于架构的代码(例如x86 do_trap
(通过force_sig_info
和send_signal
complete_signal
来执行,后者调整任务状态,然后告诉调度程序唤醒有问题的进程。 有问题的进程被唤醒,一个迷宫般的特定于架构的信号处理逻辑最终将其传递给get_signal
,调用do_group_exit
,do_exit
。 致命的异步信号(例如,在shell提示符下键入kill 12345
(从sys_kill
开始,经过kill_something_info
,group_send_sig_info
,do_send_sig_info
到send_signal
,之后一切都像上面一样进行。 在这两种情况下,complete_signal
之前的所有步骤都可能发生在任何进程上下文中,但"违规进程唤醒"之后的所有步骤都发生在该进程的上下文中。
此描述中唯一特定于 Linux 的部分是内核代码中的函数名称。 Unix的任何实现都将具有内核函数,这些函数或多或少地执行Linux的do_exit
和schedule
所做的事情,并且涉及字段_exit
,致命同步信号和致命异步信号的操作序列将可识别地相似。