Linux开发之初并没有网络功能,因此在内核对于网络的支持上与FreeBSD相比仍然有一定的差距。图2是TCP协议栈在Linux上的实现。
图2 TCP协议栈在Linux上的实现
TCP协议在LINUX上的实现稍显复杂。首先从发送数据开始分析。在LINUX上,socket被实现为一个文件系统,这样可以通过vfs的write来调用,也可以直接使用send来调用,它们最终都是调用sock-sendmsg。sock-sendmsg通过它的内核版本sock-sendmsg直接调用tcp-sendmsg来发送数据。在tcp-sendmsg中,同时完成数据复制和数据校验,节省了一次遍历操作,这是和FreeBSD不同的地方。Linux使用skb结构来管理数据缓冲,这与FreeBSD的m-buf大同小异。当复制完数据后,使用tcp-push来进行发送。Tcp-push通过-tcp-push-pending-frames调用tcp-write-xmit将数据填入top的发送缓冲区。这里的填充仅是指针引用而已。下一步,tcp-transmit-skb将数据放人ip的发送队列。Ip—queue—xmit函数完成IP包头的设置以及数据效验,并调用ip-output进入下一步发送。如果不用分片,将使用ip—finish—output继续发送。在这里,将检查硬件头部描述符(这里就是以太网包头)并填充人数据缓冲区,而后调用dev—queue—xmit函数来进一步处理。Dev-queue-xmit函数将数据排队放人硬件缓冲区以等待随后的发送。而使用具体的网卡驱动程序cp-start-xmit 来完成数据的最终发送。最后的cp-start-xmit做的事情和freebsd的相应函数差不多,检查数据,并将其复制进硬件级冲。
当接收到一个数据包的时候,网卡会产生中断,这样网卡驱动的cp-interrupt会被调用。Cp-interrupt做的事情很省,只进行必要的检查后就返回了,更多的事情通过cp-rx-poll来完成,这是因为linux驱动分为上半部和下半部,下半部的cp-rx-poll函数在软中断中被调用,这样做是为了提高驱动的处理效率。Cp—rx—poll做的事情主要就是把申请并将数据复制进一个skb缓冲中。Netif-rx函数将数据从这个队列中转移至网络核心层队列中,netif-receive-skb从这里接收数据,并调用ip-rcv来处理。Ip-rcv和ip-vcv-finish一起检查数据包,得到包的路由,并调用相应的input函数来完成路由,在这里就是ip-rcv-finish一起检查数据包,得到包的路由,并调用相应的input函数来完成路由-在这里就是ip-local-deliver,ip-local-deliver完成IP包的重组,而后将使用ip-local-deliver-finish来进入TCP的处理流程,tcp-v4-do-rcv先判断是否正常的用户数据,如果是则用
tcp-rcv-cstabUshde处理,否则用tcp—rcv-state-process来更新本条TCP连接的状态机。在top-rcv-cstablished中同样实现有首部预测。如果一切顺利,将唤醒等待在top-recvmsg中的用户进程。后者将数据从skb缓冲中复制进用户进程缓冲。