在做异步I/O时,内核如何判断一个I/O操作是否完成?
When doing asynchronous I/O, how does the kernel determine if an I/O operation is completed?
我问这个问题的一些背景知识。我几个小时前问过这个问题
有答案
All I/O must be done through syscalls, and the way syscalls are implemented in Go, they are always called through code that is controlled by the runtime. This means that when you call a syscall, instead of just calling it directly (thus giving up control of the thread to the kernel), the runtime is notified of the syscall you want to make, and it does it on the goroutine's behalf. This allows it to, for example, do a non-blocking syscall instead of a blocking one (essentially telling the kernel, "please do this thing, but instead of blocking until it's done, return immediately, and let me know later once the result is ready"). This allows it to continue doing other work in the meantime.
因此,根据我的理解,golang 调度程序所做的是确保它不会在等待 I/O 操作的线程上花费时间。相反,它以某种方式将责任推给了内核。
但是,我想更深入地了解这个过程,因为有很多事情我不清楚。
目前这是我的理解,可能完全错误。
- 在 goroutine 中向远程服务器发出 I/O 请求,例如 GET 请求
- Golang 进行系统调用以读取 TCP 流,这是一个阻塞操作,但它不是等待,而是要求内核在获取信息时得到通知。调度程序从其队列中删除阻塞的 goroutine
- 当内核获得所有信息后,它会将其转发给 go 进程,并让调度程序知道将 goroutine 添加回其队列。
我很难理解的是 I/O 操作是如何在不创建另一个线程的情况下完成的,以及内核实际上是如何 "knows" I/O 操作完成的。是通过轮询还是有某种中断系统?
我希望这看起来有些道理。我对这种低级别的概念还很陌生。
下面的KERNEL表示"kernel side"。它包括 OS 内核代码 + 加载的驱动程序。
假定您与远程服务器建立了 TCP 连接。这是内核如何处理异步 write/read TCP 流的示例。
当你向TCP流发送一个字节数组时,内核会将缓冲区流放入RAM并控制DMA系统将缓冲区复制到网卡。当 DMA 完成其工作时,CPU 中会调用一个中断。内核注册的中断处理程序会将信号从 DMA 转换为完成回调以写入 TCP 流方法。当然,实际的 TCP 堆栈要复杂得多。这些句子只是关于事情如何运作的想法。
对于从TCP流读取的情况,当一个数据包进入网卡时,会调用另一个中断。内核注册的另一个处理程序将中断转换为 golang 端的事件。
同样,真实的案例非常非常复杂。 OS种类多,版本多,IO操作种类多,硬件设备多
我问这个问题的一些背景知识。我几个小时前问过这个问题
有答案
All I/O must be done through syscalls, and the way syscalls are implemented in Go, they are always called through code that is controlled by the runtime. This means that when you call a syscall, instead of just calling it directly (thus giving up control of the thread to the kernel), the runtime is notified of the syscall you want to make, and it does it on the goroutine's behalf. This allows it to, for example, do a non-blocking syscall instead of a blocking one (essentially telling the kernel, "please do this thing, but instead of blocking until it's done, return immediately, and let me know later once the result is ready"). This allows it to continue doing other work in the meantime.
因此,根据我的理解,golang 调度程序所做的是确保它不会在等待 I/O 操作的线程上花费时间。相反,它以某种方式将责任推给了内核。
但是,我想更深入地了解这个过程,因为有很多事情我不清楚。
目前这是我的理解,可能完全错误。
- 在 goroutine 中向远程服务器发出 I/O 请求,例如 GET 请求
- Golang 进行系统调用以读取 TCP 流,这是一个阻塞操作,但它不是等待,而是要求内核在获取信息时得到通知。调度程序从其队列中删除阻塞的 goroutine
- 当内核获得所有信息后,它会将其转发给 go 进程,并让调度程序知道将 goroutine 添加回其队列。
我很难理解的是 I/O 操作是如何在不创建另一个线程的情况下完成的,以及内核实际上是如何 "knows" I/O 操作完成的。是通过轮询还是有某种中断系统?
我希望这看起来有些道理。我对这种低级别的概念还很陌生。
下面的KERNEL表示"kernel side"。它包括 OS 内核代码 + 加载的驱动程序。
假定您与远程服务器建立了 TCP 连接。这是内核如何处理异步 write/read TCP 流的示例。
当你向TCP流发送一个字节数组时,内核会将缓冲区流放入RAM并控制DMA系统将缓冲区复制到网卡。当 DMA 完成其工作时,CPU 中会调用一个中断。内核注册的中断处理程序会将信号从 DMA 转换为完成回调以写入 TCP 流方法。当然,实际的 TCP 堆栈要复杂得多。这些句子只是关于事情如何运作的想法。
对于从TCP流读取的情况,当一个数据包进入网卡时,会调用另一个中断。内核注册的另一个处理程序将中断转换为 golang 端的事件。
同样,真实的案例非常非常复杂。 OS种类多,版本多,IO操作种类多,硬件设备多