内核如何阻塞读写系统调用?

How does kernel block read and write sys calls?

从我之前的问题我了解到当一端发送 FIN 时,内核将停止阻塞读取(因为在 FIN 之后内核不再“期望”任何数据,因此它停止阻塞)。但问题是特定于套接字的。因为 read()write() 可以用于任何文件描述符(任何设备类型,例如我可以从管道、套接字、字符设备等读取)。通过所有这些设备类型,内核会进行某种阻塞。例如。通过管道,内核阻塞 read() 直到管道的写端被 close()ed(直到它不“期望”没有更多数据进入)。但问题是,如果内核根据设备类型进行“某种阻塞”,并自行决定何时“期待”某些数据,何时不,

  1. “阻塞”部分是如何实现的?也就是说,进程如何在某个时刻停止其 执行 并“等待”输入? (这是“阻塞”的行为)。它是如何在内核级别实现的? (我怀疑某种缓冲,但真的很想知道 exec 实现)

  2. 如果内核可以阻止并停止进程的执行,我也可以在用户级别执行吗?那就是我可以以某种方式以编程方式停止进程的执行,在某处创建随机缓冲区并等待来自其他进程的输入吗?这就是我询问内核实现的原因,因为我想在用户 space.

    中模仿相同的行为

如果可能的话甚至提供一些关于内核部分的解释性资源(例如好书)(我猜是关于内核中的 IO 设备实现)。

这个话题太宽泛,无法深入讨论,但这里有一些基础知识。

主要有两件事可以停止进程:来自进程外部的中断和来自进程内部的某些指令,我称之为陷阱指令。在这两种情况下,计算机处理器的行为在很大程度上是相似的:

  • 无论何时执行进程,处理器都会不断地从内存中读取指令,执行这些指令,并维护有关进程的信息,包括下一条要执行的指令的地址以及包含数据的各种寄存器过程。
  • 当中断或其中一条陷阱指令发生时,处理器保存下一条指令的地址以及可能的进程的一些其他数据,将某些内部处理器状态设置为特权模式,并转移控制到(开始执行指令)一个特殊地址。

对于中断和陷阱,该地址可能不同,对于不同的中断和不同的陷阱,它可能不同。不管它是什么,操作系统已经在那个地址放置了特​​殊的软件。它是“处理”中断或陷阱(或跳转到执行它的软件)的软件。

该软件为被中断或被捕获的进程保存处理器中的数据。它将它存储在操作系统为每个进程维护的一些数据结构中。

现在进程不再执行。操作系统正在执行,进程不会再次执行,除非操作系统将该进程的数据从数据结构恢复到处理器寄存器,然后将控制权转移回进程的指令。这是通过一些特殊的 return-from-interrupt 或 return-from-trap 指令完成的,这些指令执行最后一点恢复进程的处理器状态,以便它可以从中断的地方恢复执行。

因此,操作系统阻止进程的方式就是不继续执行它。对于操作系统来说,进程主要是它操纵的数据,它选择如何处理这些数据。

当进程试图从套接字读取时,它会调用包含陷阱指令的例程。该例程设置了一些数据,表示进程想要从套接字读取数据,然后执行陷阱指令。陷阱导致进程停止执行,内核开始执行。内核中的陷阱处理程序查看例程传递的数据,发现它是一个读取套接字的请求。它为此调用了一些其他软件。如果该软件发现套接字没有可用数据,它只会让进程挂起,然后 return 到内核中的其他软件。

然后内核通常会查看是否有任何其他进程准备好 运行。如果是,它将选择其中之一并开始 运行ning。如果没有,它会将处理器置于低功耗模式以等待中断。

在未来的某个时候,一些数据可能会到达套接字。网络设备可能会向处理器产生中断,或者某些其他进程可能会调用例程来发送数据。无论系统以何种方式获取数据,当管理套接字的软件看到它有新数据时,它会检查它是否有进程在等待来自套接字的数据。如果是,它将告诉内核进程不再需要等待。如果没有其他东西阻止进程,它将准备好 运行。当内核决定轮到该进程再次使用处理器(或一个处理器,如果有多个处理器可用)时,它将如上所述恢复该进程。

您通常不能自己在用户代码中实现这些过程。他们需要对硬件的特权访问。操作系统通常为进程提供通信方式,您可以使用执行该通信的系统调用来实现与此类似的事情。