Linux kernel 如何在内核中高效地实现异步I/O

Linux kernel 如何在内核中高效地实现异步I/O,linux-kernel,operating-system,kernel,low-level-io,Linux Kernel,Operating System,Kernel,Low Level Io,这个问题是关于低级异步I/O系统调用的,比如send+epoll/aio_read和其他调用。 我询问的是网络I/O和磁盘I/O 实现这些异步调用的简单方法是为每个异步I/O请求创建一个线程,然后以同步方式执行请求。 显然,这种幼稚的解决方案在大量并行请求的情况下伸缩性很差。 即使使用了线程池,我们仍然需要为每个并行请求使用一个线程 因此,我推测这是通过以下更有效的方式实现的: 对于写入/发送数据: 将发送请求附加到某个内核内部异步I/O队列 专用的“写线程”以这样一种方式接收这些发送请求,从

这个问题是关于低级异步I/O系统调用的,比如send+epoll/aio_read和其他调用。 我询问的是网络I/O和磁盘I/O

实现这些异步调用的简单方法是为每个异步I/O请求创建一个线程,然后以同步方式执行请求。 显然,这种幼稚的解决方案在大量并行请求的情况下伸缩性很差。 即使使用了线程池,我们仍然需要为每个并行请求使用一个线程

因此,我推测这是通过以下更有效的方式实现的:

对于写入/发送数据:

  • 将发送请求附加到某个内核内部异步I/O队列

  • 专用的“写线程”以这样一种方式接收这些发送请求,从而充分利用目标硬件。为此,可以使用特殊的I/O调度程序

  • 根据目标硬件,写入请求最终被调度,例如通过直接内存访问(DMA)

对于读取/接收数据:

  • 硬件引发一个I/O中断,该中断跳转到内核的I/O中断处理程序中

  • 中断处理程序向读取队列追加通知并快速返回

  • 专用“读取线程”拾取读取队列的通知并执行两项任务:1)必要时将读取数据复制到目标缓冲区。2.)必要时以某种方式通知目标进程(如epoll、信号等)

对于所有这些,写线程或读线程的数量不需要超过CPU内核的数量。因此,并行请求的可伸缩性问题将得到解决


这是如何在真正的操作系统内核中实现的?这些推测中哪一个是正确的?

那些“异步”I/O内容是内核和驱动程序服务的另一个假象。我将以wifi驱动程序为例。(即网络)

  • 接收
  • 1) 如果数据包进入,wifi H/W将产生中断,并将dot11帧或dot3帧DMA到DRAM(这取决于wifi H/W。如今,大多数现代wifi硬件将以硬件形式转换数据包-实际上是硬件上的FW)

    2) Wifi驱动程序(在内核中运行)应该处理多个与Wifi相关的事情,但最有可能的是,它将形成套接字缓冲区(skb),然后将skb发送到Linux内核。通常,它发生在NET_RX_SOFTIRQ中,或者您可以创建自己的线程

    3) 数据包到达Linux堆栈。您可以将其发送到用户空间。它发生在“uu netif_receive_skb_core”中,如果数据包是“IP”数据包,则第一个rx_处理程序将是“IP_rcv()”

    4) ip数据包向上移动到传输层hander,即udp_rcv()/tcp_rcv()。要将数据包发送到传输层,您必须经过套接字层,最终,您将在特定套接字上形成数据包链表(您可以说是Q)

    5) 据我所知,这个“Q”是向用户空间提供数据包的队列。您可以在此处执行“异步”或“同步”I/O

  • 德克萨斯州
  • 1) 数据包将经过内核的传输层和IP层,最终,您的netdev TX处理程序将被调用(hard_start_xmit或ndo_xmit_start)。基本上,如果您的netdev(例如eth0或wifi0)是以太网设备,它将连接到您的以太网驱动程序“TX”功能或wifi驱动程序“TX”功能。这是回调,通常在驱动程序启动时设置

    2) 在此阶段,您的数据包已转换为“skb”

    3) 在回调中,它将准备所有的头和描述符并执行DMA

    4) 一旦TX在HW上停止,HW将生成中断,您需要释放数据包

    在这里,我的观点是,您的网络I/O已经在DMA和驱动程序级别作为“异步”工作。大多数现代驾驶人对此可能有不同的背景。对于TX,它将使用线程、tasklet或NET_TX_SOFTIRQ。对于RX,如果我们使用“NAPI”,它将使用NET_RX_SOFTIRQ。或者它也可以使用线程和微线程

    所有这些都是基于“中断”或其他触发器独立发生的


    “同步I/O”主要在上层应用层进行模拟。所以,若你们在内核中重新编写套接字层,你们可以做任何你们想做的事情,因为底层已经按照你们的要求工作了。

    “这在真正的操作系统内核中是如何实现的?”-对于堆栈溢出来说,这个话题太宽泛了。您使用了“linux内核”标记,您没有试着阅读linux内核中的I/O吗?例如,请参阅有关为块设备编写驱动程序的文档。