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

时间:2016-04-08 03:38:54

标签: multithreading asynchronous go io kernel

关于我为什么这么问的一些背景知识。几个小时前我问过这个问题

When a goroutine blocks on I/O how does the scheduler identify that it has stopped blocking?

有答案

  

所有I / O必须通过系统调用完成,并且在Go中实现syscalls的方式,它们总是通过运行时控制的代码调用。这意味着当你调用一个系统调用时,而不是直接调用它(从而放弃对内核的线程控制),运行时会收到你想要的系统调用通知,并代表goroutine执行。这允许它,例如,做一个非阻塞的系统调用而不是阻塞系统调用(基本上告诉内核,“请做这件事,但不要阻塞,直到它完成,立即返回,并在结果后让我知道准备好了”)。这允许它在此期间继续做其他工作。

因此,根据我的理解,golang调度程序所做的是确保不会在等待I / O操作的线程上花费时间。相反,它以某种方式将责任推迟到内核。

但是,我希望对这个过程有更深入的了解,因为我有很多不清楚的事情。

现在这是我的理解,这可能是完全错误的。

  1. 向goroutine
  2. 内的远程服务器发出I / O请求,例如GET请求
  3. Golang使用一个系统调用来读取TCP流,这是一个阻塞操作,但它不是等待,而是要求内核在获取信息时得到通知。调度程序从队列中删除阻塞goroutine
  4. 当内核获取所有信息时,它会将其转发到go进程,并让调度程序知道将goroutine添加回其队列。
  5. 我正在努力理解的是如何在不创建另一个线程的情况下完成I / O操作,以及内核实际“知道”I / O操作是如何完成的。是通过轮询还是有某种中断系统?

    我希望这有点似乎有意义。我对这个低级别的概念很新。

1 个答案:

答案 0 :(得分:2)

下面的KERNEL意味着"内核方面"。它包括OS内核代码+加载的驱动程序。

鉴于您与远程服务器建立了TCP连接。以下是Kernel如何处理异步写/读TCP流的示例。

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

对于从TCP流读取的情况,当包进入网卡时,会调用另一个中断。内核注册的另一个处理程序会将中断转换为golang端的事件。

同样,真实情况非常复杂。有许多操作系统,许多版本,许多类型的IO操作和许多硬件设备。