Linux协议栈(3)——接收流程及函数

Linux协议栈(3)——接收流程及函数

本章来看下,数据是如何从网络中接收并最后到达应用程序的。

网络层将数据链路层提供的帧组成数据包,包中封装有网络层包头,其中含有逻辑地址信息。

1.1.1.1  链路层

包到达机器的物理网卡时候触发一个中断,并将通过DMA传送到位于 linux kernel 内存中的rx_ring。

中断处理程序分配 skb_buff 数据结构,并将接收到的数据帧从网络适配器I/O端口拷贝到skb_buff 缓冲区中,并设置 skb_buff 相应的参数,这些参数将被上层的网络协议使用,例如skb->protocol;

然后发出一个软中断(NET_RX_SOFTIRQ,该变量定义在include/linux/interrupt.h  文件中),通知内核接收到新的数据帧。

  进入软中断处理流程,调用 net_rx_action 函数。包从 rx_ring 中被删除,进入 netif _receive_skb 处理流程。

netif_receive_skb根据注册在全局数组 ptype_all 和 ptype_base 里的网络层数据报类型,把数据报递交给不同的网络层协议的接收函数(INET域中主要是ip_rcv和arp_rcv)。

 

1.1.1.2  网络层

网络IP层的入口函数在ip_rcv函数。

ip_rcv函数调用第三层协议的接收函数处理该skb包,进入第三层网络层处理。

该函数首先会做包括checksum在内的各种检查,如果需要的话会做 IP defragment(分片合并),最终到达 ip_rcv_finish 函数。

  ip_rcv_finish 函数会调用ip_router_input函数,进入路由处理环节。会调用 ip_route_input 来更新路由,然后查找 route,决定该会被发到本机还是会被转发还是丢弃:

  如果发到本机的话,调用 ip_local_deliver 函数,可能会做 de-fragment(合并多个包),并调用ip_local_deliver_finish。最后调用下一层接口,包括 tcp_v4_rcv (TCP), udp_rcv (UDP),icmp_rcv (ICMP),igmp_rcv(IGMP)。对于 TCP 来说,函数 tcp_v4_rcv 函数会被调用,从而处理流程进入 TCP 栈。

  如果需要转发,则进入转发流程,调用 dev_queue_xmit,进入链路层处理流程。

       如果不是发送到本机的话就要进行转发,则调用ip_forward转发。

       如果是igmp管理数据包,则调用ip_mr_input函数进行组播路由的查找。

 

1.1.1.3  传输层

传输层 TCP 处理入口在tcp_v4_rcv函数(位于 linux/net/ipv4/tcp_ipv4.c 文件中),首先会做一些完整性检查,发现问题直接将包丢弃。

       如果是tcp,则调用tcp_v4_do_rcv

       然后sk->sk_state == TCP_ESTABLISHED,调用tcp_rcv_established。

       调用tcp_data_queue方法将报文放入队列中。然后用tcp_ofo_queue方法报文插入receive队列的。

 

1.1.1.4  应用层

  应用调用  read 或者 recvfrom 时,该调用会被映射为/net/socket.c 中的 sys_recv 系统调用,并被转化为 sys_recvfrom 调用,然后调用 sock_recgmsg 函数。

  对于 INET 类型的 socket,/net/ipv4/af_inet.c 中的 inet_recvmsg 方法会被调用,它会调用相关协议的数据接收方法。

  TCP 会调用 tcp_recvmsg。该函数从 socket buffer 中拷贝数据到buffer。

  UDP从用户空间可以调用三个recv()/recvfrom()/recvmsg() 中的任意一个来接收 UDP 包,这些系统调用最终都会调用内核中的 udp_recvmsg 方法。

       如下图:

919451da587b76dcbec78a579fcf2f67462ec0b4

网络子系统非常庞大,而且存在变化,后续会进行不断细化和更新。

发布了133 篇原创文章 · 获赞 25 · 访问量 17万+
展开阅读全文
评论将由博主筛选后显示,对所有人可见 | 还能输入1000个字符

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览