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


    内核通用的套接口(不包括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);
        }
    }

  • 相关阅读:
    leetcode792:匹配子序列的单词数
    Easyui 常用语法-其他
    cmake 入门笔记
    Python实战实例代码-网络爬虫-数据分析-机器学习-图像处理
    docker基于alpine基础镜像合集(java、python)集成chrome
    python实现微信新版v3的jsapi支付
    使用python对比两个json文件的不同并输出
    【SCI征稿】2区数字技术、供应链网络、人工智能、工业物联网智能传感器等领域,进展超顺,仅3个月左右录用
    Spring Boot-2.3.7.RELEASE整合activiti-6.0示例步骤
    韩国机器人公司Rainbow Robotics推出RB-Y1轮式双臂机器人
  • 原文地址:https://blog.csdn.net/wuyongmao/article/details/126266590