linux内核tcp的定时器管理(一)
在内核中tcp协议栈有6种类型的定时器:
1 重传定时器。
2 delayed ack定时器
3 零窗口探测定时器
上面三种定时器都是作为tcp状态机的一部分来实现的。
4 keep-alive 定时器
主要是管理established状态的连接。
5 time_wait定时器
主要是用来客户端关闭时的time_wait状态用到。
6 syn-ack定时器(主要是用在listening socket)
管理新的连接请求时所用到。
而在内核中,tcp协议栈管理定时器主要有下面4个函数:
inet_csk_reset_xmit_timer
这个函数是用来重启定时器
inet_csk_clear_xmit_timer
这个函数用来删除定时器。
上面两个函数都是针对状态机里面的定时器。
tcp_set_keepalive
这个函数是用来管理keepalive 定时器的接口。
tcp_synack_timer
这个函数是用来管理syn_ack定时器的接口。
ok,我们现在先来看定时器的初始化。
首先是在tcp_v4_init_sock中对定时器的初始化,它会调用tcp_init_xmit_timers,我们就先来看这个函数:
static void tcp_delack_timer(unsigned long data){struct sock *sk = (struct sock *)data;struct tcp_sock *tp = tcp_sk(sk);struct inet_connection_sock *icsk = inet_csk(sk);bh_lock_sock(sk);///用户进程正在使用,则等会再尝试。if (sock_owned_by_user(sk)) {/* Try again later. */icsk->icsk_ack.blocked = 1;NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_DELAYEDACKLOCKED);sk_reset_timer(sk, &icsk->icsk_delack_timer, jiffies + TCP_DELACK_MIN);goto out_unlock;}sk_mem_reclaim_partial(sk);///判断sock状态 以及ack的状态。如果是close或者已经处在ICSK_ACK_TIMER,则直接跳出。if (sk->sk_state == TCP_CLOSE || !(icsk->icsk_ack.pending & ICSK_ACK_TIMER))goto out;///如果已经超时,则重启定时器,并退出。if (time_after(icsk->icsk_ack.timeout, jiffies)) {sk_reset_timer(sk, &icsk->icsk_delack_timer, icsk->icsk_ack.timeout);goto out;}///清除ack状态。icsk->icsk_ack.pending &= ~ICSK_ACK_TIMER;///开始遍历prequeue。此时主要的目的是为了调用tcp_rcv_eastablished.这里会调用tcp_ack_snd_check来发送ack。if (!skb_queue_empty(&tp->ucopy.prequeue)) {struct sk_buff *skb;NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPSCHEDULERFAILED);///遍历prequeue队列,发送未发送的ack。while ((skb = __skb_dequeue(&tp->ucopy.prequeue)) != NULL)sk_backlog_rcv(sk, skb);tp->ucopy.memory = 0;}///检测是否有ack还需要被发送。也就是处于ICSK_ACK_SCHED状态的ackif (inet_csk_ack_scheduled(sk)) {if (!icsk->icsk_ack.pingpong) {/* Delayed ACK missed: inflate ATO. */icsk->icsk_ack.ato = min(icsk->icsk_ack.ato << 1, icsk->icsk_rto);} else {///到这里说明已经长时间没有通信,并且处于交互模式。这个时候我们需要关闭pingpong模式。icsk->icsk_ack.pingpong = 0;icsk->icsk_ack.ato = TCP_ATO_MIN;}///立即发送ack。tcp_send_ack(sk);NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_DELAYEDACKS);}TCP_CHECK_TIMER(sk);out:if (tcp_memory_pressure)sk_mem_reclaim(sk);out_unlock:bh_unlock_sock(sk);sock_put(sk);}