时间:2019-03-08 标签:c++system()raisesENOMEM

标签 c++ c++11 gdb errno

这个问题是M(not)WEquestion .我写了一个重现错误的代码:

#include <cstdlib>
#include <iostream>
#include <vector>

int *watch_errno = __errno_location();

int main(){
    std::vector<double> a(7e8,1);  // allocate a big chunk of memory
    std::cout<<std::system(NULL)<<std::endl;
}

它必须使用 g++ -ggdb -std=c++11(Debian 上的 g++ 4.9)编译。笔记 int *watch_errno 仅对允许 gdb 监视 errno 有用。

当它在 gdb 下运行时,我得到这个:

(gdb) watch *watch_errno 
Hardware watchpoint 1: *watch_errno
(gdb) r
Starting program: /tmp/bug 
Hardware watchpoint 1: *watch_errno

Old value = <unreadable>
New value = 0
__static_initialization_and_destruction_0 (__initialize_p=1, __priority=65535) at bug.cpp:10
10      }
(gdb) c
Continuing.
Hardware watchpoint 1: *watch_errno

Old value = 0
New value = 12
0x00007ffff7252421 in do_system (line=line@entry=0x7ffff7372168 "exit 0") at ../sysdeps/posix/system.c:116
116     ../sysdeps/posix/system.c: No such file or directory.
(gdb) bt
#0  0x00007ffff7252421 in do_system (line=line@entry=0x7ffff7372168 "exit 0") at ../sysdeps/posix/system.c:116
#1  0x00007ffff7252510 in __libc_system (line=<optimized out>) at ../sysdeps/posix/system.c:182
#2  0x0000000000400ad8 in main () at bug.cpp:9
(gdb) l
111     in ../sysdeps/posix/system.c
(gdb) c
Continuing.
0
[Inferior 1 (process 5210) exited normally]

由于某种原因,errno 在第 9 行被设置为 ENOMEM,它对应于 system() 调用。请注意,如果 vector 的尺寸较小(我猜它 取决于您将运行代码的计算机),代码工作正常并且 system(NULL) 在 shell 可用时返回 1。

为什么标记 ENOMEM 被升起?为什么代码不使用交换内存?这是一个错误吗?有解决方法吗? popenexec* 会做同样的事情吗? (我知道,每个帖子我应该只问一个问题,但所有这些问题都可以总结为“发生了什么事?”)

根据要求,这是 ulimit -a 的结果:

-t: cpu time (seconds)              unlimited
-f: file size (blocks)              unlimited
-d: data seg size (kbytes)          unlimited
-s: stack size (kbytes)             8192
-c: core file size (blocks)         0
-m: resident set size (kbytes)      unlimited
-u: processes                       30852
-n: file descriptors                65536
-l: locked-in-memory size (kbytes)  64
-v: address space (kbytes)          unlimited
-x: file locks                      unlimited
-i: pending signals                 30852
-q: bytes in POSIX msg queues       819200
-e: max nice                        0
-r: max rt priority                 0
-N 15:                              unlimited

这里是 strace -f myprog 的相关部分

mmap(NULL, 5600002048, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7faa98562000
rt_sigaction(SIGINT, {SIG_IGN, [], SA_RESTORER, 0x7fabe622b180}, {SIG_DFL, [], 0}, 8) = 0
rt_sigaction(SIGQUIT, {SIG_IGN, [], SA_RESTORER, 0x7fabe622b180}, {SIG_DFL, [], 0}, 8) = 0
rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
clone(child_stack=0, flags=CLONE_PARENT_SETTID|SIGCHLD, parent_tidptr=0x7fff8797635c) = -1 ENOMEM (Cannot allocate memory)
rt_sigaction(SIGINT, {SIG_DFL, [], SA_RESTORER, 0x7fabe622b180}, NULL, 8) = 0
rt_sigaction(SIGQUIT, {SIG_DFL, [], SA_RESTORER, 0x7fabe622b180}, NULL, 8) = 0
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 1), ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fabe6fde000
write(1, "0\n", 20
)                      = 2
write(1, "8\n", 28
)                      = 2
munmap(0x7faa98562000, 5600002048)      = 0

这里是免费的输出:

           total       used       free     shared    buffers     cached
Mem:       7915060    1668928    6246132      49576      34668    1135612
-/+ buffers/cache:     498648    7416412
Swap:      2928636          0    2928636

最佳答案

system() 函数首先使用 fork() 或类似的方法创建进程的新拷贝(在 Linux 中,这最终在 clone() 系统调用,如您所示),然后在子进程中调用 exec 以创建运行所需命令的 shell。

如果新进程的虚拟内存不足,fork() 调用可能会失败(即使您打算立即用更小的占用空间替换它,内核也不知道) .某些系统允许您使用写时复制 (vfork()) 或内存过量使用 (/proc/sys/vm/overcommit_memory/proc/sys/vm/overcommit_ratio)。

请注意,以上内容同样适用于任何可能创建新进程的库函数 - 例如popen()。虽然不是 exec(),因为它替换进程而不是克隆它。

如果提供的机制不适合您的用例,那么您可能需要实现自己的 system() 替换。我建议尽早启动子进程(在分配大量内存之前),其唯一工作是在 stdin 上接受 NUL 分隔的命令行并在 上报告退出状态>标准输出

后一种解决方案的伪代码大纲如下所示:

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;
}

您需要通过引用手册页在整个过程中添加适当的错误检查。你可能想让它更面向对象,并且可能使用 iostreams 进行读写操作等。

关于时间:2019-03-08 标签:c++system()raisesENOMEM,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32892908/

相关文章:

c++ - 为什么我从继承自 QAbstractListModel 的类中得到 "use of delete function"?

c++ - ASIO UDP:类 std::allocator<void> 没有名为 ‘construct’ 的成员

c++ - 创建 shared_ptr 似乎会导致段错误

ruby - 如何在运行时调试我的 Rack 应用程序?

C++:新放置与自己的新重载发生冲突

c++ - 用mingw和msys把VC++库转成mingw库?

c++ - 如何使用 COMMTIMEOUTS 等待直到字节可用但读取了一个以上的字节?

c++ - 在之前使用 emplace 而不是构造对象

c++ bad_alloc,但它在 gdb 中消失了

c++ - C/C++ 中的正向和反向 Gabor 变换库