关于 Out of Socket memory 的解释

      访问: 3,260 次      评论    

主要是想跟大家分享一下对 Linux 内核报 Out of Socket memory 错误的相关研究,涉及几个内核参数的讲解:

  • cat /proc/net/sockstat

  • net.ipv4.tcp_mem = 4631232   6174976    9262464

  • net.ipv4.tcp_max_orphans = 262144

  • net.ipv4.tcp_orphan_retries = 0

------- 正文开始:------------------

举一个网上看到的例子:

一台 Nginx server,到晚上高峰 messages 出现大量的如下信息:

Apr 23 22:43:21 rs1 kernel: […] Out of socket memory

两种情况内核会出发 "Out of socket memory" 的信息:

  1. 有很多的孤儿套接字(orphan sockets) net.ipv4.tcp_max_orphans(常见

  2. tcp socket 用尽了内核所定义的内存限制 net.ipv4.tcp_mem(不常见


内核代码:

The only match for "Out of socket memory" in the kernel's code (as of v2.6.38) is in net/ipv4/tcp_timer.c:

static int tcp_out_of_resources(struct sock *sk, int do_reset)
{
        struct tcp_sock *tp = tcp_sk(sk);
        int shift = 0;
 
        /* If peer does not open window for long time, or did not transmit
         * anything for long time, penalize it. */
        if ((s32)(tcp_time_stamp - tp->lsndtime) > 2*TCP_RTO_MAX || !do_reset)
                shift++;
 
        /* If some dubious ICMP arrived, penalize even more. */
        if (sk->sk_err_soft)
                shift++;
 
        if (tcp_too_many_orphans(sk, shift)) {
                if (net_ratelimit())
                        printk(KERN_INFO "Out of socket memory\n");

So the question is: when does tcp_too_many_orphans return true? Let's take a look in include/net/tcp.h:

static inline bool tcp_too_many_orphans(struct sock *sk, int shift)
{
        struct percpu_counter *ocp = sk->sk_prot->orphan_count;
        int orphans = percpu_counter_read_positive(ocp);
 
        if (orphans << shift > sysctl_tcp_max_orphans) {
                orphans = percpu_counter_sum_positive(ocp);
                if (orphans << shift > sysctl_tcp_max_orphans)
                        return true;
        }
 
        if (sk->sk_wmem_queued > SOCK_MIN_SNDBUF &&
            atomic_long_read(&tcp_memory_allocated) > sysctl_tcp_mem[2])
                return true;
        return false;
}

 

一、首先看看情况2:net.ipv4.tcp_mem


查看内核分配了多少的内存给 TCP(以一台48G内存的服务器举例来说):

# cat /proc/sys/net/ipv4/tcp_mem
4631232     6174976     9262464
(low       pressure    high)

 

  • 第一个数字表示 low,当 tcp 使用的内存 page 少于 4631232 (17.67G)时,kernel 不对其进行任何的干预

  • 第二个数字表示 pressure,当 tcp 使用了超过 6174976 的 pages(23.56G) 时,kernel 会进入 “memory pressure” 状态,TCP会“温和”地减少它的内存使用量,直到TCP的总内存使用量低于low 值时,退出 memory pressure 状态

  • 第三个数字表示 high,TCP所能使用的全局最大内存量,当 tcp 使用的 pages 超过 9262464 (35.33G)时,我们就会看到题目中显示的信息

  • 详细可 man tcp,搜索 tcp_mem, 查看英文原文解释

关于tcp_mem

注:对于 TCP socket 的内存分配来说,是使用 page 来计数的,而非 bytes,一般情况下 1 page = 4096 bytes。page 大小可以通过下面命令获得:

# getconf PAGESIZE
4096

net.ipv4.tcp_mem 的默认值是由内核在启动时根据可用内存总量来自动计算的,基本上结果是非常大的量了,根本不用去调节它!

查看 tcp 实际用的内存:

# cat /proc/net/sockstat | grep TCP
TCP: inuse 5740 orphan 204 tw 16914 alloc 5740 mem 1220

可以看到,实际使用的 mem pages(1220) 远远小于 net.ipv4.tcp_mem 中的 low 值,所以,“Out of socket memory”的错误是不是由第二种情况引起的。


二、再看看情况1:net.ipv4.tcp_max_orphans


关于 orphan socket 的解释,可以看man tcp 然后搜索tcp_max_orphans。orphaned socket 对于应用程序来说,意义不大(因为应用程序不能与它们进行交互),这也是内核要限制被 orphaned socket 消耗内存的原因。而对于 web server (或者HTTP Load Balancer)来说,有一定量的 orphaned socket 也属正常,那么多的连接放在那儿了。

关于orphaned 连接的计数

关于orphan 计数(cat /proc/net/sockstat),经过我实际测试,只有FIN_WAIT1 和 LAST_ACK 状态的连接会计入 orphan,FIN_WAIT2和TIME_WAIT 都计入tw,而CLOSE_WAIT 既不计入orphan也不计入tw。

即像 man tcp 中tcp_max_orphans 一段所说,这个限制是用来抵御简单的拒绝服务攻击的,所以在没有收到对方回复的ACK(涉嫌denial-of-service)时的状态 FIN_WAIT1和LAST_ACK 就被计入orphan 计数统计了!

查看 orphaned socket 限制:

# cat /proc/sys/net/ipv4/tcp_max_orphans
262144

对比当前系统中的orphaned socket数量:

# cat /proc/net/sockstat | grep TCP
TCP: inuse 35938 orphan 21564 tw 70529 alloc 35942 mem 1894

由于内核代码中有个移位 shift 运算(见上面代码),shift 的范围是0-2,判断条件 if (orphans << shift > sysctl_tcp_max_orphans)  ,即某些情况下内核想“惩罚”更多的异常连接,它将实际的orphan数量21564 左移1位(乘2)或2位(乘4)来“人为”的加大异常连接的score值。其结果就是,基于这样的实现后,即便你的系统中存在的orphan连接数量远低于 net.ipv4.tcp_max_orphans 的值时,系统也会报出“Out of socket memory”的情况。

所以内核 shift 计算后的值跟实际的值是 2x 或者是 4x 的关系。


三、尝试解决:


  1. 现在根据实际情况,观察业务高峰期时系统 /proc/net/socketstat 中 orphan 的计数,将其乘以4,再加大一些,作为 net.ipv4.tcp_max_orphans 值就可以了。

  2. 还有个叫 net.ipv4.tcp_orphan_retries 参数(默认是0,在内核实现后其实就是8次,火丁有讲),对于 web server,可以减小。

 

涉及到内核的东西,网上大部分都是病急乱投医。这个不分是中文的还是英文的,要找到一篇好的解释真不容易。

参考:The "Out of socket memory" error 

 


添加新评论