【问题标题】:"setrlimit()" is not affecting the running process“setrlimit()”不影响正在运行的进程
【发布时间】:2019-07-17 11:33:48
【问题描述】:

我正在尝试模拟 Linux 中进程的错误场景,即堆不足以在 C++ Linux 应用程序中分配内存。 但是即使我使用“setrlimit”来减少进程可用的堆内存,堆内存仍然被成功分配。

struct rlimit the_limit = { 1, 1 };
if (-1 == setrlimit(RLIMIT_DATA, &the_limit)) {
    perror("setrlimit failed");
}

try
{
   char *n = new char[5600];

   if (n==NULL)
   {
      cout <<"\nAllocation Failure\n";
   }
}
catch (std::bad_alloc& ba)
{
   std::cerr << "bad_alloc caught: " << ba.what() << '\n';
}

【问题讨论】:

  • Linux 很乐意overcommit(也就是说,允许程序分配超过可用资源)。这是因为内存不会映射到进程,除非它们被实际使用(读取或写入),并且多个进程同时需要它们的所有页面是不常见的。
  • 另外,RLIMIT_DATA 并不代表您认为的意思,这在副本中有解释。
  • @Someprogrammerdude 这可能不是原因。在这种情况下,我认为问题更可能与使用 C++ 的代码有关。请问可以重新打开吗?
  • @PSkocik new 将使用 malloc (在相对现代的 Linux 上)将使用 mmap。并且RLIMIT_DATA 不会更改mmap 使用的限制,如副本中所述。
  • @Someprogrammerdude setrlimit 手册页说 RLIMIT_DATA 确实适用于自 Linux 4.7 以来的 mmap。 C++ 示例的问题似乎是 C++ 标准库在启动时预分配了一些堆,然后使用预分配的堆来满足小的 5600B 请求。增加请求大小(在我的系统上,至少为 1setrlimit 问题与 C 对应的问题不同。

标签: c++ linux heap-memory


【解决方案1】:

大多数 C++ 标准库,包括随 g++ 提供的库,都是从预先分配的一些堆内存开始的。 5600 是一个小请求,因此,在我的 Linux 系统上,它可以从预分配的内存中得到满足 来自strace

修改示例:

#include <stdio.h>
#include <sys/resource.h>
int main()
{
    struct rlimit the_limit = { 1, 1 };
    if (-1 == setrlimit(RLIMIT_DATA, &the_limit)) { perror("setrlimit failed"); }

    puts("ALLOC");
    #if __cplusplus
    try { char *n = new char[5600]; } catch (...) { perror("alloc failure"); }
    #else
    { char *n = malloc(1); if(!n) perror("alloc failure"); }
    #endif
}

示例strace结束:

...
write(1, "ALLOC\n", 6ALLOC
)                  = 6
exit_group(0)                           = ?

要么增加请求大小,例如在我的情况下,至少 1&lt;&lt;16 或切换到纯 C 会导致从操作系统提供分配请求,然后限制确实适用:

1&lt;&lt;16 分配请求结束 strace:

write(1, "ALLOC\n", 6ALLOC
)                  = 6
brk(0x561bcc5d4000)                     = 0x561bcc5b2000
mmap(NULL, 1048576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = -1 ENOMEM (Cannot allocate memory)
dup(2)                                  = 3
fcntl(3, F_GETFL)                       = 0x2 (flags O_RDWR)
fstat(3, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 14), ...}) = 0
write(3, "alloc failure: Cannot allocate m"..., 38alloc failure: Cannot allocate memory
) = 38
close(3)                                = 0
exit_group(0)                           = ?

请注意,通用分配器实现通常使用sbrk 和/或 mmap 直接从操作系统获取内存,正如您可以从 setrlimit 手册页中收集到的那样,RLIMIT_DATA 仅适用于 mmap 支持的分配,如果您使用的是 Linux >= 4.7。

【讨论】:

  • 非常感谢您的解释,对我的理解帮助很大。
  • @PichumaniChellappa 没问题。你不需要在这里感谢。 ✓-ing 和 ⇑-ing 答案就可以了 :)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-05-29
  • 2013-09-10
  • 1970-01-01
  • 1970-01-01
  • 2020-03-27
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多