IT技术互动交流平台

关于/proc/sys/net/core/wmem_default设置为2048后 udp可发送数据包最大值的问题

作者:瀚海书香  发布日期:2013-04-18 20:28:25

Author: Tony <tingw.liu@gmail.com> <tony@liutingwei.com>
本文基于SLES 11 sp2的3.0.13-0.27内核

首先参数/proc/sys/net/core/wmem_default表示的是udp socket默认的用于发送最大可申请的空间。相应代码在net/core/sock.c中


void sock_init_data(struct socket *sock, struct sock *sk) 
{
skb_queue_head_init(&sk->sk_receive_queue);
skb_queue_head_init(&sk->sk_write_queue);
skb_queue_head_init(&sk->sk_error_queue);
#ifdef CONFIG_NET_DMA
skb_queue_head_init(&sk->sk_async_wait_queue);
#endif

sk->sk_send_head = NULL;

init_timer(&sk->sk_timer);

sk->sk_allocation = GFP_KERNEL;
sk->sk_rcvbuf = sysctl_rmem_default;
sk->sk_sndbuf = sysctl_wmem_default;
sk->sk_state = TCP_CLOSE;
sk_set_socket(sk, sock);

sock_set_flag(sk, SOCK_ZAPPED);

if (sock) {
sk->sk_type = sock->type;
sk->sk_wq = sock->wq;
sock->sk = sk; 
} else 
sk->sk_wq = NULL;

spin_lock_init(&sk->sk_dst_lock);
rwlock_init(&sk->sk_callback_lock);
lockdep_set_class_and_name(&sk->sk_callback_lock,
af_callback_keys + sk->sk_family,
af_family_clock_key_strings[sk->sk_family]);

sk->sk_state_change = sock_def_wakeup;
sk->sk_data_ready = sock_def_readable;
sk->sk_write_space = sock_def_write_space;
sk->sk_error_report = sock_def_error_report;
sk->sk_destruct = sock_def_destruct; 

设置/proc/sys/net/core/wmem_default 为2048,使用udpsendutil工具,测试发现,可以发送的最大数据包为4432。见下图
 


 

对于4432这个数字,让我这数学专业的高材生(自夸一下)也找不出与2048到底有什么联系啊。。。 www.it165.net
于是通读udp.c代码,发现了问题的原因,为了验证自己的想法,修改net/core/sock.c和net/ipv4/ip_output.c 添加日志输出,改动后的代码见附录(所有改动部分都被宏TINGWEI_UDP_DEBUG 包裹),编译内核后,运行日志如下:
 


 

从日志可以分析出:
     当udp_sendmsg尝试发送4432字节数据的时候:
            MTU是1500,所有第一个数据包最多发送(1500-8字节udp头),总共1472个字节。
            在net/core/sock.c中的sk_alloc函数,可以看到sk_wmem_alloc的初始值被设置为1。

 

struct sock *sk_alloc(struct net *net, int family, gfp_t priority,
struct proto *prot)
{
struct sock *sk;

sk = sk_prot_alloc(prot, priority | __GFP_ZERO, family);
if (sk) {
sk->sk_family = family;
/*
* See comment in struct sock definition to understand
* why we need sk_prot_creator -acme
*/
sk->sk_prot = sk->sk_prot_creator = prot;
sock_lock_init(sk);
sock_net_set(sk, get_net(net));
atomic_set(&sk->sk_wmem_alloc, 1);

sock_update_classid(sk);
}

return sk;
} 

            第一个skb的大小:
                32字节对齐的【1472字节数据+8字节udp头+20字节ip头+16字节对齐的[14字节mac头]】+240字节skb结构体,总共1776字节,申请成功;sk_wmem_alloc变为1+1776=1777
            第二个skb,由于没有udp头,所以最多是1480字节的数据,sk_wmem_alloc < 2 * 2048 (ip_output.c 935行),申请成功;sk_wmem_alloc变为1777+1776=3553
            第三个skb,申请的时候sk->sk_wmem_alloc = 3553 < 2 * 2048, 所有可以申请成功。sk_wmem_alloc变为3553+1776=5329 并且所有的数据部分(1472+1480+1480=4432)都已经包含,数据包可以发送成功。
     当udp_sendmsg尝试发送4433字节数据的时候:
        前3个skb跟上面是类似的,但是申请完之后,(1472+1480+1480 = 4432)仍有1个字节的数据没有发送,所以需要再申请一个skb来发送最后一个字节的数据。
        第四个skb,申请的时候sk->sk_wmem_alloc=5329 > 2 *2048, 所以申请失败,从ip_output.c的948行,进入错误处理流程,返回-ENOBUFS。

        上面的代码函数,与附录代码行数一致!


Tag标签: proc   udp  
  • 专题推荐

About IT165 - 广告服务 - 隐私声明 - 版权申明 - 免责条款 - 网站地图 - 网友投稿 - 联系方式
本站内容来自于互联网,仅供用于网络技术学习,学习中请遵循相关法律法规