戳蓝字「TopCoder」关注我们哦!
在分析网卡数据是如何传递给进程的流程之前,要知道数据是如何从进程写到网卡的,因为只有发起方写数据到网卡然后接收方才能接收到并处理。
发送数据
发送方的发送数据的处理流程大致为:用户空间 -> 内核 -> 网卡 -> 网络。
在用户态空间,调用发送数据接口 send/sento/wirte
等写数据包,在内核空间会根据不同的协议走不同的流程。以TCP为例,TCP是一种流协议,内核只是将数据包追加到套接字的发送队列中,真正发送数据的时刻,则是由TCP协议来控制的。TCP协议处理完成之后会交给IP协议继续处理,最后会调用网卡的发送函数,将数据包发送到网卡。
接收数据
接收方的接收数据的处理流程大致为:网络 -> 网卡 -> 内核(epoll等) -> 进程(业务处理逻辑)。
网络中数据首先到达网卡,对于网卡来说,数据包的到达是一个无法预料的事件,系统需要通过某种手段来得知该事件。一般来说,有2种方案:轮询和通知,轮询机制就是不断轮询网卡看数据有没有到来,该方式无疑会浪费较多的CPU资源,没数据时会造成大量的空轮询。通知机制就是网卡接收到数据时再通知CPU,然后再读取数据即可。
网卡的数据通知机制看起来很完美了,但是实际应用中,CPU响应中断处理时,为了不影响当前的工作,需要将当前工作的上下文保存起来,然后再进行中断处理。试想,当前千兆、万兆网卡已经非常普遍,若是那时网卡满负载,那么每秒钟就会产生大量的中断。除了切换过程带来的计算代价,上下文的切换还会导致CPU Cache的失效,对于高性能服务器来说,这是一个不容忽视的问题。因此,Linux做了优化,组合了通知和轮询的机制,简单来说,在CPU响应网卡中断时,不再仅仅是处理一个数据包就退出,而是使用轮询的方式继续尝试处理新数据包,直到没有新数据包到来,或者达到设置的一次中断最多处理的数据包个数。
注意网卡的硬中断处理是在网卡驱动中进行的,硬中断处理是一个特殊的上下文,CPU会屏蔽掉绝大部分中断,并且有不少的限制。所以硬中断应尽可能快地处理,以提高系统的响应速度,因此内核将具体的处理工作放到了软中断中。
数据离开网卡驱动之后就进入到了协议栈,经过IP层、网络层协议的处理,就会触发IO读事件,比如epoll的reactor模型中,就会触发对应的读事件,然后回调对应的IO处理函数,数据之后会交给业务线程来处理,比如Netty的数据接收处理流程就是这样的。
推荐阅读
欢迎小伙伴关注【TopCoder】阅读更多精彩好文。