什么网站可以做兼职赚钱吗,centos wordpress安装,seo专业术语,自助建站网信息发布平台在 tcp_v4_rcv 中#xff0c;得到 TCP 的头之后#xff0c;我们可以开始处理 TCP 层的事情。因为 TCP 层是分状态的#xff0c;状态被维护在数据结构 struct sock 里面#xff0c;因而我们要根据 IP 地址以及 TCP 头里面的内容#xff0c;在 tcp_hashinfo 中找到这个包对应…在 tcp_v4_rcv 中得到 TCP 的头之后我们可以开始处理 TCP 层的事情。因为 TCP 层是分状态的状态被维护在数据结构 struct sock 里面因而我们要根据 IP 地址以及 TCP 头里面的内容在 tcp_hashinfo 中找到这个包对应的 struct sock从而得到这个包对应的连接的状态。
接下来我们就根据不同的状态做不同的处理TCP_LISTEN、TCP_NEW_SYN_RECV 状态属于连接建立过程中。TCP_TIME_WAIT 状态是连接结束的时候的状态。
我们来分析最主流的网络包的接收过程这里面涉及三个队列
backlog 队列prequeue 队列sk_receive_queue 队列
为什么接收网络包的过程需要在这三个队列里面倒腾过来、倒腾过去呢这是因为同样一个网络包要在三个主体之间交接。
第一个主体是软中断的处理过程。在执行 tcp_v4_rcv 函数的时候依然处于软中断的处理逻辑里所以必然会占用这个软中断。
第二个主体就是用户态进程。如果用户态触发系统调用 read 读取网络包也要从队列里面找。
第三个主体就是内核协议栈。哪怕用户进程没有调用 read读取网络包当网络包来的时候也得有一个地方收着呀。
当前这个 sock 是不是正有一个用户态进程等着读数据呢如果没有内核协议栈也调用 tcp_add_backlog暂存在 backlog 队列中并且抓紧离开软中断的处理过程。
如果把 sysctl_tcp_low_latency 设置为 0那就要放在 prequeue 队列中暂存这样不用等待网络包处理完毕就可以离开软中断的处理过程但是会造成比较长的时延。如果把 sysctl_tcp_low_latency 设置为 1我们还是调用 tcp_v4_do_rcv。
在 tcp_v4_do_rcv 中分两种情况一种情况是连接已经建立处于 TCP_ESTABLISHED 状态调用 tcp_rcv_established。另一种情况就是其他的状态调用 tcp_rcv_state_process。
对于 TCP 所有状态的处理其中和连接建立相关的状态。 在 tcp_data_queue 中对于收到的网络包我们要分情况进行处理。
第一种情况seq tp-rcv_nxt说明来的网络包正是我服务端期望的下一个网络包。这个时候我们判断 sock_owned_by_user也即用户进程也是正在等待读取这种情况下就直接 skb_copy_datagram_msg将网络包拷贝给用户进程就可以了。
如果用户进程没有正在等待读取或者因为内存原因没有能够拷贝成功tcp_queue_rcv 里面还是将网络包放入 sk_receive_queue 队列。
接下来tcp_rcv_nxt_update 将 tp-rcv_nxt 设置为 end_seq也即当前的网络包接收成功后更新下一个期待的网络包。
我们还会判断一下另一个队列out_of_order_queue也看看乱序队列的情况看看乱序队列里面的包会不会因为这个新的网络包的到来也能放入到 sk_receive_queue 队列中。
乱序的包不能进入 sk_receive_queue 队列。因为一旦进入到这个队列意味着可以发送给用户进程。然而按照 TCP 的定义用户进程应该是按顺序收到包的没有排好序就不能给用户进程。
第二种情况end_seq 不大于 rcv_nxt也即服务端期望网络包 5。但是来了一个网络包 3怎样才会出现这种情况呢肯定是服务端早就收到了网络包 3但是 ACK 没有到达客户端中途丢了那客户端就认为网络包 3 没有发送成功于是又发送了一遍这种情况下要赶紧给客户端再发送一次 ACK表示早就收到了。
第三种情况seq 不小于 rcv_nxt tcp_receive_window。这说明客户端发送得太猛了。本来 seq 肯定应该在接收窗口里面的这样服务端才来得及处理结果现在超出了接收窗口说明客户端一下子把服务端给塞满了。
这种情况下服务端不能再接收数据包了只能发送 ACK 了在 ACK 中会将接收窗口为 0 的情况告知客户端客户端就知道不能再发送了。这个时候双方只能交互窗口探测数据包直到服务端因为用户进程把数据读走了空出接收窗口才能在 ACK 里面再次告诉客户端又有窗口了又能发送数据包了。
第四种情况seq 小于 rcv_nxt但是 end_seq 大于 rcv_nxt这说明从 seq 到 rcv_nxt 这部分网络包原来的 ACK 客户端没有收到所以重新发送了一次从 rcv_nxt 到 end_seq 时新发送的可以放入 sk_receive_queue 队列。
当接收的网络包进入各种队列之后接下来我们就要等待用户进程去读取它们了。
读取一个 socket就像读取一个文件一样读取 socket 的文件描述符通过 read 系统调用。
read 系统调用对于一个文件描述符的操作大致过程都是类似的。最终它会调用到用来表示一个打开文件的结构 stuct file 指向的 file_operations 操作。
整个过程可以分成以下几个层次。
硬件网卡接收到网络包之后通过 DMA 技术将网络包放入 Ring Buffer硬件网卡通过中断通知 CPU 新的网络包的到来网卡驱动程序会注册中断处理函数 ixgb_intr中断处理函数处理完需要暂时屏蔽中断的核心流程之后通过软中断 NET_RX_SOFTIRQ 触发接下来的处理过程NET_RX_SOFTIRQ 软中断处理函数 net_rx_actionnet_rx_action 会调用 napi_poll进而调用 ixgb_clean_rx_irq从 Ring Buffer 中读取数据到内核 struct sk_buff调用 netif_receive_skb 进入内核网络协议栈进行一些关于 VLAN 的二层逻辑处理后调用 ip_rcv 进入三层 IP 层在 IP 层会处理 iptables 规则然后调用 ip_local_deliver 交给更上层 TCP 层在 TCP 层调用 tcp_v4_rcv这里面有三个队列需要处理如果当前的 Socket 不是正在被读取则放入 backlog 队列如果正在被读取不需要很实时的话则放入 prequeue 队列其他情况调用 tcp_v4_do_rcv在 tcp_v4_do_rcv 中如果是处于 TCP_ESTABLISHED 状态调用 tcp_rcv_established其他的状态调用 tcp_rcv_state_process在 tcp_rcv_established 中调用 tcp_data_queue如果序列号能够接的上则放入 sk_receive_queue 队列如果序列号接不上则暂时放入 out_of_order_queue 队列等序列号能够接上的时候再放入 sk_receive_queue 队列。
接下来就是用户态读取网络包的过程这个过程分成几个层次。
VFS 层read 系统调用找到 struct file根据里面的 file_operations 的定义调用 sock_read_iter 函数。sock_read_iter 函数调用 sock_recvmsg 函数。Socket 层从 struct file 里面的 private_data 得到 struct socket根据里面 ops 的定义调用 inet_recvmsg 函数。Sock 层从 struct socket 里面的 sk 得到 struct sock根据里面 sk_prot 的定义调用 tcp_recvmsg 函数。TCP 层tcp_recvmsg 函数会依次读取 receive_queue 队列、prequeue 队列和 backlog 队列。 此文章为11月Day26学习笔记内容来源于极客时间《趣谈Linux操作系统》推荐该课程。