从网卡到应用程序,数据包会经过一系列组件,其中驱动做了什么?内核做了什么?为了优化,我们又能做些什么?整个过程中涉及到诸多细微可调的软硬件参数,并且相互影响,不存在一劳永逸的“银弹”。本文中又拍云系统开发高级工程师杨鹏将结合自己的的实践经验,介绍在深入理解底层机制的基础上如何做出“场景化”的最优配置。
文章根据杨鹏在又拍云 Open Talk 技术沙龙北京站主题演讲《性能优化:更快地接收数据》整理而成,现场视频及 PPT 可点击阅读原文查看。
大家好,我是又拍云开发工程师杨鹏,在又拍云工作已有四年时间,期间一直从事 CDN 底层系统开发的工作,负责调度、缓存、负载均衡等 CDN 的核心组件,很高兴来跟大家分享在网络数据处理方面的经验和感受。今天分享的主题是《如何更快地接收数据》,主要介绍加速网络数据处理的方法和实践。希望能帮助大家更好的了解如何在系统的层面,尽量在应用程序无感的情况下做到极致的优化。言归正传,进入主题。
首先需要清楚在尝试做任何优化的时候,想到的第一件事情应该是什么?个人觉得是衡量指标。做任何改动或优化之前,都要明确地知道,是怎样的指标反映出了当前的问题。那么在做了相应的调整或改动之后,也才能通过指标去验证实际效果与作用。
针对要分享的主题,有一个围绕上面指标核心的基本原则。在网络层面做优化,归根结底只需要看一点,假如可以做到网络栈的每个层次,加入能监控到对应层次的丢包率,这样核心的指标,就可以明确地知道问题出在哪一层。有了明确可监控的指标,之后做相应的调整与实际效果的验证也就很简单了。当然上述两点相对有点虚,接下来就是比较干的部分了。
如上图所示,当收到一个数据包,从进入网卡,一直到达应用层,总的数据流程有很多。在当前阶段,无需关注每个流程,留意其中几个核心的关键路径即可:
第一个,数据包到达网卡;
第二个,网卡在收到数据包时,它需要产生一个中断,告诉 CPU 数据已经到了;
第三步,内核从这个时候开始进行接管,把数据从网卡中拿出来,交到后面内核的协议栈去处理。
以上是三个关键的路径。上图中右边的手绘图指的就是这三个步骤,并有意区分了两个颜色。之所以这么区分是因为接下来会按这两部分进行分享,一是上层驱动部分,二是下层涉及到内核的部分。 当然内核比较多,通篇只涉及到内核网络子系统,更具体来说是内核跟驱动交互部分的内容。
网卡驱动网卡驱动的部分,网卡是硬件,驱动(driver)是软件,包括了网卡驱动部分的大部分。这部分可简单分四个点,依次是初始化、启动、监控与调优驱动它的初始化流程。
网卡驱动-初始化
驱动初始化的过程和硬件相关,无需过分关注。但需注意一点就是注册 ethool 的一系列操作,这个工具可以对网卡做各种各样的操作,不止可以读取网卡的配置,还可以更改网卡的配置参数,是一个非常强大的工具。
那它是如何控制网卡的呢?每个网卡的驱动在初始化时,通过接口,去注册支持 ethool 工具的一系列操作。ethool 是一套很通用的接口,比如说它支持 100 个功能,但每个型号的网卡,只能支持一个子集。所以具体支持哪些功能,会在这一步进行声明。
上图截取的部分,是在初始化时结构体的赋值。前面两个可以简单看一下,驱动在初始化的时候会告诉内核,如果想要操作这块网卡对应的回调函数,其中最主要的是启动和关闭,有用 ifconfig 工具操作网卡的应该都很熟悉,当用 ifconfig up/down 一张网卡的时候,调用的都是它初始化时指定的这几个函数。
网卡驱动-启动
驱动初始化过程之后就是启动(open)中的流程了,一共分为四步:分配 rx/tx 队列内存、
开启 NAPI、注册中断处理函数、开启中断。其中注册中断处理函数和开启中断是理所当然的,任何一个硬件接入到机器上都需要做这个操作。当后面收到一些事件时,它需要通过中断去通知系统,然后开启中断。
第二步的 NAPI 后面会详细说明,这里先重点关注启动过程中对内存的分配。网卡在收到数据时,都必须把数据从链路层拷贝到机器的内存里,而这块内存就是网卡在启动时,通过接口向内核、向操作系统申请而来的。内存一旦申请下来,地址确定之后,后续网卡在收到数据的时候,就可以直接通过 DMA 的机制,直接把数据包传送到内存固定的地址中去,甚至不需要 CPU 的参与。