全部博文(41)
分类: LINUX
2009-08-19 15:38:22
tcp.c文件的retransmit_timer函数
978计划工作组
/*
* The TCP retransmit timer. This lacks a few small details.
*
* 1. An initial rtt timeout on the probe0 should cause what we can
* of the first write queue buffer to be split and sent.
* 2. On a 'major timeout' as defined by RFC1122 we shouldn't report
* ETIMEDOUT if we know an additional 'soft' error caused this.
* tcp_err should save a 'soft error' for us.
*/
static void retransmit_timer(unsigned long data)
{
struct sock *sk = (struct sock*)data;
int why = sk->ip_xmit_timeout;
/*
* only process if socket is not in use
*/
cli();
if (sk->inuse || in_bh)
{
/* Try again in 1 second */
sk->retransmit_timer.expires = HZ;
add_timer(&sk->retransmit_timer);
sti();
return;
}
sk->inuse = 1;
sti();
/* Always see if we need to send an ack. */
if (sk->ack_backlog && !sk->zapped)
{
sk->prot->read_wakeup (sk);
if (! sk->dead)
sk->data_ready(sk,0);
}
/* Now we need to figure out why the socket was on the timer. */
switch (why)
{
/* Window probing */
case TIME_PROBE0:
tcp_send_probe0(sk);
tcp_write_timeout(sk);
break;
/* Retransmitting */
case TIME_WRITE:
/* It could be we got here because we needed to send an ack.
* So we need to check for that.
*/
{
struct sk_buff *skb;
unsigned long flags;
save_flags(flags);
cli();
skb = sk->send_head;
if (!skb)
{
restore_flags(flags);
}
else
{
/*
* Kicked by a delayed ack. Reset timer
* correctly now
*/
if (jiffies < skb->when + sk->rto)
{
reset_xmit_timer (sk, TIME_WRITE, skb->when + sk->rto - jiffies);
restore_flags(flags);
break;
}
restore_flags(flags);
/*
* Retransmission
*/
sk->prot->retransmit (sk, 0);
tcp_write_timeout(sk);
}
break;
}
/* Sending Keepalives */
case TIME_KEEPOPEN:
/*
* this reset_timer() call is a hack, this is not
* how KEEPOPEN is supposed to work.
*/
reset_xmit_timer (sk, TIME_KEEPOPEN, TCP_TIMEOUT_LEN);
/* Send something to keep the connection open. */
if (sk->prot->write_wakeup)
sk->prot->write_wakeup (sk);
sk->retransmits++;
tcp_write_timeout(sk);
break;
default:
printk ("rexmit_timer: timer expired - reason unknown\n");
break;
}
release_sock(sk);
}
重传定时器到期执行函数,根据重传定时器具体超时原因进行相应处理。
in_bh:如果该变量已经设置为1,则表示函数的下半部分代码已经在执行,此时设置expires为1秒并添加到计时器队列即1秒后重试,然后返回,这样做的目的是因为该函数不允许重入。
sk->inuse:值为1表示其它进程正在使用该套接字,本进程需要等待。
sk->ack_backlog:计算目前累计的应发送而未发送的应答数据包的个数。
sk->ack_zapped:用于标识本地是否接收到远端发送的RESET复位数据包,zapped=1表示对方发送了复位数据包进行了连接复位,所以在发送任何数据包(包括应答数据包)之前必须重新进行连接,换句话说,如果zapped=1,此处不必发送应答数据包。
read_wakeup:该函数指针指向tcp_read_wakeup函数,实现发送应答数据包的功能 。
sk->dead:如果套接字仍在使用则值为0,如值为1表示套接字处于相关结构释放状态 。
sk->data_ready:该函数指针指向def_callback2函数,唤醒睡眠在该侦听套接字睡眠队列中的进程 。
TIME_PROBEO:是常数6,窗口探测计时器。
TIME_WRITE:是常数1,超时重传。
TIME_KEEPOPEN:是常数3,保活。
tcp_send_probe0:发送窗口探测数据包。其一方面发送窗口探测数据包,另一方面采用指数退避算法更新超时间隔时间后,重新设置窗口探测定时器,以便在对方未响应的情况下,继续进行窗口探测。
tcp_write_timeout:进行超时处理,具体参见《tcp.c文件的tcp_write_timeout函数(10).doc》。
skb->when:该数据包的发送时间。
sk->rto:延迟时间。
reset_xmit_timer:重新设置重传计时器。具体参见《tcp.c文件的reset_xmit_timer函数(7).doc》
write_wakeup:此函数指针指向tcp_write_wakeup函数,实现向远端发送保活数据包。
此函数功能是将sock 结构中缓存的数据包送给应用程序读取,并在检测套接字已处于关闭且等待销毁时,设置一定时器,在定时器到期时,进行sock 结构的释放