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什么都是对的,宗教般的正确。

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值