免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 2370 | 回复: 4
打印 上一主题 下一主题

[网络子系统] TCP定时器的回调函数中,为何存在检查定时时间是否超时的代码? [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2013-11-08 20:59 |只看该作者 |倒序浏览
本帖最后由 linux_26 于 2013-11-08 21:00 编辑

个人认为,定时器触发后,应该被定时的对象肯定是超时了,为何存在检查定时时间没有超时的代码,不太理解。
比如在重传定时器到点时,调用的 tcp_write_timer()中有下面代码
440           if (time_after(tp->timeout, jiffies)) {
441                   if (!mod_timer(&tp->retransmit_timer, tp->timeout))
442                           sock_hold(sk);
443                   goto out;
444           }
在tcp_delack_timer()中,也存在如下代码:
231           if (time_after(tp->ack.timeout, jiffies)) {
232                   if (!mod_timer(&tp->delack_timer, tp->ack.timeout))
233                           sock_hold(sk);
234                   goto out;
235           }

论坛徽章:
6
金牛座
日期:2013-10-08 10:19:10技术图书徽章
日期:2013-10-14 16:24:09CU十二周年纪念徽章
日期:2013-10-24 15:41:34狮子座
日期:2013-11-24 19:26:19未羊
日期:2014-01-23 15:50:002015年亚洲杯之阿联酋
日期:2015-05-09 14:36:15
2 [报告]
发表于 2013-11-08 23:30 |只看该作者
回复 1# linux_26
个人认为,定时器触发后,应该被定时的对象肯定是超时了,为何存在检查定时时间没有超时的代码,不太理解。
比如在重传定时器到点时,调用的 tcp_write_timer()中有下面代码
440           if (time_after(tp->timeout, jiffies)) {
441                   if (!mod_timer(&tp->retransmit_timer, tp->timeout))
442                           sock_hold(sk);
443                   goto out;
444           }
在tcp_delack_timer()中,也存在如下代码:
231           if (time_after(tp->ack.timeout, jiffies)) {
232                   if (!mod_timer(&tp->delack_timer, tp->ack.timeout))
233                           sock_hold(sk);
234                   goto out;
235           }


在家里没有代码,只能从在线代码猜测一下,这个与timer的关系不大,应该结合代码所处的语境有关系。以tcp_delack_timer()为例:
  1. static void tcp_delack_timer(unsigned long data)
  2. 209 {
  3. 210         struct sock *sk = (struct sock *)data;
  4. 211         struct tcp_sock *tp = tcp_sk(sk);
  5. 212         struct inet_connection_sock *icsk = inet_csk(sk);
  6. 213
  7. 214         bh_lock_sock(sk);
  8. 215         if (sock_owned_by_user(sk)) {
  9. 216                 /* Try again later. */
  10. 217                 icsk->icsk_ack.blocked = 1;
  11. 218                 NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_DELAYEDACKLOCKED);
  12. 219                 sk_reset_timer(sk, &icsk->icsk_delack_timer, jiffies + TCP_DELACK_MIN);
  13. 220                 goto out_unlock;
  14. 221         }
  15. 222
  16. 223         sk_mem_reclaim_partial(sk);
  17. 224
  18. 225         if (sk->sk_state == TCP_CLOSE || !(icsk->icsk_ack.pending & ICSK_ACK_TIMER))
  19. 226                 goto out;
  20. 227
  21. 228         if (time_after(icsk->icsk_ack.timeout, jiffies)) {
  22. 229                 sk_reset_timer(sk, &icsk->icsk_delack_timer, icsk->icsk_ack.timeout);
  23. 230                 goto out;
  24. 231         }
复制代码
假设场景为:
1. timer触发,进入219行,重置timer的超时时间为jiffies+TCP_DELACK_MIN,返回
2. 在这个过程中另一个路径修改了icsk->icsk_ack.timeout 要大于jiffies+TCP_DELACK_MIN,尚没有触发sk_reset_timer
3.timer到期运行,此时的icsk->icsk_ack.timeout > jiffies.
   

论坛徽章:
0
3 [报告]
发表于 2013-11-10 19:51 |只看该作者
本帖最后由 henrystark 于 2013-11-10 19:51 编辑

回复 2# 瀚海书香

最近也在看这一段代码,有些疑问不大清楚:
1.在tcp相关的源代码里,没有看到tcp主流程显式调用tcp_delack_timer,但这个函数确实起作用了。
2.tcp_output.c中有tcp_send_delayed_ack函数,也判断是否超时,如果到了发送时间,就发送ack,这一点很好理解。但是,如果没有达到发送时间,就需要等待定时器。但是它的逻辑也没有显式调用delack_timer,那么,等待定时器这个过程如何实现?
3.delack_timer是一个“回调”函数,这个“回调”体现在哪里?
4.我在源代码里面使用过dump_stack(),结果显示delack_timer的调用过程难以理解
   
另外,delack_timer这个函数有个有趣的地方:它可以直接发送ack。

论坛徽章:
0
4 [报告]
发表于 2013-11-10 22:29 |只看该作者
回复 3# henrystark

所谓定时器 回调函数的整个工作流程是这样的:
1,初始化某个定时器。
如:TCP中初始化delack定时器如下。这样就向内核申请了一个定时器。
void tcp_init_xmit_timers(struct sock *sk)
{
...
        init_timer(&tp->delack_timer);
        tp->delack_timer.function=&tcp_delack_timer;
        tp->delack_timer.data = (unsigned long) sk;
        tp->ack.pending = 0;
...
}

2,某个事件触发了启动定时器,比如在下述函数中,发送ack的时候发现不用立即发送,延迟一会发送更好,于是启动定时:
void tcp_send_ack(struct sock *sk)
{
tcp_reset_xmit_timer(sk, TCP_TIME_DACK, TCP_DELACK_MAX);//启动定时器
}

3,等待定时器设置的时间到点后,中断就会导致调用定时器设置的函数被调用,即 tcp_delack_timer。而该函数的功能就是发送ack给对端。
tp->delack_timer.function=&tcp_delack_timer;
   

论坛徽章:
0
5 [报告]
发表于 2013-11-11 10:07 |只看该作者
回复 4# linux_26
3.2.18的逻辑稍微有些不同,看这段代码,设置delack_timer的是最后一句sk_reset_timer吗?怎么看都不像。。。
  1. void tcp_send_delayed_ack(struct sock *sk)
  2. {
  3.         struct inet_connection_sock *icsk = inet_csk(sk);
  4.         int ato = icsk->icsk_ack.ato;
  5.         unsigned long timeout;

  6.         if (ato > TCP_DELACK_MIN) {
  7.                 const struct tcp_sock *tp = tcp_sk(sk);
  8.                 int max_ato = HZ / 2;

  9.                 if (icsk->icsk_ack.pingpong ||
  10.                     (icsk->icsk_ack.pending & ICSK_ACK_PUSHED))
  11.                         max_ato = TCP_DELACK_MAX;

  12.                 /* Slow path, intersegment interval is "high". */

  13.                 /* If some rtt estimate is known, use it to bound delayed ack.
  14.                  * Do not use inet_csk(sk)->icsk_rto here, use results of rtt measurements
  15.                  * directly.
  16.                  */
  17.                 if (tp->srtt) {
  18.                         int rtt = max(tp->srtt >> 3, TCP_DELACK_MIN);

  19.                         if (rtt < max_ato)
  20.                                 max_ato = rtt;
  21.                 }

  22.                 ato = min(ato, max_ato);
  23.         }

  24.         /* Stay within the limit we were given */
  25.         timeout = jiffies + ato;

  26.         /* Use new timeout only if there wasn't a older one earlier. */
  27.         if (icsk->icsk_ack.pending & ICSK_ACK_TIMER) {
  28.                 /* If delack timer was blocked or is about to expire,
  29.                  * send ACK now.
  30.                  */
  31.                 if (icsk->icsk_ack.blocked ||
  32.                     time_before_eq(icsk->icsk_ack.timeout, jiffies + (ato >> 2))) {
  33.                         tcp_send_ack(sk);
  34.                         return;
  35.                 }

  36.                 if (!time_before(timeout, icsk->icsk_ack.timeout))
  37.                         timeout = icsk->icsk_ack.timeout;
  38.         }
  39.         icsk->icsk_ack.pending |= ICSK_ACK_SCHED | ICSK_ACK_TIMER;
  40.         icsk->icsk_ack.timeout = timeout;
  41.         sk_reset_timer(sk, &icsk->icsk_delack_timer, timeout);
  42. }
复制代码
  1. void sk_reset_timer(struct sock *sk, struct timer_list* timer,
  2.                     unsigned long expires)
  3. {
  4.         if (!mod_timer(timer, expires))
  5.                 sock_hold(sk);
  6. }
复制代码
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP