所有 Unix 内核都是可重入的:多个进程可能同时在内核模式下执行。如何在代码中实现这种效果?我应该如何处理许多进程调用系统调用,在内核模式下挂起的情况?
1 回答
[编辑-“可重入”一词有几种不同的含义。这个答案使用了基本的“多个上下文可以同时执行相同的代码”。这通常适用于单个例程,但可以扩展到适用于一组协作例程,通常是共享数据的例程。一个极端的例子是应用于一个完整的程序——一个网络服务器,或者一个操作系统。如果 Web 服务器一次只能处理一个客户端,则它可能被认为是不可重入的。(呃!)如果一次只有一个进程/线程/处理器可以执行内核代码,则操作系统内核可能被称为不可重入。
类似的操作系统发生在向多处理器系统的过渡期间。许多人经历了从为单处理器编写到单锁保护一切(即不可重入)的缓慢过渡,经历了越来越细粒度的锁定的各个阶段。IIRC,Linux 终于摆脱了“大内核锁”。版本 2.6.37 - 但在此之前大部分时间已经消失,只是保护尚未转换为多处理实现的残余。
这个答案的其余部分是根据单个例程编写的,而不是完整的程序。]
如果你在用户空间,你不需要做任何事情。你调用任何你想要的系统调用,正确的事情就会发生。
所以我假设你在问内核中的代码。
从概念上讲,它相当简单。当多个线程调用同一个子例程时,它也与用户空间中的多线程程序中发生的情况几乎相同。(假设它是一个 C 程序——其他语言可能有不同的命名机制。)
当系统调用实现使用自动(堆栈)变量时,它有自己的副本——重入没有问题。当它需要使用全局数据时,它通常需要使用某种锁定 - 所需的特定锁定取决于它正在使用的特定数据,以及它对这些数据所做的事情。
这都是非常通用的,所以也许一个例子可能会有所帮助。
假设系统调用要修改进程的某些属性。该过程由 a 表示,struct task_struct
它是各种链表的成员。这些链表受tasklist_lock
. 您的系统调用获得tasklist_lock
,找到正确的进程,可能获得控制它关心的字段的每个进程的锁,修改该字段,并丢弃两个锁。
还有一个细节,即执行不同系统调用的进程的情况,它们彼此不共享数据。通过合理的实现,完全没有冲突。一个进程可以让自己进入内核来处理它的系统调用,而不会影响其他进程。我不记得专门查看过 linux 实现,但我认为它是“合理的”。类似于进入异常处理程序的陷阱,它在表中查找子例程来处理请求的特定系统调用。该表有效const
,因此不需要锁。