在进行异步I/O时,内核如何确定I/O操作是否已完成?

huangapple go评论77阅读模式
英文:

When doing asynchronous I/O, how does the kernel determine if an I/O operation is completed?

问题

我会为你翻译以下内容:

一些关于我提问的背景信息。几个小时前,我提出了这个问题:

https://stackoverflow.com/questions/36489498/when-a-goroutine-blocks-on-i-o-how-does-the-scheduler-identify-that-it-has-stopp

其中的答案是:

> 所有的 I/O 操作必须通过系统调用来完成,而在 Go 语言中,系统调用的实现方式是通过运行时(runtime)控制的代码来调用的。这意味着当你调用一个系统调用时,运行时会被通知你想要进行的系统调用,然后代表 goroutine 执行它。这使得它能够执行非阻塞的系统调用,而不是阻塞的系统调用(基本上是告诉内核:“请执行这个操作,但不要阻塞,立即返回,并在结果准备好后通知我”)。这使得它能够在此期间继续执行其他工作。


所以根据我的理解,golang 调度器确保不会花费时间在等待 I/O 操作的线程上。相反,它将这个责任委托给内核。

然而,我想更深入地了解这个过程,因为有很多事情对我来说还不清楚。

目前我对此的理解可能完全错误。

  1. 在 goroutine 中发出 I/O 请求,比如向远程服务器发送一个 GET 请求。
  2. Golang 发起一个系统调用来读取 TCP 流,这是一个阻塞操作,但是它不会等待,而是请求内核在获取到信息后通知它。调度器会将该阻塞的 goroutine 从队列中移除。
  3. 当内核获取到所有的信息后,将其转发给 Go 进程,并通知调度器将该 goroutine 添加回队列中。

我不太理解的是,如何在不创建另一个线程的情况下进行 I/O 操作,以及内核如何“知道”I/O 操作何时完成。是通过轮询还是通过某种中断系统来实现的?

希望这有些意义。我对这种底层概念非常陌生。

英文:

Some background on why I'm asking this. I asked this question a few hours ago

https://stackoverflow.com/questions/36489498/when-a-goroutine-blocks-on-i-o-how-does-the-scheduler-identify-that-it-has-stopp

which had the answer

> 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.


So from my understanding, what the golang scheduler does is that it makes sure not to get hung up spending time on threads waiting for I/O operations. Instead, it somehow defers that responsibility to the kernel.

However, I want to get a deeper understanding of the process because there a lot of things that are unclear to me.

Right now this is my understanding, which may potentially be completely wrong.

  1. Make I/O request such as a GET request to a remote server inside goroutine
  2. Golang makes a syscall to read a TCP stream, which is a blocking operation, but instead of waiting, it asks the kernel to be notified when it's gotten the information. The scheduler removes that blocking goroutine from its queue
  3. When the kernel gets all the information it forwards it to the go process, and lets the scheduler know to add the goroutine back to its queue.

What I'm struggling to understand is how the I/O operation is done without creating another thread, and how the kernel actually "knows" the I/O operation is done. Is it through polling or is there some kind of interrupt system in place?

I hope this makes some semblance of sense. I'm very new to concepts that are this low level.

答案1

得分: 2

下面的代码是指“内核端”。它包括操作系统内核代码和加载的驱动程序。

假设您与远程服务器建立了TCP连接。以下是内核处理异步写入/读取TCP流的示例。

当您将字节数组发送到TCP流时,内核将将缓冲流放入RAM,并控制DMA系统将缓冲区复制到网络卡。当DMA完成其工作时,CPU内部会调用一个中断。内核注册的中断处理程序将将DMA的信号转换为写入TCP流方法的完成回调。当然,实际的TCP堆栈要复杂得多。这些句子只是说明事物运作方式的想法。

对于从TCP流中读取的情况,当网络卡上出现一个数据包时,会触发另一个中断。内核注册的另一个处理程序将将中断转换为golang端的事件。

再次强调,实际情况非常复杂。有许多操作系统、许多版本、许多种类的IO操作和许多硬件设备。

英文:

The KERNEL below means "kernel side". It includes OS kernel code + loaded drivers.

Given you have a TCP connection to a remote server. Here is an example how Kernel handles asynchronous write/read TCP stream.

When you send a byte array to TCP stream, kernel will puts the buffer stream in RAM and control the DMA system to copy the buffer to networking card. When DMA done its job, there is an interrupt inside the CPU invoked. A interrupt handler registered by kernel will transform the signal from DMA into a done callback for write to TCP stream method. Of course, the actual TCP stack is much more complex. These sentences are just idea how the thing works.

For the case read from TCP stream, when a package come in on networking card, there is another interrupt invoked. The another handler registered by kernel will transform the interrupt to event on golang side.

Again, the real case is very very complex. There are many OSes, many versions, many kind of IO operations and many hardware devices.

huangapple
  • 本文由 发表于 2016年4月8日 11:38:53
  • 转载请务必保留本文链接:https://go.coder-hub.com/36491094.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定