2

I've been under the impression that since Unix systems introduced anonymous mmap's, RLIMIT_DATA has been somewhat useless, as mmap address space isn't included in the data segment size. In particular, the glibc malloc() implementation uses anonymous mmap's for large allocations.

However, my testing reveals that at least on somewhat non-ancient Linux systems, anonymous mmap's are included in the data segment. Consider the test program below:


/* Test mmap and ulimit.  */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
#include <string.h>

int print_memstat()
{
    char line[80];
    FILE *f = fopen("/proc/self/status", "r");
    if (!f)
        return -1;
    while (1) {
    if (!fgets(line, 80, f))
        break;
    if (strncmp(line, "VmSize", 6) == 0
        || strncmp(line, "VmRSS", 5) == 0
        || strncmp(line, "VmData", 6) == 0)
        printf("%s", line);
    }
    fclose(f);
    return 0;
}



int main(int argc, char* argv[])
{
    if (argc < 3) {
        printf("Usage: %s MODE SIZE_IN_MiB\nwhere\n\
    MODE=0: Anonymous mmap\n\
    MODE=1: mmap of a temporary file\n\
    MODE=2: malloc\n", argv[0]);
        return 0;
    }
    int mode = atoi(argv[1]);
    long n = atol(argv[2]); // Mem in MiB

    n *= 1024*1024;

    void *p;
    int fd;
    char template[] = "/tmp/mmaptestXXXXXX";

    switch (mode) {
    case 0:
        p = mmap(NULL, n, PROT_READ|PROT_WRITE, 
               MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
        if (p == MAP_FAILED) {
            perror("anonymous mmap failed");
            return 1;
        }
        break;
    case 1:
        fd = mkstemp(template);
        if (fd == -1) {
            perror("mkstemp failed");
            return 1;
        }
        unlink(template);
        if (ftruncate(fd, n) == -1) {
           perror("ftruncate failed");
           return 1;
        }
        p = mmap(NULL, n, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0);
        if (p == MAP_FAILED) {
           perror("mmap failed");
           return 1;
        }
        break;
    case 2:
        p = malloc(n);
        if (!p) {
           perror("malloc failed");
           return 1;
        }
        break;
    default:
        printf("MODE must be 0, 1 or 2\n");
        return 0;
    }
    memset(p, 42, n); // Bump up RSS to approx VSS
    print_memstat();
    return 0;
}

Tested on RHEL5, 6, and Ubuntu 12.04. On all of those, when allocating using anonymous mmap() (MODE=0, and MODE=2 when using glibc malloc()), the data section size (VmData) increases to include the allocation.

EDIT: Some further experimentation shows that while anonymous mmap()'s do increase the reported data section size in the VmData field in /proc/[PID]/status, RLIMIT_DATA limits apply only to the heap size, that is, brk() allocations and not anonymous mmap(). Aargh!

Now my question is, how long as this been the behavior on Linux, and how portable is it to other Unix systems? I.e. do other Unixes also include anonymous mmap's in the data segment size?

janneb
  • 36,249
  • 2
  • 81
  • 97

0 Answers0