TCP的Slow Path处理

TCP的Slow Path处是常规输入数据包的处理方式,根据套接字的状态来确定数据包的处理方式,在Slow Path处理方式中当套接字接受缓冲区已经满就不在接受新的Socket Buffer或当套接字忙(被其他进程锁住)时就将收到的数据包放入套接字阻塞等待队列backlog queue中,将数据包加入backlog queue队列的条件

(1)、输入的数据包包含数据段,不是ACK段。

(2)、数据包完好无损。

(3)、套接字缓冲区已满或者套接字被其他进程锁住,无法获取。

当数据包加入到backog queue队列后套接字就会被唤醒,进程调度器调度应用进程,开始从backlog queue队列中读取数据包缓冲区,如果缓冲区没有满、或者套接字没有被锁住就将数据包加入到receive_queue队列中,Slow Path处理由tcp_v4_do_rcv函数完成。

1、套接字的状态是ESTABLISHED

首先检查套接字的状态,如果套接字状态处于连接状态ESTABLISHED数据包就可以通过Fast Path路径处理,调用tcp_rcv_established函数完成连接状态的数据包处理,处理失败调转到复位标签reset处,回复对端一个复位消息。

...
//套接字状态是ESTABLISHED
if (sk->sk_state == TCP_ESTABLISHED) { /* Fast path */
		sock_rps_save_rxhash(sk, skb->rxhash);
		TCP_CHECK_TIMER(sk);
		//套接字状态为连接状态,缓冲区可以通过Fast Path路径处理
		//由tcp_rcv_established函数处理, 将数据包加入到recieve_queue队列中
		if (tcp_rcv_established(sk, skb, tcp_hdr(skb), skb->len)) {
			rsk = sk;
			//处理失败回复一个复位请求
			goto reset;
		}
		TCP_CHECK_TIMER(sk);
		return 0;
	}
...

2、套接字状态是LISTEN

当套接字状态处于LISTEN监听状态,这时要查看接受的数据包是否是一个请求连接的SYN数据包,如果是一个有效的SYN,就将套接字的状态转变为接受状态,这个过程由tcp_v4_hnd_req完成,这个函数返回建立连接的自套接字struct sock *snk,如果snk不为NULL,就调用tcp_child_process函数在子套接字nsk上处理函数,如处理成功自套接字状态就编程ESTABLISHED,就可以接受发送数据包了。

...

//检查tcp头部长度,检查校验和
	if (skb->len < tcp_hdrlen(skb) || tcp_checksum_complete(skb))
		goto csum_err;

	//套接字是监听状态
	if (sk->sk_state == TCP_LISTEN) {
		//处理SYN请求,处理成功表示连接建立成功
		//返回子套接字数据结构,此时套接字状态变成ESTABLISHED状态
		struct sock *nsk = tcp_v4_hnd_req(sk, skb);
		if (!nsk)
			goto discard;

		if (nsk != sk) {
			//在子套接字nsk上处理
			//处理成功套接字状态变成ESTABLISHED,套接字就可以接受发送数据了
			if (tcp_child_process(sk, nsk, skb)) {
				rsk = nsk;
				goto reset;
			}
			return 0;
		}
	} else
		sock_rps_save_rxhash(sk, skb->rxhash);
...

3、其他状态处理

如果套接字的状态不是ESTABLISHED、LISTEN就调用tcp_rev_state_precess处理套接字状态切换,如处理失败就返回一个复位数据包给对端,错误处理标志有三个:reset:复位套接字连接、discard:扔掉数据包、csumm_err更新接受错误统信息。

	...

    TCP_CHECK_TIMER(sk);
	//套接字不是监听状态,状态切换处理
	if (tcp_rcv_state_process(sk, skb, tcp_hdr(skb), skb->len)) {
		rsk = sk;
		goto reset;
	}
	TCP_CHECK_TIMER(sk);
	return 0;

reset:
	//复位请求
	tcp_v4_send_reset(rsk, skb);
discard:
	//扔掉数据包
	kfree_skb(skb);
	/* Be careful here. If this function gets more complicated and
	 * gcc suffers from register pressure on the x86, sk (in %ebx)
	 * might be destroyed here. This current version compiles correctly,
	 * but you have been warned.
	 */
	return 0;

csum_err:
	//更新错误统计信息
	TCP_INC_STATS_BH(sock_net(sk), TCP_MIB_INERRS);
	goto discard;

下图是TCP套接字状态切换图:
 

TCP套接字状态切换图

tcp_v4_do_rcv完整代码:

int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb)
{
	struct sock *rsk;
#ifdef CONFIG_TCP_MD5SIG
	/*
	 * We really want to reject the packet as early as possible
	 * if:
	 *  o We're expecting an MD5'd packet and this is no MD5 tcp option
	 *  o There is an MD5 option and we're not expecting one
	 */
	if (tcp_v4_inbound_md5_hash(sk, skb))
		goto discard;
#endif

	if (sk->sk_state == TCP_ESTABLISHED) { /* Fast path */
		sock_rps_save_rxhash(sk, skb->rxhash);
		TCP_CHECK_TIMER(sk);
		//套接字状态为连接状态,缓冲区可以通过Fast Path路径处理
		//由tcp_rcv_established函数处理
		if (tcp_rcv_established(sk, skb, tcp_hdr(skb), skb->len)) {
			rsk = sk;
			//处理失败回复一个复位请求
			goto reset;
		}
		TCP_CHECK_TIMER(sk);
		return 0;
	}
	//检查tcp头部长度,检查校验和
	if (skb->len < tcp_hdrlen(skb) || tcp_checksum_complete(skb))
		goto csum_err;

	//套接字是监听状态
	if (sk->sk_state == TCP_LISTEN) {
		//处理SYN请求,处理成功表示连接建立成功
		//返回子套接字数据结构,此时套接字状态变成ESTABLISHED状态
		struct sock *nsk = tcp_v4_hnd_req(sk, skb);
		if (!nsk)
			goto discard;

		if (nsk != sk) {
			//在子套接字nsk上处理
			//处理成功套接字状态变成ESTABLISHED,套接字就可以接受发送数据了
			if (tcp_child_process(sk, nsk, skb)) {
				rsk = nsk;
				goto reset;
			}
			return 0;
		}
	} else
		sock_rps_save_rxhash(sk, skb->rxhash);


	TCP_CHECK_TIMER(sk);
	//套接字不是监听状态,状态切换处理
	if (tcp_rcv_state_process(sk, skb, tcp_hdr(skb), skb->len)) {
		rsk = sk;
		goto reset;
	}
	TCP_CHECK_TIMER(sk);
	return 0;

reset:
	//复位请求
	tcp_v4_send_reset(rsk, skb);
discard:
	//扔掉数据包
	kfree_skb(skb);
	/* Be careful here. If this function gets more complicated and
	 * gcc suffers from register pressure on the x86, sk (in %ebx)
	 * might be destroyed here. This current version compiles correctly,
	 * but you have been warned.
	 */
	return 0;

csum_err:
	//更新错误统计信息
	TCP_INC_STATS_BH(sock_net(sk), TCP_MIB_INERRS);
	goto discard;
}

猜你喜欢

转载自blog.csdn.net/City_of_skey/article/details/84641991
今日推荐