TCP对发送缓存的管理是在两个层面上进行的,一个层面是单个socket的发送缓存管理,
另一个层面是整个TCP层的内存管理。
单个socket的发送缓存所涉及的变量。
[java] struct sock { ... /* 预分配缓存大小,是已经分配但尚未使用的部分 */ int sk_forward_alloc; ... /* 提交给IP层的发送数据大小(累加skb->truesize) */ atomic_t sk_wmem_alloc; ... int sk_sndbuf; /* 发送缓冲区大小的上限 */ struct sk_buff_head sk_write_queue; /* 发送队列 */ ... /* 发送队列的总大小,包含发送队列中skb负荷大小, * 以及sk_buff、sk_shared_info结构体、协议头的额外开销。 */ int sk_wmem_queued; ... };
整个TCP层的内存相关变量。
[java] struct proto tcp_prot = { .name = "TCP", .owner = THIS_MODULE, ... /* 设置TCP的内存压力标志,把tcp_memory_pressure置为1 */ .enter_memory_pressure = tcp_enter_memory_pressure, /* 检查sock是否有剩余的发送缓存(sk_wmem_queued < sk_sndbuf)。 * 值得注意的是,用户可以使用TCP_NOTSENT_LOWAT选项来避免占用过多的发送缓存。 */ .stream_memory_free = tcp_stream_memory_free, ... /* TCP目前已经分配的内存 */ .memory_allocated = &tcp_memory_allocated, /* TCP内存压力标志,超过tcp_mem[1]后设置,低于tcp_mem[0]后清除 */ .memory_pressure = &tcp_memory_pressure, /* TCP内存使用的最小值、压力值、最大值,单位为页 */ .sysctl_mem = sysctl_tcp_mem, /* 每个sock写缓存的最小值、默认值、最大值,单位为字节 */ .sysctl_wmem = sysctl_tcp_wmem, /* 每个sock读缓存的最小值、默认值、最大值,单位为字节 */ .sysctl_rmem = sysctl_tcp_rmem, .max_header = MAX_TCP_HEADER, /* 协议头的最大长度 */ ... }; atomic_long_t tcp_memory_allocated; /* Current allocated memory. */ int tcp_memory_pressure __read_mostly;
tcp_mem是整个TCP层的内存消耗,单位为页。
long sysctl_tcp_mem[3] __read_mostly;
tcp_mem - vector of 3 INTEGERs: min, pressure, max
min: below this number of pages TCP is not bothered about its memory appetite.
pressure: when amount of memory allocated by TCP exceeds this number of pages,
TCP moderates it memory consumption and enters memory pressure mode, which
is exited when memory consumption falls under min.
max: number of pages allowed for queueing by all TCP sockets.
Defaults are calculated at boot time from amount of available memory.
在tcp_init()中,调用tcp_init_mem()来初始化sysctl_tcp_mem[3]数组。
tcp_mem[0]是最小值,为3/32的系统内存。
tcp_mem[1]是压力值,为1/8的系统内存,也是最小值的4/3。
tcp_mem[2]是最大值,为3/16的系统内存,也是最小值的2倍。
[java] static void tcp_init_mem(void) { /* nr_free_buffer_pages()计算ZONE_DMA和ZONE_NORMAL的页数, * 对于64位系统来说,其实就是所有内存了。 */ unsigned long limit = nr_free_buffer_pages() / 8; limit = max(limit, 128UL); /* 不能低于128页 */ sysctl_tcp_mem[0] = limit / 4 * 3; /* 最小值设为3/32的系统内存 */ sysctl_tcp_mem[1] = limit; /* 压力值设为1/8的系统内存 */ sysctl_tcp_mem[2] = sysctl_tcp_mem[0] * 2; /* 最大值设为3/16的系统内存 */ }
tcp_wmem是每个sock的写缓存,单位为字节。
int sysctl_tcp_wmem[3] __read_mostly;
tcp_wmem - vector of 3 INTEGERs: min, default, max
min: Amount of memory reserved for send buffers for TCP sockets.
Each TCP socket has rights to use it due to fact of its birth.
Default: 1 page
default: initial size of send buffer used by TCP sockets.
This value overrides net.core.wmem_default used by other protocols.
It is usually lower than net.core.wmem_default.
Default: 16K
max: Maximal amount of memory allowed for automatically tuned send buffers
for TCP sockets. This value does not override net.core.wmem_max.
Calling setsockopt() with SO_SNDBUF disables automatic tuning of that
socket's send buffer size, in which case this value is ignored.
Default: between 64K and 4MB, depending on RAM size.
tcp_wmem[0]是最小值,为4KB。
tcp_wmem[1]是默认值,为16KB。
tcp_wmem[2]是最大值,为4MB。
tcp_rmem[0]是最小值,为4KB。
tcp_rmem[1]是默认值,为87380字节。
tcp_wmem[2]是最大值,为6MB(之前的内核为4MB)。
[java] void __init tcp_init(void) { ... /* 初始化sysctl_tcp_mem数组 */ tcp_init_mem(); /* Set per-socket limits to no more than 1/128 the pressure threshold */ /* 系统内存的1/128,单位为字节 */ limit = nr_free_buffers_pages() << (PAGE_SHIFT - 7); max_wshare = min(4UL * 1024 * 1024, limit); /* 不能低于4MB */ max_rshare = min(6UL * 1024 * 1024, limit); /* 不能低于6MB */ sysctl_tcp_wmem[0] = SK_MEM_QUANTUM; /* 最小值为一页,4KB */ sysctl_tcp_wmem[1] = 16 * 1024; /* 默认值为16KB */ /* 取系统内存的1/128、4MB中的小者,并且不能低于64KB。 * 也就是说如果系统内存超过512MB,那么最大值为4MB。 */ sysctl_tcp_wmem[2] = max(64 * 1024, max_wshare); sysctl_tcp_rmem[0] = SK_MEM_QUANTUM; /* 最小值为一页,4KB */ sysctl_tcp_rmem[1] = 87380; /* 默认值为差不多85KB */ /* 去系统内存的1/128、6MB中的小者,且不能低于87380。 * 也就是说如果系统内存超过768MB,那么最大值为6MB。 * 在较低内核版本中,是如果系统内存超过512MB,最大值为4MB。 */ sysctl_tcp_rmem[2] = max(87380, max_rshare); ... }