标签: 拥塞控制

  • TCP拥塞控制算法的实现

    基于对google提出的bbr算法源码阅读的一些学习:

    https://github.com/torvalds/linux/blob/master/net/ipv4/tcp_bbr.c#L39

    并不详细介绍bbr的原理,也不逐行解释

    拥塞接口

    TCP底层的拥塞控制通过若干个定义在TCP层的接口被模块化了,不同的算法就可以直接hook对应需要的回调来实现不同的TCP拥塞控制算法。

    Linux内核机制

    • 内核模块化,通过module_init和module_exit来初始化和卸载一个模块。
    • 模块开发不能使用常规的库函数,例如printf, malloc,需要使用内核提供的:printk, kmalloc。
    • 模块是系统的一部分,所以模块卸载需要自己卸载干净,系统不会帮忙进行回收。
    • 模块内开发不能直接对用户指针的对象取值或者复制,因为那是用户空间的地址,在内核空间会没有映射导致有问题,需要使用put_user给用户空间的内存地址复制。
    • 与上类似的函数还有:copy_to_user,copy_from_user,get_user,put_user。
    • 拥塞控制的结构体是:tcp_congestion_ops,定义了一系列的事件函数,通过hook这些函数实现不同的TCP拥塞控制算法。
    static struct tcp_congestion_ops tcp_bbr_cong_ops __read_mostly = {
        .flags      = TCP_CONG_NON_RESTRICTED,
        .name       = "bbr",
        .owner      = THIS_MODULE,
        .init       = bbr_init,                         // 初始化函数
        .cong_control   = bbr_main,                   // 在拥塞状态下,发包前的回调,用于更新拥塞窗口和传输速度
        .sndbuf_expand  = bbr_sndbuf_expand,        // 返回给tcp_sndbuf_expand使用的乘数
        .undo_cwnd  = bbr_undo_cwnd,                // 损失后cwnd的新值
        .cwnd_event = bbr_cwnd_event,               // 当发生拥塞时的回调
        .ssthresh   = bbr_ssthresh,                   // 返回慢启动的阈值
        .min_tso_segs   = bbr_min_tso_segs,           // 系统sysctl_tcp_min_tso_segs的重写
        .get_info   = bbr_get_info,                   // 获取inet的日志信息
        .set_state  = bbr_set_state,                // ca_state变化前会调用
    };
    
    static int __init bbr_register(void)
    {
        BUILD_BUG_ON(sizeof(struct bbr) > ICSK_CA_PRIV_SIZE);
        return tcp_register_congestion_control(&tcp_bbr_cong_ops);
    }
    
    static void __exit bbr_unregister(void)
    {
        tcp_unregister_congestion_control(&tcp_bbr_cong_ops);
    }
    
    module_init(bbr_register);
    module_exit(bbr_unregister);
    • BUILD_BUG_ON是编译时的检测,为了保证bbr的结构体小于等于内核准备的空间大小。
    • 不同的机器的ICSK_CA_PRIV_SIZE值可能不同,但是编译确定下来还有bbr的空间大小和分布,所以一个系统编译,其他系统也是能够使用的。
    • do_div表示除法函数mod = do_div(x, y),结果存储在x中,余数存储在mod中。