TCP rwnd自适应

紧接着前面的继续说。

填满TCP长肥管道
socket与Linux TCP
TCP rwnd算法挖坟

找到了RFC675这个宝贝,值得一读再读。本文对rwnd的计算做个简单侵入hook,并且做个模拟。

将rwnd替换成自己计算出来的值,需要修改 __tcp_select_window 的返回值,而它的计算过程可以在 __tcp_select_window 之前进行:

下面是代码:

#!/usr/local/bin/stap -g

%{
#include <net/tcp.h>
%}

function get_rwnd(skk:long)
%{
    struct sock *sk = (struct sock *)STAP_ARG_skk;

    if (inet_sk(sk)->inet_num == 5001) {
                // loss_buff统计比较麻烦,用计数器比较好,干脆全部只统计包量,不统计包长。
        unsigned long ret = sk->sk_rcvbuf /*+ loss_buff*/; 
        ret *= ret;
        do_div(ret, sock_net(sk)->ipv4.sysctl_tcp_rmem[2]);
        ret *= 315724928;
        do_div(ret, sock_net(sk)->ipv4.sysctl_tcp_rmem[2]);
        STAP_RETVALUE = ret;
    } else {
        STAP_RETVALUE = 0;
    }
%}

global win = 0

probe kernel.function("__tcp_select_window")
{
    win = get_rwnd($sk);
}

probe kernel.function("__tcp_select_window").return
{
    if (win != 0) {
        $return = win;
    }
}

这段逻辑事实上应该替换 __tcp_select_window 逻辑的,并且free space应该减去由于应用读取不畅造成的丢包总量。大致是个意思。
下面是一个模拟程序:

#define _GNU_SOURCE

#include<stdio.h>
#include<string.h>
#include<pthread.h>
#include<stdlib.h>
#include<unistd.h>
#include <math.h>
#include <sched.h>

pthread_t tid_sender, tid_receiver, tid_reader, tid_log;

unsigned long seq_front = 0, seq_end = 0;
unsigned int rwnd = 10, logwnd = 0;
unsigned int rate;

void* logger(void *arg)
{
    cpu_set_t set;
    CPU_ZERO(&set);
    CPU_SET(4, &set);
    while(1) {
        printf ("front:%d  end:%d  rwnd:%d rate:%d\n", seq_front, seq_end, logwnd, rate);
    }
}

void* sender(void *arg)
{
    unsigned long tmp;
    unsigned int wnd;
    cpu_set_t set;

    CPU_ZERO(&set);
    CPU_SET(5, &set);
    while(1) {
        wnd = __sync_fetch_and_or(&rwnd, 0);
        if (wnd == 0)
            continue;
        __sync_fetch_and_and(&rwnd, 0);
        logwnd = wnd;
        while (wnd) {
            __sync_fetch_and_add(&seq_front, 1);
            wnd --;
            usleep(10000);
        }
    }
}
void* receiver(void *arg)
{
    unsigned long tmp_front, tmp_end, i, newwnd;
    unsigned int wnd;

    cpu_set_t set;
    CPU_ZERO(&set);
    CPU_SET(6, &set);

    wnd = rwnd;
    while (1) {
        while (wnd) {
            tmp_front = __sync_fetch_and_sub(&seq_front, 0);
            if (tmp_front == 0) {
                continue;
            }
            __sync_fetch_and_sub(&seq_front, 1);
            tmp_end = __sync_fetch_and_add(&seq_end, 1);
            wnd --;
        }
        newwnd = 1000*pow(1 - ((double)tmp_end)/((double)0xfffff), 3.5);
        wnd = __sync_or_and_fetch(&rwnd, newwnd);
    }
}
void* reader(void *arg)
{
    unsigned long tmp;
    cpu_set_t set;
    CPU_ZERO(&set);
    CPU_SET(7, &set);
    while (1) {
        tmp = __sync_fetch_and_sub(&seq_end, 0);
        if (tmp == 0)
            continue;
        __sync_fetch_and_sub(&seq_end, 1);
        usleep(rate);
    }
}

int main(int argc, char **argv)
{
    rate = 10000;
    pthread_create(&tid_sender, NULL, &sender, NULL);
    pthread_create(&tid_receiver, NULL, &receiver, NULL);
    pthread_create(&tid_reader, NULL, &reader, NULL);
    pthread_create(&tid_log, NULL, &logger, NULL);

    while (getchar() != 'e') {
        scanf("%d", &rate);
    }
}

看懂这个代码,如有bug修复它,运行这个程序,输入微调reader速度,就可以观察到自适应的过程了。

买小龙虾前,把昨晚的代码写了一下,运行一下还是有趣的。但还是要喷Linux。Linux内核里不好做分数乘除这种浮点运算是很多TCP优化落不了地的根源!u64都装不住一个分数三次方,不得不写一个大数运算函数,为了折腾一个1000000量级这么小的数的屡次平方和除法,竟然要搞“大数”,真tmd讽刺!垃圾linus,sb。搞不懂Linux内核如今还不支持浮点数的根本原因,当然,你要是解释它为什么不支持,总有1000个原因,反正Linux什么都是对的,宗教般的正确。

浙江温州皮鞋湿,下雨进水不会胖。

原文链接: https://blog.csdn.net/dog250/article/details/125014211

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍;

也有高质量的技术群,里面有嵌入式、搜广推等BAT大佬

    TCP rwnd自适应

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/405499

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年4月26日 上午9:14
下一篇 2023年4月26日 上午9:14

相关推荐