计算机 · 2021年12月30日 0

内核tcp结束流程分析

tcp_done

void tcp_done(struct sock *sk)
{
	struct request_sock *req = tcp_sk(sk)->fastopen_rsk;

	if (sk->sk_state == TCP_SYN_SENT || sk->sk_state == TCP_SYN_RECV)
		TCP_INC_STATS(sock_net(sk), TCP_MIB_ATTEMPTFAILS);

	tcp_set_state(sk, TCP_CLOSE);
	tcp_clear_xmit_timers(sk);
	if (req)
		reqsk_fastopen_remove(sk, req, false);

	sk->sk_shutdown = SHUTDOWN_MASK;

	if (!sock_flag(sk, SOCK_DEAD))
		sk->sk_state_change(sk);
	else
		inet_csk_destroy_sock(sk);
}
  1. 设置状态为TCP_CLOSE;
  2. 清理相关timer:pacing,compressed ack,以及其他delay,重传之类的timer;
  3. 清理fastopen相关的request_sock; 4.设置sk_shutdownSHUTDOWN_MASK; 5.如果这个sk没有被标记SOCK_DEAD,那么调用sk_state_change处理这个sk,否则调用inet_csk_destroy_sock处理;sk_state_changesock初始化的时候在sock_init_data中设置的,默认值为sock_def_wakeup。 这个sk_state_changestruct sock这个基类里初始化的一个函数,大致就是epoll事件的回调的意思,sk_state_change被赋值为sock_def_wakeup用来唤醒应用进程,告知其此socket状态已经改变。

inet_csk_destroy_sock

那么继续理解inet_csk_destroy_sock的逻辑:

void inet_csk_destroy_sock(struct sock *sk)
{
	WARN_ON(sk->sk_state != TCP_CLOSE);
	WARN_ON(!sock_flag(sk, SOCK_DEAD));

	/* It cannot be in hash table! */
	WARN_ON(!sk_unhashed(sk));

	/* If it has not 0 inet_sk(sk)->inet_num, it must be bound */
	WARN_ON(inet_sk(sk)->inet_num && !inet_csk(sk)->icsk_bind_hash);

	sk->sk_prot->destroy(sk);

	sk_stream_kill_queues(sk);

	xfrm_sk_free_policy(sk);

	sk_refcnt_debug_release(sk);

	percpu_counter_dec(sk->sk_prot->orphan_count);

	sock_put(sk);
}

从这个实现可以看出,到tcp_done的时候,这个TCP socket必须早已经从hash_info中给删除掉了。

调用tcp_done的时机

  1. tcp_abort中调用,而tcp_abort是用来赋值给sock_diag的diag_destroy指针的;
    但是这个tcp_abort函数应该是有bug的,只能用来作为参考;
  2. tcp_reset中调用;
    这是在收到RSET包后调用的;
  3. tcp_rcv_state_process中调用;
    TCP在收到包后会调用tcp_rcv_state_process进行处理,具体逻辑暂时不管,从中可以看到处于LAST_ACK的socket在收到正确的包后会直接调用tcp_done结束自己;
  4. tcp_v4_err中调用;
  5. tcp_v4_syn_recv_sock中调用;
  6. tcp_time_wait中调用;
    在TCP socket要进入time-wait或者fin-wait-2状态时,会调用tcp_time_wait函数,创建一个新的inet_timewait_sock来取代原来的sock,并且将原来的sock释放掉以节约内存;
  7. tcp_write_err中调用;
  8. tcp_out_of_resources中调用;
  9. tcp_keepalive_timer中调用;
  10. tcp_v6_err中调用;
  11. tcp_v6_syn_recv_sock中调用;

tcp_send_active_reset

释放congestion control的时机

在tcp_cleanup_congestion_control中会调用congestion control注册的destroy函数。

  • tcp_reinit_congestion_control切换cc算法时调用;
  • tcp_v4_destroy_sock调用;
    那么什么时候会调用这个tcp_v4_destroy_sock呢?
  1. tcp_v6_destroy_sock里会调用,有点类似tcp_ipv6是tcp_ipv4的子类的意思;
  2. tcp_v4_destroy_sock注册成了tcp_ipv4的destroy函数;

那么什么时候会调用这个注册的destroy函数呢?

  1. inet_csk_destroy_sock函数调用;
    嗯,可以把tcp_sock看作inet_connection_sock的子类吧。
    tcp_done里可能会调用inet_csk_destroy_sock,也就是说tcp_done函数里大概率就把tcp socket关联的congestion control给释放掉了。
  2. sk_common_release中调用;