TCP rwnd自适应

Posted dog250

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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什么都是对的,宗教般的正确。

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

以上是关于TCP rwnd自适应的主要内容,如果未能解决你的问题,请参考以下文章

计算机网络之TCP协议流量拥塞控制算法原理:滑动窗口cwnd与rwnd

新 TCP 流控

TCP拥塞控制

Java之大数加减乘除——构建类

TCP之Delay ACK在Linux和Windows上实现的异同-Linux的自适应ACK

TCP流量控制