参考 翻译:理解TCP/IP网络栈&编写网络应用一文内容。
1. TCP/IP的特性
- 面向连接
这里的连接用什么结构管理? - 双向字节流
字节流是什么意思? - 顺序投递
能够完全遵循时间顺序投递? - ACK实现可靠性
怎么可靠? - 流量控制、阻塞控制
2. 数据传输流程
网络栈很多层
内核层使用socket(因为抽象成文件,会走VFS路径)来管理从用户区拷贝过来的接收与发送的数据缓冲区,socket会关联一个TCP控制块,其包含TCP连接的所有状态数据。(基本回答问题1)
TCP中允许情况下会创建TCP segment,包含两个部分:TCP头+数据,注意TCP校验和不会中内核中算出,会offload到NIC中处理
创建的TCP分段进入IP层中增加IP头,然后执行IP路由(之后下一层是链路层增加链路头,至此形成完整报文)在执行IP路由时回选择一个NIC传输接口,这时会调用NIC驱动程序。如果此时有抓包程序则会在此层进行数据拷贝处理。驱动程序与内核通过请求NIC定义的通讯协议进行传输。
NIC会从系统内存中拷贝数据包,向数据包中增加帧间隙(Inter-Frame Gap,IFG),同步码(preamble)和crc校验和。帧间隙和同步码用于区分数据包的开始(NIC目前是在哪一层?)。NIC会在主机的CPU上产生中断。驱动程序在启动时会注册一个处理中断的函数。操作系统调用中断处理程序,之后中断处理程序会把已发送的数据包返回给操作系统。
除了应用程序直接调用write之外,内核也可以直接调用TCP传输数据,比如接收到ACK并且得知接收方的接收窗口增大之后,内核会创建socket缓冲区剩余数据的TCP片段并且把数据发送给接收者。
3.数据接收流程
NIC接收到数据包后计算CRC,然后拷贝到驱动提前向内核申请的缓冲区中,之后NIC发出中断。驱动程序会判断它是否能处理新的数据包。
驱动向上层发送数据包时,Linux的sk_buff
,或者BSD系列内核的mbuf
,或者windows的NET_BUFFER_LIST
。驱动会把数据包包装成指定的数据结构,并发送到上一层。
之后数据链路层,IP层同样检查数据包是否有效,最后到TCP层查找到对应的TCB,socket接收缓冲区的大小就是TCP接收窗口。TCP吞吐量会随着接收窗口变大而增加。
4. 流量控制
步骤3即使用定时器超时机制
5. 中断处理
中断比较复杂,例如NIC接收数据产生中断,驱动在释放已经传输的数据包之后调用napi_schedule()
函数去处理接收到的数据包,这个函数会请求软中断。
在中断上下文被执行之后,软中断的上下文会被执行。中断上下文和软中断上下文会通过相同的线程执行,但是它们会使用不同的栈。并且中断上下文会屏蔽硬件中断;而软中断上下文不会屏蔽硬件中断。
处理接收到的数据包的软中断处理程序是net_rx_action()
函数。这个函数会调用驱动程序的poll()
函数。而poll()
函数会调用netif_receive_skb()
函数,并把接收到的数据包一个接一个的发送到上层。在软中断处理结束后,应用程序会从停止的位置重新开始执行完成系统调用。所以才有多队列NIC以解决软中断并发处理瓶颈问题。