数据从网卡到应用的过程

1、过程概述

假设一个HTTP请求的数据到达网卡,那数据是如何被层层处理并到达应用呢?
在这里插入图片描述
在这里插入图片描述

2、网卡

网卡(Network Adapter),也称网络适配器,是一个 硬件设备,有全球唯一的 MAC(Media Access Control)地址,MAC地址在网卡生产时就被烧制在ROM中,网卡初始化时恢复到计算机中。

  • 1、网卡收到的数据是 光信号或电信号,然后将其还原成 数字信息(1和0组成)。

在这里插入图片描述
下图是还原的数字信息结构。

在这里插入图片描述

  • 根据 FCS(帧校验序列,Frame Check Sequence)校验数据,判断数据在传输过程是否因噪音等影响导致信号失真,从而导致数据错误,需要丢弃这种无效的数据包。

在这里插入图片描述

  • 然后 检查 数据包中MAC头部中的接收方的MAC地址,若不是发给自己,则丢弃数据包;若数据包是发给自己,则将数字信息保存到网卡内部缓冲区。

以上过程网卡自行搞定,不需要CPU参与,CPU也不知道数据包的到达。

3、网卡驱动

硬件需要驱动程序来控制,就像电脑需要操作系统一样,而网卡驱动就是CPU控制和使用网卡的程序

网卡处理完数字信号后,接下来的数据接收需要CPU参与,此时网卡通过中断将数据包达到的事件通知给CPU。接着,CPU暂停手头工作,开始用网卡驱动来干活。

  • 从网卡缓冲区读取接收到的数据
  • 根据MAC头部的以太类型字段判断协议种类并调用处理该协议的软件(即协议栈)

通常我们接触的以太类型是 IP协议,因此会调用TCP/IP协议栈来处理。

  • 1、网卡驱动程序提取这个帧的全部内容,去掉以太网的帧头,然后向上传递给IP层
  • 2、IP层接收到包已经,继续去掉IP头的内容,然后交给TCP层
  • 3、TCP层根据TCP协议定义的格式,继续解包,read()从socket buffer读取数据,然后传递给应用层
  • 4、应用层根据TCP层传来的数据,按照对应的应用层来分析包

4、物理网卡收到数据包的处理流程

接收数据包是一个复杂的过程,涉及很多底层的技术细节,但大致需要以下几个步骤:

  • 1、网卡收到数据包。

  • 2、将数据包从网卡硬件缓存转移到服务器内存中。

  • 3、通知内核处理。

  • 4、经过TCP/IP协议逐层处理。

  • 5、应用程序通过read()从socket buffer读取数据。
    在这里插入图片描述
    物理网卡收到数据包的处理流程如上图所示,详细步骤如下:

  • 1、网卡收到数据包,先将高低电平转换到网卡fifo存储,网卡申请ring buffer的描述,根据描述找到具体的物理地址,从fifo队列物理网卡会使用DMA将数据包写到了该物理地址,,其实就是skb_buffer中.

  • 2、这个时候数据包已经被转移到skb_buffer中,因为是DMA写入,内核并没有监控数据包写入情况,这时候NIC触发一个硬中断,每一个硬件中断会对应一个中断号,且指定一个vCPU来处理,如上图vcpu2收到了该硬件中断.

  • 3、硬件中断的中断处理程序,调用驱动程序完成,a.启动软中断

  • 4、硬中断触发的驱动程序会禁用网卡硬中断,其实这时候意思是告诉NIC,再来数据不用触发硬中断了,把数据DMA拷入系统内存即可

    5、硬中断触发的驱动程序会启动软中断,启用软中断目的是将数据包后续处理流程交给软中断慢慢处理,这个时候退出硬件中断了,但是注意和网络有关的硬中断,要等到后续开启硬中断后,才有机会再次被触发

  • 6、NAPI触发软中断,触发napi系统

  • 7、消耗ringbuffer指向的skb_buffer

  • 8、NAPI循环处理ringbuffer数据,处理完成

  • 9、启动网络硬件中断,有数据来时候就可以继续触发硬件中断,继续通知CPU来消耗数据包.

其实上述过程过程简单描述为:网卡收到数据包,DMA到内核内存,中断通知内核数据有了,内核按轮次处理消耗数据包,一轮处理完成后,开启硬中断。其核心就是网卡和内核其实是生产和消费模型,网卡生产,内核负责消费,生产者需要通知消费者消费;如果生产过快会产生丢包,如果消费过慢也会产生问题。也就说在高流量压力情况下,只有生产消费优化后,消费能力够快,此生产消费关系才可以正常维持,所以如果物理接口有丢包计数时候,未必是网卡存在问题,也可能是内核消费的太慢。

如何将网卡收到的数据写入到内核内存?

NIC在接收到数据包之后,首先需要将数据同步到内核中,这中间的桥梁是rx ring buffer。它是由NIC和驱动程序共享的一片区域,事实上,rx ring buffer存储的并不是实际的packet数据,而是一个描述符,这个描述符指向了它真正的存储地址,具体流程如下:

  • 1、驱动在内存中分配一片缓冲区用来接收数据包,叫做sk_buffer;

  • 2、将上述缓冲区的地址和大小(即接收描述符),加入到rx ring buffer。描述符中的缓冲区地址是DMA使用的物理地址; 3、驱动通知网卡有一个新的描述符;

  • 4、网卡从rx ring buffer中取出描述符,从而获知缓冲区的地址和大小;

  • 5、网卡收到新的数据包;

  • 6、网卡将新数据包通过DMA直接写到sk_buffer中。
    在这里插入图片描述
    当驱动处理速度跟不上网卡收包速度时,驱动来不及分配缓冲区,NIC接收到的数据包无法及时写到sk_buffer,就会产生堆积,当NIC内部缓冲区写满后,就会丢弃部分数据,引起丢包。这部分丢包为rx_fifo_errors,在 /proc/net/dev中体现为fifo字段增长,在ifconfig中体现为overruns指标增长。

参考

1、https://www.jianshu.com/p/e6162bc984c8
2、https://bbs.gameres.com/thread_842984_1_1.html

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐