最近在看传输层发送数据到ip网络层的流程,写点文章记录一下。
申明:本文用的流程图出自《嵌入式Linux网络体系结构设计与TCP/IP协议栈》一书,此书中有不少细节上的错误,建议阅读的时候对着2.6的内核代码看,以代码为准。另外,此书从第五章开始,个人建议每章倒着阅读可能会更好一点(个人意见)。
1、首先看一下数据包在整个内核协议栈的一个流程:
2、不同的协议调用不同的函数到网络层,如图(图片出自《深入理解LINUX网络技术内幕》一书):
不同的协议以不同的方式调用dst_output,传输层协议可以多次调用ip_append_data函数来存储好几个传输请求,而不实际传输任何东西。ip_append_data不仅是将传输请求暂存于缓冲区而已,同时也以一种透明的方式产生一些最佳大小的数据片段,使得ip层稍后更容易处理分段。这样可以让ip层不用在做分段时还得把数据从一个缓冲区拷贝到另一个缓冲区,因而获得很大的性能改善。
传输期间,在特定的情况下也会用到其他函数:ip_build_and_send_pkt,由TCP使用,来传输SYN ACK消息;ip_send_reply,由TCP使用,来传输ACK和Reset消息,该函数会使用ip_append_data和ip_push_pending_frame,因此TCP不光只会用ip_queue_xmit来传输数据。
3、首先讨论udp的发送过程,udp发送数据到网络层有2个函数,一个函数是ip_append_data,另外一个是ip_append_page,下面分别来看这2个函数的作用。
3.1、ip_append_data函数
当发送的数据来自用户空间,应用程序调用sndmsg来请求将数据从用户地址空间移动到内核地址空间时,这个复制是由ip_append_data函数的输入参数getfrag函数来完成的。udp_sendmsg函数调用ip_append_data和udp_push_pending_frames来发送数据,函数调用关系如下:
3.2、ip_append_page函数
内核还为用户地址空间的应用提供了另外一个接口sendfile,它允许应用程序优化发送并复制数据。这个接口称为“零复制”TCP/UDP。sendfile接口只有在网络设备支持Scatter/Gather I/O功能时才能使用。这时ip_append_data的逻辑实现不需要复制任何数据(即用户需要发送的数据扔保存在其创建处)。内核只需江frags数组初始化指向接收数据缓冲区的位置,在必要的时候计算传输层的校验和。这种复制逻辑由ip_append_page函数实现。
4、udp_push_pending_frames发送数据
udp_push_pending_frames函数的执行流程:
5、ip_push_pending_frames函数解析
ip_push_pending_frames函数首先从sk_write_queue队列取出缓冲的数据缓冲区。队列中的第一个缓冲区一定是一个sk_buff,地址由skb局部变量保存。队列随后的缓冲区连接到skb的frag_list链表上,更新skb_buff的len和data_len数据域指明发送缓冲区的总长度len,以及在frag_list中缓冲区的总长度。一旦缓冲区链接到frag_list后,就从sk_write_queue队列清除。
将sk_write_queue队列中的缓冲区取出链接成一个新的链表的操作非常快。这里没有数据复制,只有指针操作,最后释放sk_write_queue队列,这样传输层又可以向网络层发送新数据。
函数中设置ip头以后,调用ip_local_out函数,这样数据从传输层到网络层发送完毕。代码如下:
在函数返回之前,
ip_push_pending_frames函数要清除IPCORK_OPT域。清除了
IPCORK_OPT域后,会使strcut cork数据结构中的内容无效,这是为了在以后发送数据包的目的地址与当前发送数据包的目的地址一样时,可以重新使用strcut cork数据结构,而且IP层也要知道什么时候释放旧数据。该功能由ip_cork_realease函数完成。
5、结束语:本文简略的介绍了udp发送数据的一个大致的流程,下一章将介绍ip_append_data和ip_append_page函数的功能和工作流程
如有错误,请大神指出,谢谢!
阅读(2284) | 评论(0) | 转发(0) |