c++ system() raises ENOMEM

后端 未结 2 729
礼貌的吻别
礼貌的吻别 2021-01-28 23:27

This question is a M(not)WE of this question. I wrote a code that reproduces the error:

#include 
#include 
#include 

        
相关标签:
2条回答
  • 2021-01-28 23:38

    The system() function works by first creating a new copy of the process with fork() or similar (in Linux, this ends up in the clone() system call, as you show) and then, in the child process, calling exec to create a shell running the desired command.

    The fork() call can fail if there is insufficient virtual memory for the new process (even though you intend to immediately replace it with a much smaller footprint, the kernel can't know that). Some systems allow you to trade the ability to fork large processes for reduced guarantees that page faults may fail, with copy-on-write (vfork()) or memory overcommit (/proc/sys/vm/overcommit_memory and /proc/sys/vm/overcommit_ratio).

    Note that the above applies equally to any library function that may create new processes - e.g. popen(). Though not exec(), as that replaces the process and doesn't clone it.

    If the provided mechanisms are inadequate for your use case, then you may need to implement your own system() replacement. I recommend starting a child process early on (before you allocate lots of memory) whose sole job is to accept NUL-separated command lines on stdin and report exit status on stdout.

    An outline of the latter solution in pseudo-code looks something like:

    int request_fd[2];
    int reply_fd[2];
    
    pipe(request_fd);
    pipe(reply_fd);
    
    if (fork()) {
        /* in parent */
        close(request_fd[0]);
        close(reply_fd[1]);
    } else {
        /* in child */
        close(request_fd[1]);
        close(reply_fd[0]);
        while (read(request_fd[0], command)) {
            int result = system(command);
            write(reply_fd[1], result);
        }
        exit();
    }
    
    // Important: don't allocate until after the fork()
    std::vector<double> a(7e8,1);  // allocate a big chunk of memory
    
    int my_system_replacement(const char* command) {
        write(request_fd[1], command);
        read(reply_fd[0], result);
        return result;
    }
    

    You'll want to add appropriate error checks throughout, by reference to the man pages. And you might want to make it more object-oriented, and perhaps use iostreams for your read and write operations, etc.

    0 讨论(0)
  • 2021-01-28 23:50

    Your line

       std::vector<double> a(7e8,1);
    

    is probably wrong. You are calling a constructor for std::vector which takes a vector size and an initializing element. The 7e8 is converted to a huge size (i.e. to 700000000 elements).

    You might want to construct a two-element vector, so use

       std::vector<double> a{7e8,1};
    

    And with your huge vector, system(3) library function will call fork(2) system call which is failing with:

    ENOMEM fork() failed to allocate the necessary kernel structures because memory is tight.

    Maybe you reached some limit, e.g. set by setrlimit(2) somewhere else.
    Try cat /proc/self/limits to find them (on Linux).

    Use strace(1) (e.g. as strace -f yourprogram) to find out what is happening; look around the fork or clone line...

    BTW, system(3) should return an error code on failure. You should test it. And you might want to call system("echo here pid $$"); instead of system(NULL);

    0 讨论(0)
提交回复
热议问题