linux内核接收网络数据流程(三)

一 前面一节我们讲了skb通过事件通知到了软中断处理,最终调用deliver_skb分发出去skb,

注意deliver_skb调用这里是一个循环,list_for_each_entry_rcu netfilter模块就是这里调用的,因此可以知道,抓包工具等对socket读写是没有影响的,这里浅拷贝了一份skbnetfilter模块当实参传递的。而读写socket是更上层的调用,不属于同一个模块。

 

二 分析上层协议栈处理流程

deliver_skb()

{

Ip_rcv()->Ip_rcv_finish()

{

Dst_input();

}

}

//这里到dst_input分为两种情况,如果是本地数据包,即dst等于自己的,抛到上层协议栈处理,调用ip_local_deliver()。如果不是本地的数据包,路由出去,调用ip_forward()

 

三 本地处理ip_local_deliver()

{

ip_is_fragment()判断是否是IP分组

{

ip_defrag()

{//首先调用ip_find查找是否已经在分组队列里面存在相应的queue,如果没有

//就新建一个queue等待手机之后的报文,如果已经存在就返回queue的入口,ip_find()主要是调用iphashfn(),使用ip头部里面的identifier,

//source addr,dest addr,protocol四个元素去进行hash计算吗

//iphashfn()调用的是jash_3words,该函数是搞性能查找算法

ip_find()

}//ip 碎片重组

}

//最终通过NF_HOOK调用这个函数处理数据包

ip_local_deliver_finish()

{

ipprot = rcu_dereference(inet_protos[protocol]);

//获取传输层协议

ret = ipprot->handler(skb);//传输层协议处理skb

这里我们仅仅分析tcp的数据包处理

}

}

 

tcp_v4_rcv()//这个函数包含了tcp协议的状态机变化,以及数据包处理流程

{

__TCP_INC_STATS(net, TCP_MIB_INSEGS);//调整统计值

skb_checksum_init();//checksum 检查

解析tcp协议各个字段

tcp_v4_do_rcv();//最终调用这个函数处理,当然还有一些其它状态的处理,可以自行阅读源码,

}

 

tcp_v4_do_rcv()

{

if (sk->sk_state == TCP_ESTABLISHED)

{

tcp_rcv_established(sk, skb, tcp_hdr(skb), skb->len);//ESTABLISHED状态处理

}

 

if (sk->sk_state == TCP_LISTEN)

{

tcp_rcv_state_process();

}

}

 

void tcp_rcv_established(struct sock *sk, struct sk_buff *skb,

 const struct tcphdr *th, unsigned int len)

{

· 快速路径 使用快速路径只进行最少的处理,如处理数据段、发生ACK、存储时间戳等。

· 慢速路径 使用慢速路径可以处理乱序数据段、PAWS、socket内存管理和紧急数据等。

static inlinevoid__tcp_fast_path_on(struct tcp_sock *tp, u32 snd_wnd) { tp->pred_flags =htonl((tp->tcp_header_len <<26|ntohl(TCP_FLAG_ACK) |snd_wnd); }

}

流程示意图(http://blog.csdn.net/Al_xin/article/details/52304732借的图)

 linux内核接收网络数据流程(三)_第1张图片

四 下一节讲ip数据包路由ip_forwardtcp_rcv_established()如何把skb抛到应用层,并激活socket可读的






你可能感兴趣的:(内核,音视频,tcp)