套接口通用发送缓存区限定

TCPIP协议 专栏收录该内容
105 篇文章 6 订阅

内核通用的套接口(不包括TCP套接口)发送缓冲区大小可由PROC文件wmem_default获得,其最大值可由wmem_max文件得到。默认情况下,两者值相同,如下所示。

$ cat /proc/sys/net/core/wmem_max
212992
$
$ cat /proc/sys/net/core/wmem_default 
212992

两者的值在net_core_table结构中初始化,wmem_max的初始化为sysctl_wmem_max变量的值,wmem_default初始化为sysctl_wmem_default变量的值。在用户通过PROC文件配置这两者值的时候,将最小值限定在min_sndbuf的值之上。最小的发送缓冲区大小由宏SOCK_MIN_SNDBUF定义,其为sk_buff结构体大小与2048的和,之后在乘以2的所得到的值,意味着最小的发送缓冲区能够容纳2个数据包。

#define TCP_SKB_MIN_TRUESIZE    (2048 + SKB_DATA_ALIGN(sizeof(struct sk_buff)))
#define SOCK_MIN_SNDBUF     (TCP_SKB_MIN_TRUESIZE * 2)
static int min_sndbuf = SOCK_MIN_SNDBUF;

static struct ctl_table net_core_table[] = {
    {
        .procname   = "wmem_max",
        .data       = &sysctl_wmem_max,
        .extra1     = &min_sndbuf,
    },
    {
        .procname   = "wmem_default",
        .data       = &sysctl_wmem_default,
        .extra1     = &min_sndbuf,
    },
}

默认和最大的发送缓冲区的值由宏SK_WMEM_MAX定义,其值为256个大小为256字节的数据包及相应sk_buff所占用的内存空间。

__u32 sysctl_wmem_max __read_mostly = SK_WMEM_MAX;
__u32 sysctl_wmem_default __read_mostly = SK_WMEM_MAX;

#define SKB_TRUESIZE(X) ((X) + SKB_DATA_ALIGN(sizeof(struct sk_buff)) + SKB_DATA_ALIGN(sizeof(struct skb_shared_info)))

#define _SK_MEM_PACKETS     256
#define _SK_MEM_OVERHEAD    SKB_TRUESIZE(256)
#define SK_WMEM_MAX     (_SK_MEM_OVERHEAD * _SK_MEM_PACKETS)

初始化发送缓冲区大小


套接口创建时(inet_create),调用sock_init_data初始化发送缓冲的大小。初始值为默认的sysctl_wmem_default变量的值,即SK_WMEM_MAX。

static int inet_create(struct net *net, struct socket *sock, int protocol, int kern)
{
    sock_init_data(sock, sk);
    if (sk->sk_prot->init)
        err = sk->sk_prot->init(sk);
}
void sock_init_data(struct socket *sock, struct sock *sk)
{
    sk->sk_sndbuf       =   sysctl_wmem_default;
}

套接口创建函数,最后会调用具体协议的初始化回调函数,例如TCP协议的初始化函数tcp_init_sock,注意TCP会重新初始化发送缓冲的长度。UDP协议的套接口初始化函数udp_init_sock,不改变发送缓存大小,直接使用协议通用的初始配置。

struct proto udp_prot = {
    .name          = "UDP",
    .init          = udp_init_sock,
}
int udp_init_sock(struct sock *sk)
{
    skb_queue_head_init(&udp_sk(sk)->reader_queue);
    sk->sk_destruct = udp_destruct_sock;
}

套接口发送缓存的最大值


默认情况下套接口发送缓冲区最大值与缺省值相同。用户层可通过setsockopt系统调用,修改系统缺省的发送缓冲区大小值,但是设置值不能大于内核限定的最大值sysctl_wmem_max。并且用户设置的值不能小于最小值的一半(SOCK_MIN_SNDBUF表示2个数据包)。

int sock_setsockopt(struct socket *sock, int level, int optname, char __user *optval, unsigned int optlen)
{
    struct sock *sk = sock->sk;

    switch (optname) {
    case SO_SNDBUF:
        val = min_t(u32, val, sysctl_wmem_max);
set_sndbuf:
        sk->sk_userlocks |= SOCK_SNDBUF_LOCK;
        sk->sk_sndbuf = max_t(int, val * 2, SOCK_MIN_SNDBUF);
        break;
}

当用户设置新值,内核增加一个SOCK_SNDBUF_LOCK的标志在套接口结构的成员sk_userlocks中。在TCP三次握手完成之后,如果用户没有锁定发送缓冲区的大小,内核可根据双方协商的MSS值重设发送缓冲区大小值,反之,使用用户的设定值。再者,当接收到对端的ACK报文,确认了发送缓冲区中的报文之后,也进行一次发送缓冲区的检查,看是否可以扩展其大小。

void tcp_init_buffer_space(struct sock *sk)
{
    if (!(sk->sk_userlocks & SOCK_SNDBUF_LOCK))
        tcp_sndbuf_expand(sk);
}
static void tcp_new_space(struct sock *sk)
{
    struct tcp_sock *tp = tcp_sk(sk);

    if (tcp_should_expand_sndbuf(sk))
        tcp_sndbuf_expand(sk);
}

另外,内核中如果锁定了发送缓冲区的大小,试图减小其空间的操作都不会被执行,如函数sk_stream_moderate_sndbuf,在套接口发送缓冲空间进入压力状态后,也不会减小其大小。

static inline void sk_stream_moderate_sndbuf(struct sock *sk)
{
    if (!(sk->sk_userlocks & SOCK_SNDBUF_LOCK)) {
        sk->sk_sndbuf = min(sk->sk_sndbuf, sk->sk_wmem_queued >> 1);
        sk->sk_sndbuf = max_t(u32, sk->sk_sndbuf, SOCK_MIN_SNDBUF);
    }
}

 

内核版本 4.15.0

 

  • 0
    点赞
  • 0
    评论
  • 0
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

相关推荐
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值