【问题标题】:Setting stack size with setrlimit fails with gcc使用 setrlimit 设置堆栈大小因 gcc 失败
【发布时间】:2020-12-04 20:40:28
【问题描述】:

我在 Ubuntu 18.04 上使用 gcc 10.1。在定义一个大的堆栈分配变量时,我遇到了段错误,即使我的堆栈似乎足够大以容纳它。这是一个代码sn-p:

#include <iostream>
#include <array>
#include <sys/resource.h>

using namespace std;

int main() {
  if (struct rlimit rl{1<<28, 1l<<32}; setrlimit(RLIMIT_STACK, &rl))
    cout << "Can not set stack size! errno = " << errno << endl;
  else
    cout << "Stack size: " << rl.rlim_cur/(1<<20) << "MiB to " << rl.rlim_max/(1<<20) << "MiB\n";

  array<int8_t, 100'000'000> a;

  cout << (int)a[42] << endl;
}

使用 gcc 编译时会出现段错误,但使用 clang 11.0.1 编译时运行良好并输出:

Stack size: 256MiB to 4096MiB
0

编辑

Clang 正在删除 a 的分配。这是一个更好的例子:

#include <iostream>
#include <array>
#include <sys/resource.h>

using namespace std;

void f() {
  array<int8_t, 100'000'000> a;

  cout << (long)&a[0] << endl;    
}

int main()
{
  if (struct rlimit rl{1<<28, 1l<<32}; setrlimit(RLIMIT_STACK, &rl))
    cout << "Can not set stack size! errno = " << errno << endl;
  else
    cout << "Stack size: " << rl.rlim_cur/(1<<20) << "MiB to " << rl.rlim_max/(1<<20) << "MiB" << endl;

  array<int8_t, 100'000'000> a; // line 21

  cout << (long)&a[0] << endl;  // line 23
  f();
}

您可以在以下位置找到:https://wandbox.org/permlink/XMaGFMa7heWfI9G8。注释掉第 21 行和第 23 行时,它运行良好,否则会出现段错误。

【问题讨论】:

    标签: c++ gcc stack g++ clang++


    【解决方案1】:

    使用proc(5)pmap(1)strace(1) 了解您的计算机的限制。

    array<int8_t, 100'000'000> a;
    

    call stack 上需要大约 100Mbytes 的空间,通常限制在几兆字节(甚至可能通过你的 Linux kernel,但我不确定)

    在您的终端中也可以尝试cat /proc/$$/limits。在我的身上,我得到了

     Limit                     Soft Limit           Hard Limit           Units     
     Max cpu time              unlimited            unlimited            seconds   
     Max file size             unlimited            unlimited            bytes     
     Max data size             unlimited            unlimited            bytes     
     Max stack size            8388608              unlimited            bytes     
    

    编译器之间的行为差​​异可能归因于各种优化(例如,某些 C++ 标准允许,如 n4849)。一个足够聪明的编译器可以在你的函数 f 中只使用几个词来表示 a (例如,因为它可能会通过一些 abstract interpretation 技术找出位置 a[1024] ... a[99999999] 是没用)。

    如果您使用最近 GCC(例如GCC 10)进行编译,您可以将invoke it 作为g++ -O -Wall -Wextra -fanalyzer -Wstack-usage=2048 来获得有用的警告。另请参阅由CHARIOTDECODER 项目资助的this draft 报告。

    在实践中,对大数据使用动态分配(例如 placement newmmap(2)smart pointers

    对于实际应用程序,请考虑编写您的 GCC plugin 以获得临时警告。

    或者至少用g++ -O2 -fverbose-asm -S foo.cc 编译你的源代码foo.cc 并查看生成的foo.s 并用clang++ 重复:生成的汇编文件不同。

    【讨论】:

    • 我的电脑有 32GB RAM,所以 256MB 堆栈没有实际限制。你是说最大堆栈大小受Linux限制,不能通过调用setrlimit(RLIMIT_STACK, ...)来改变?
    • IIRC,最大堆栈大小可能受 Linux 内核限制。考虑重新编译它。
    • 谢谢。这可以解释关于在 Linux 上设置堆栈大小的信息稀缺性。我在 Windows 上使用 256MB 堆栈构建我的应用程序,没有任何问题。我很惊讶setrlimit 在这种情况下具有误导性。
    • 不,只是你的编译器做了优化!顺便说一句,如果我得到报酬,我可以编写这样的 GCC 插件。请随时通过电子邮件与我联系
    • 我测试了超级智能优化器理论,它不成立。请参阅:wandbox.org/permlink/D1nK7zM5zjPHTZF6,其中函数 f 中的大 a 很好,但 main 中的段错误。
    猜你喜欢
    • 2014-09-21
    • 2011-05-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-07
    • 2020-08-25
    • 2018-02-04
    相关资源
    最近更新 更多