进程资源未受setrlimit限制

10

我写了一个简单的程序,限制它的数据大小为65Kb,并验证了这一点。为此,我分配了一个超过65Kb的虚拟内存,如果我所有步骤都正确(如下所示),那么malloc调用应该失败,对吧?

#include <sys/resource.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>

int main (int argc, char *argv[])
{
  struct rlimit limit;


  /* Get max data size . */
  if (getrlimit(RLIMIT_DATA, &limit) != 0) {
    printf("getrlimit() failed with errno=%d\n", errno);
    return 1;
  }

  printf("The soft limit is %lu\n", limit.rlim_cur);
  printf("The hard limit is %lu\n", limit.rlim_max);

  limit.rlim_cur = 65 * 1024;
  limit.rlim_max = 65 * 1024;

  if (setrlimit(RLIMIT_DATA, &limit) != 0) {
    printf("setrlimit() failed with errno=%d\n", errno);
    return 1;
  }

  if (getrlimit(RLIMIT_DATA, &limit) != 0) {
    printf("getrlimit() failed with errno=%d\n", errno);
    return 1;
  }

  printf("The soft limit is %lu\n", limit.rlim_cur);
  printf("The hard limit is %lu\n", limit.rlim_max);
  system("bash -c 'ulimit -a'");
    int *new2 = NULL;
    new2 = malloc(66666666);
    if (new2 == NULL)
    {
        printf("malloc failed\n");
        return;
    }
    else
    {
        printf("success\n");
    }

  return 0;
}

令人惊讶的是,输出结果如下:

The soft limit is 4294967295
The hard limit is 4294967295
The soft limit is 66560
The hard limit is 66560
core file size          (blocks, -c) 0
data seg size           (kbytes, -d) 65
scheduling priority             (-e) 0
file size               (blocks, -f) unlimited
pending signals                 (-i) 14895
max locked memory       (kbytes, -l) 64
max memory size         (kbytes, -m) unlimited
open files                      (-n) 1024
pipe size            (512 bytes, -p) 8
POSIX message queues     (bytes, -q) 819200
real-time priority              (-r) 0
stack size              (kbytes, -s) 8192
cpu time               (seconds, -t) unlimited
max user processes              (-u) 14895
virtual memory          (kbytes, -v) unlimited
file locks                      (-x) unlimited
success

我有任何错误吗? 请提出您的意见。 感谢!

1
在“malloc失败”消息后的裸返回应该被修复为返回1(或某个非零值)。 - Tom Karzes
1
我正在AMD64上运行ubntu linux 14.04,拥有8GB的内存,并使用gcc进行编译,参数为:-Wall -Wextra -pedantic -std-c99。这将导致编译器输出以下三个警告:1)第41行9号:警告:在非void返回函数中没有值的'return';2)未使用的参数argc;3)未使用的参数argv。在提问关于运行时问题时,请始终发布可以清晰编译的代码。 - user3629249
2个回答

7
setrlimit man page 中得知:
RLIMIT_DATA 进程数据段的最大大小(初始化数据,未初始化数据和堆)。这个限制影响到对brk(2) 和sbrk(2) 的调用,在遇到软限制的资源时,这些调用将失败并返回ENOMEM错误。
具体地说,这个资源不适用于通过mmap获得的内存。内部malloc使用各种机制来获取新的内存。在这种情况下,你会发现它使用了mmap而不是sbrk或brk。你可以通过使用strace从你的程序中转储系统调用来验证这一点。
要实现您想要的,请改用RLIMIT_AS资源。

显然有一个Linux内核补丁旨在解决这个问题,但显然它还没有被合并到内核中(至少不是OP正在运行的内核或我正在运行的内核)。以下是该补丁的描述和链接:RLIMIT_DATA patch - Tom Karzes
@kaylum,正是我需要的!在这种情况下,内存确实是由“mmap”分配的。我通过malloc_stats()库调用进行了验证。同样的输出如下所示 - Arena 0: system bytes = 0 in use bytes = 0 Total (incl. mmap): system bytes = 66670592 in use bytes = 66670592 max mmap regions = 1 max mmap bytes = 66670592现在我很想了解有关mmap的信息,是否有任何相关的输入或链接? :) - pa1
@Coder 取决于你想了解关于 mmap 的什么内容。但是如果你还没有阅读过,那么 mmap man page 绝对是开始的地方。 - kaylum

2
在修正代码编译问题后,以下是代码:
#include <sys/resource.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>

int main ( void )
{
  struct rlimit limit;


  /* Get max data size . */
  if (getrlimit(RLIMIT_DATA, &limit) != 0) {
    printf("getrlimit() failed with errno=%d\n", errno);
    exit( EXIT_FAILURE );
  }

  printf("The soft limit is %lu\n", limit.rlim_cur);
  printf("The hard limit is %lu\n", limit.rlim_max);

  limit.rlim_cur = 65 * 1024;
  limit.rlim_max = 65 * 1024;

  if (setrlimit(RLIMIT_DATA, &limit) != 0)
  {
    printf("setrlimit() failed with errno=%d\n", errno);
    exit( EXIT_FAILURE );
  }

  if (getrlimit(RLIMIT_DATA, &limit) != 0)
  {
    printf("getrlimit() failed with errno=%d\n", errno);
    exit( EXIT_FAILURE );
  }

  printf("The soft limit is %lu\n", limit.rlim_cur);
  printf("The hard limit is %lu\n", limit.rlim_max);
  system("bash -c 'ulimit -a'");

    int *new2 = NULL;
    new2 = malloc(66666666);

    if (new2 == NULL)
    {
        printf("malloc failed\n");
        exit( EXIT_FAILURE );
    }
    else
    {
        printf("success\n");
    }

  return 0;
}

这里是输出:

The soft limit is 18446744073709551615
The hard limit is 18446744073709551615
The soft limit is 66560
The hard limit is 66560
bash: xmalloc: .././variables.c:2307: cannot allocate 48 bytes (16384 bytes allocated)
success

这段文字表示对rlimit的修改成功,system调用成功,bash命令失败,malloc成功。同一代码的多次运行总是输出完全相同的值,因此rlimit值没有永久更改。在多个终端窗口中运行上述代码数次后,保留每个终端窗口打开状态,在另一个终端窗口中运行bash命令导致以下结果:
core file size          (blocks, -c) 0
data seg size           (kbytes, -d) unlimited
scheduling priority             (-e) 0
file size               (blocks, -f) unlimited
pending signals                 (-i) 54511
max locked memory       (kbytes, -l) 64
max memory size         (kbytes, -m) unlimited
open files                      (-n) 1024
pipe size            (512 bytes, -p) 8
POSIX message queues     (bytes, -q) 819200
real-time priority              (-r) 0
stack size              (kbytes, -s) 8192
cpu time               (seconds, -t) unlimited
max user processes              (-u) 54511
virtual memory          (kbytes, -v) unlimited
file locks                      (-x) unlimited

在另一个终端中运行代码,然后在同一终端中运行“bash”命令,输出完全相同的输出值。因此,我怀疑该代码在限制可用内存量方面采取了错误的方法。

2
这并没有回答问题,即为什么设置RLIMIT_DATA无法限制数据段大小。如果它起作用的话,malloc将会失败,这也是OP所期望的。 - Tom Karzes
它指出了以下几点:1)更改是短暂的而不是永久的;2)采取了错误的方法;3)由于“虚拟内存”和“内存分页”,实际可用RAM的减少最多会使程序运行变慢,因为可用内存较小会导致更多的“页面故障”事件,但除此之外并没有太大的兴趣;4)malloc不需要分配的内存实际上在RAM中分配,直到/当它实际被使用时,即使这些页面只有在实际访问时才会被访问。 - user3629249
重要的一点是,在当前的Linux内核中,RLIMIT_DATA没有期望的效果,因此OP应该使用RLIMIT_AS。请参见keylum的答案和解释。 - Tom Karzes

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接