FREEBSD中TCP定时器的基石
TCP连接中的七个定时器:连接建立定时器、重传定时器、延迟ACK定时器、持续定时器、保活定时器、FIN_WAIT_2定时器、TIME_WAIT定时器都是由两个定时器函数来实现的,一个函数没200ms调用一次(快速定时器);另一个函数每500ms调用一次(慢速定时器)。
1、tcp_fasttimo函数
void
tcp_fasttimo()
{
register struct inpcb *inp;
register struct tcpcb *tp;
int s = splnet();
inp = tcb.inp_next;
if (inp)
for (; inp != &tcb; inp = inp->inp_next)
if ((tp = (struct tcpcb *)inp->inp_ppcb) &&
(tp->t_flags & TF_DELACK)) {
tp->t_flags &= ~TF_DELACK;
tp->t_flags |= TF_ACKNOW;
tcpstat.tcps_delack++;
(void) tcp_output(tp);
}
splx(s);
}
tcp_fasttimo函数每隔200ms被pr_fasttimo调用一次,用于操作延迟ACK定时器。
该函数检查TCP链表中每个具有对应TCP控制块的Internet PCB,如果TF_DELACK标志置位,清除该标志,并置TF_ACKNOW标志。调用tcp_output,由于TF_ACKNOW标置已置位,ACK被发送。
void
pffasttimo(arg)
void *arg;
{
register struct domain *dp;
register struct protosw *pr;
for (dp = domains; dp; dp = dp->dom_next)
for (pr = dp->dom_protosw; pr < dp->dom_protoswNPROTOSW; pr++)
if (pr->pr_fasttimo)
(*pr->pr_fasttimo)();
timeout(pffasttimo, NULL, hz/5);
}
该函数用两个for循环对内核支持的所有域的每个定义有pr_slowtimo函数的协议调用pr_slowtimo函数。该函数没通过timeout(pfslowtimo, (void *)0, hz/2)每500ms调用自己一次。
domaininit()
{
register struct domain *dp;
register struct protosw *pr;
#undef unix
#ifndef lint
ADDDOMAIN(unix);
ADDDOMAIN(route);
#ifdef INET
ADDDOMAIN(inet);
#endif
#ifdef NS
ADDDOMAIN(ns);
#endif
#ifdef ISO
ADDDOMAIN(iso);
#endif
#ifdef CCITT
ADDDOMAIN(ccitt);
#endif
#include "imp.h"
#if NIMP > 0
ADDDOMAIN(imp);
#endif
#endif
for (dp = domains; dp; dp = dp->dom_next) {
if (dp->dom_init)
(*dp->dom_init)();
for (pr = dp->dom_protosw; pr < dp->dom_protoswNPROTOSW; pr++)
if (pr->pr_init)
(*pr->pr_init)();
}
if (max_linkhdr < 16) /* XXX */
max_linkhdr = 16;
max_hdr = max_linkhdr + max_protohdr;
max_datalen = MHLEN - max_hdr;
timeout(pffasttimo, (void *)0, 1);
timeout(pfslowtimo, (void *)0, 1);
}
只看最后两行(标志为蓝色的两行),在系统初始化期间,内核调用domaininit来链接结构domain和protorsw,并调用timeout函数启动pffasttimo和pfslowtimo。
2、tcp_slowtimo
void
tcp_slowtimo()
{
register struct inpcb *ip, *ipnxt;
register struct tcpcb *tp;
int s = splnet();
register int i;
tcp_maxidle = TCPTV_KEEPCNT * tcp_keepintvl;
/*
* Search through tcb's and update active timers.
*/
ip = tcb.inp_next;
if (ip == 0) {
splx(s);
return;
}
for (; ip != &tcb; ip = ipnxt) {
ipnxt = ip->inp_next;
tp = intotcpcb(ip);
if (tp == 0)
continue;
for (i = 0; i < TCPT_NTIMERS; i++) {
if (tp->t_timer[i] && --tp->t_timer[i] == 0) {
(void) tcp_usrreq(tp->t_inpcb->inp_socket,
PRU_SLOWTIMO, (struct mbuf *)0,
(struct mbuf *)i, (struct mbuf *)0);
if (ipnxt->inp_prev != ip)
goto tpgone;
}
}
tp->t_idle++;
if (tp->t_rtt)
tp->t_rtt++;
tpgone:
;
}
tcp_iss += TCP_ISSINCR/PR_SLOWHZ; /* increment iss */
#ifdef TCP_COMPAT_42
if ((int)tcp_iss < 0)
tcp_iss = 0; /* XXX */
#endif
tcp_now++; /* for timestamps */
splx(s);
}
tcp_slowtimo函数,每隔500ms pr_slowtimo调用一次。它操作其他6个定时器:连接建立定时器、重传定时器、持续定时器、保活定时器、FIN_WAIT_2定时器和2MSL定时器。
两个for循环检测连接控制块中的每个具有对应TCP控制块的Internet PCB,测试每个连接的所有定时器,如果非0,计数器减1。如果减为0,则发送PRU_SLOWTIMO请求。
void
pfslowtimo(arg)
void *arg;
{
register struct domain *dp;
register struct protosw *pr;
for (dp = domains; dp; dp = dp->dom_next)
for (pr = dp->dom_protosw; pr < dp->dom_protoswNPROTOSW; pr++)
if (pr->pr_slowtimo)
(*pr->pr_slowtimo)();
timeout(pfslowtimo, NULL, hz/2);
}
该函数用两个for循环对内核支持的所有域的每个定义有pr_slowtimo函数的协议调用pr_slowtimo函数。该函数没通过timeout(pfslowtimo, (void *)0, hz/2)每500ms调用自己一次。