为什么Ruby 2.5.0会在Alpine Linux中引发SystemStackError,即使堆栈级别不是太深?

kos*_*goe 13 ruby

这是重现问题的示例代码.

# test.rb
n = 100000
res = {}
1.upto(n).to_a.inject(res) do |r, i|
  r[i] = {}
end

def f(x)
  x.each_value { |v| f(v) }
end

f(res)
Run Code Online (Sandbox Code Playgroud)

使用Docker Hub提供的Docker镜像运行此代码.

  • Alpine Linux中的Ruby 2.5.0是意想不到的(不太深)
  • Alpine Linux中的Ruby 2.4.3是预期的
  • Debian中的RUby 2.5.0是预期的

有什么区别?

补充说明

(对不起,我的英语不好.)

这是一个补充说明.

我知道示例代码导致SystemStackError.我编写了示例代码以导致SystemStackError.

我想知道"水平"差异的原因.

  • Alpine Linux中的Ruby 2.5.0 - >"...... 137级..."
  • 在ALpine Linux中使用RUby 2.4.3 - >"...... 10067级......"
  • Debian中的RUby 2.5.0 - >"...... 9866级......"

Ruby 2.5.0 + Alpine Linux(ruby:2.5.0-alpine3.7)

在这种情况下,堆栈级别为137.

% docker container run -v (pwd):/mnt/my --rm -it ruby:2.5.0-alpine3.7 ruby -v /mnt/my/test.rb
ruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-linux-musl]
Traceback (most recent call last):
        149: from /mnt/my/test.rb:11:in `<main>'
        148: from /mnt/my/test.rb:8:in `f'
        147: from /mnt/my/test.rb:8:in `each_value'
        146: from /mnt/my/test.rb:8:in `block in f'
        145: from /mnt/my/test.rb:8:in `f'
        144: from /mnt/my/test.rb:8:in `each_value'
        143: from /mnt/my/test.rb:8:in `block in f'
        142: from /mnt/my/test.rb:8:in `f'
         ... 137 levels...
          4: from /mnt/my/test.rb:8:in `f'
          3: from /mnt/my/test.rb:8:in `each_value'
          2: from /mnt/my/test.rb:8:in `block in f'
          1: from /mnt/my/test.rb:8:in `f'
/mnt/my/test.rb:8:in `each_value': stack level too deep (SystemStackError)
Run Code Online (Sandbox Code Playgroud)

但简单的情况下,堆栈级别为13092.

% docker container run --rm -it ruby:2.5.0-alpine3.7 ruby -v -e 'def f; f; end; f'
ruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-linux-musl]
Traceback (most recent call last):
        13104: from -e:1:in `<main>'
        13103: from -e:1:in `f'
        13102: from -e:1:in `f'
        13101: from -e:1:in `f'
        13100: from -e:1:in `f'
        13099: from -e:1:in `f'
        13098: from -e:1:in `f'
        13097: from -e:1:in `f'
         ... 13092 levels...
            4: from -e:1:in `f'
            3: from -e:1:in `f'
            2: from -e:1:in `f'
            1: from -e:1:in `f'
-e:1:in `f': stack level too deep (SystemStackError)
Run Code Online (Sandbox Code Playgroud)

Ruby 2.4.3 + Alpine Linux(ruby:2.4.3-alpine3.7)

在这种情况下,堆栈级别为10067.与上述情况有很大不同.

% docker container run -v (pwd):/mnt/my --rm -it ruby:2.4.3-alpine3.7 ruby -v /mnt/my/test.rb
ruby 2.4.3p205 (2017-12-14 revision 61247) [x86_64-linux-musl]
/mnt/my/test.rb:8:in `each_value': stack level too deep (SystemStackError)
        from /mnt/my/test.rb:8:in `f'
        from /mnt/my/test.rb:8:in `block in f'
        from /mnt/my/test.rb:8:in `each_value'
        from /mnt/my/test.rb:8:in `f'
        from /mnt/my/test.rb:8:in `block in f'
        from /mnt/my/test.rb:8:in `each_value'
        from /mnt/my/test.rb:8:in `f'
        from /mnt/my/test.rb:8:in `block in f'
         ... 10067 levels...
        from /mnt/my/test.rb:8:in `block in f'
        from /mnt/my/test.rb:8:in `each_value'
        from /mnt/my/test.rb:8:in `f'
        from /mnt/my/test.rb:11:in `<main>'
Run Code Online (Sandbox Code Playgroud)

我显示了差异 Dockerfile的输出.

--- 2.4/alpine3.7/Dockerfile    2017-12-28 20:34:43.000000000 +0900
+++ 2.5/alpine3.7/Dockerfile    2017-12-28 20:34:43.000000000 +0900
@@ -7,9 +7,9 @@
                echo 'update: --no-document'; \
        } >> /usr/local/etc/gemrc

-ENV RUBY_MAJOR 2.4
-ENV RUBY_VERSION 2.4.3
-ENV RUBY_DOWNLOAD_SHA256 23677d40bf3b7621ba64593c978df40b1e026d8653c74a0599f0ead78ed92b51
+ENV RUBY_MAJOR 2.5
+ENV RUBY_VERSION 2.5.0
+ENV RUBY_DOWNLOAD_SHA256 1da0afed833a0dab94075221a615c14487b05d0c407f991c8080d576d985b49b
 ENV RUBYGEMS_VERSION 2.7.4
 ENV BUNDLER_VERSION 1.16.1
Run Code Online (Sandbox Code Playgroud)

这意味着使用相同的Alpine Linux.只是Ruby是不同的.

Ruby 2.5.0 + Debian(ruby:2.5.0-stretch)

在这种情况下,堆栈级别为9866.此案例使用Ruby 2.5.0,但它运行在Debian(而不是Alpine).

% docker container run -v (pwd):/mnt/my --rm -it ruby:2.5.0-stretch ruby -v /mnt/my/test.rb
ruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-linux]
Traceback (most recent call last):
        9878: from /mnt/my/test.rb:11:in `<main>'
        9877: from /mnt/my/test.rb:8:in `f'
        9876: from /mnt/my/test.rb:8:in `each_value'
        9875: from /mnt/my/test.rb:8:in `block in f'
        9874: from /mnt/my/test.rb:8:in `f'
        9873: from /mnt/my/test.rb:8:in `each_value'
        9872: from /mnt/my/test.rb:8:in `block in f'
        9871: from /mnt/my/test.rb:8:in `f'
         ... 9866 levels...
           4: from /mnt/my/test.rb:8:in `f'
           3: from /mnt/my/test.rb:8:in `each_value'
           2: from /mnt/my/test.rb:8:in `block in f'
           1: from /mnt/my/test.rb:8:in `f'
/mnt/my/test.rb:8:in `each_value': stack level too deep (SystemStackError)
Run Code Online (Sandbox Code Playgroud)

Joe*_*Joe 1

我们在一些边缘案例中也看到了这一点。我们已经确认,操作系统和 Ruby 堆栈大小在所有情况下都是相同的。(alpine 2.4.3、alpine 2.5.0 以及在 MacOS 上本地运行 2.5.0)

迄今为止我们能发现的唯一区别是 Alpine 2.5.0 镜像是基于 Alpine 3.7 构建的,后者使用 LibreSSL 构建 Ruby。2.4.3 镜像是基于 Alpine 3.4 构建的,后者仍然使用 OpenSSL。我们针对 OpenSSL 的本地 2.5.0 构建不会显示这种“短”堆栈长度。