C++ system() 引发 ENOMEM

2023-12-13

这个问题是一个M(not)WE这个的question。我写了一段代码来重现该错误:

#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被设定为ENOMEM在第 9 行,对应于system()称呼。请注意,如果向量的尺寸较小(我猜它 取决于您将运行代码的计算机),代码工作正常并且system(NULL)当 shell 可用时返回 1。

为什么是旗帜ENOMEM上调?为什么代码不使用交换内存?这是一个错误吗?有解决方法吗?会popen or exec*照着做? (我知道,每个帖子我应该只问一个问题,但所有这些问题都可以总结为“发生了什么事?”)

根据要求,这是结果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

这是 free 的输出:

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

The system()函数的工作原理是首先创建进程的新副本fork()或类似的(在 Linux 中,这最终出现在clone()系统调用,如您所示),然后在子进程中调用exec创建一个运行所需命令的 shell。

The fork()如果新进程没有足够的虚拟内存,调用可能会失败(即使您打算立即用更小的占用空间替换它,内核也无法知道这一点)。某些系统允许您用分叉大型进程的能力来换取减少页面错误可能失败的保证,并使用写时复制(vfork())或内存过量使用(/proc/sys/vm/overcommit_memory and /proc/sys/vm/overcommit_ratio).

请注意,上述内容同样适用于可能创建新进程的任何库函数 - 例如popen()。虽然不是exec(),这样replaces该过程并且不会克隆它。

如果提供的机制不足以满足您的用例,那么您可能需要实现自己的system()替代品。我建议尽早启动一个子进程(在分配大量内存之前),其唯一的工作是接受NUL- 分隔命令行stdin并报告退出状态stdout.

后一种解决方案的伪代码概述如下:

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

您需要参考手册页在整个过程中添加适当的错误检查。您可能希望使其更加面向对象,并且可能使用 iostream 进行读写操作等。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++ system() 引发 ENOMEM 的相关文章

随机推荐

  • 如何在 Touch UI 中添加强制下拉字段

    我将 必需 添加为 真实 但它不起作用 required 为 true 仅适用于文本字段 根据下面的文档 我没有看到任何从下拉列表中添加必填字段的选项 http docs adobe com docs en aem 6 0 author a
  • 为什么 Promise.then 'onFulfilled' 函数在 setTimeout 回调之前触发? [复制]

    这个问题在这里已经有答案了 有人可以解释为什么 Promise then 函数在 setTimeout 函数之前触发吗 我本以为 setTimeout 函数会首先安排在事件循环上 所以它会在 Promise then 函数之前运行 setT
  • 运算符如何重载|专门实现了 C++ 范围适配器?

    自从范围被合并到 C 20 中以来 我一直在查看标题以了解如何operator 对于范围视图来说超载 但我找不到关于如何或在何处实现它的正确轨道 C R 相当于R C根据https en cppreference com w cpp ran
  • Makefile - 移动目标文件

    经过一番搜索后 我成功地将以下 Makefile 放在一起 CC gcc CFLAGS c Wall LDFLAGS SOURCEFILES main c SOURCES src SOURCEFILES OBJECTS SOURCES c
  • Codeigniter 2.1.3 的 SMTP Gmail 错误

    我看过很多与此问题相关的帖子 我已经完成了给出的说明 但总是遇到相同的错误 我想使用 Code Igniter 2 1 3 发送 smtp gmail 这是代码 class Email extends CI Controller funct
  • 使用 SSIS 压缩文件夹

    我正在尝试压缩 SSIS 中的一个文件夹 源文件夹中有 12 个文件 我需要压缩该文件夹 我可以很好地压缩文件 我的问题是文件夹 我必须使用 winzip 来创建压缩包 谁能给我指点一个好的教程 我无法实现我找到的任何示例 Thanks 添
  • 将字典拆分为 df 中的各个列[重复]

    这个问题在这里已经有答案了 我有以下内容df其值是字典 tests SO4 Mon 6 Tues 6 Wed 7 CH3 Mon 0 Tues 8 Wed 10 我想获得所需的输出 0 1 2 SO4 Mon 6 Tues 6 Wed 7
  • Android:如何根据首先选择的单选按钮启用/禁用复选框

    基本上我有一个带有两个单选按钮的单选组 其中一个标记为 运行 另一个标记为 通过 就在其下方 我还有一个标记为 通过完成 的复选框 问题 如何在选择 运行 单选按钮时禁用该复选框 因此无法选择该复选框 并在选择 通过 单选按钮时启用它 任何
  • 无法将数组项传递给 PhantomJS 中的函数

    我正在尝试将源代码同时提取到多个网页 这些链接通过源文本文件输入到数组中 我能够迭代数组并打印出链接并确认它们存在 但是当尝试通过函数传递它们时 它们在第一次迭代后变得未定义 我的最终目标是将每个页面的源代码保存到自己的文档中 它正确执行第
  • 将打印按钮添加到 Inno Setup 中的许可证页面(Inno Setup 6 重新访问)

    这是一个标准 RTF 文档 用于LicenseFile财产在 Setup Inno 设置部分 是否可以在此页面添加一个打印按钮来触发打印许可协议 我看到了类似的问题和答案 在 Inno Setup 的许可页面中添加 打印协议 按钮 我刚刚尝
  • 将 Trace 方法添加到 System.Diagnostics.TraceListener

    我编写了一个从 System Diagnostics TraceListener 派生的 Log 类 如下所示 public class Log TraceListener 它充当 Log4Net 的包装器 并允许人们像这样使用 Syste
  • 如何在 bigquery 中取消嵌套结构数组的字符串?

    bigquery 方法 当 option name 为 labels 时 INFORMATION SCHEMA TABLE OPTIONS 返回 option value 的结构数组字符串 更多信息在这里 表选项例如 我创建表 CREATE
  • 如何告诉 Google 翻译不要翻译网站的某个部分?

    谷歌翻译有一个开发者工具这将在网站上启用谷歌翻译 有没有办法告诉谷歌翻译不要翻译网站的某个部分 也许在 HTML 元素上有一个类名 我已经尝试过HTML5 翻译 否属性 它没有任何作用 这是一个特殊的问题 因为谷歌错误翻译了网站的名称 根据
  • Asp.Net C# MVC 动态表单(更改 Dom 结构并获取服务器上的数据)

    我使用 JavaScript 动态更改客户端的 DOM 以添加一些新的输入字段 不使用Ajax可以在服务器端获取数据吗 只需按下发送按钮即可在我的控制器中获取新数据 答案就在这里 http habrahabr ru blogs aspnet
  • Spring Data Rest 中嵌套资源的分页

    当访问以下 URL 时 我会收到分页响应 api userPosts links self href api userPosts page size sort templated true next href api userPosts p
  • 如何使用 jQuery 在元素的属性上插入变量?

    我只是想问是否可以在属性上插入 jQuery 变量 这是我的示例代码 a href Click here a 我需要输入 href 的 jQuery var 值 我怎样才能做到这一点 先感谢您 Sure function var u
  • 显示所选父级的树形菜单

    我在这里构建一个树形菜单 类别可通过其 id 进行访问 并显示其子类别 我的函数对于两级菜单运行良好 但无法生成第三级菜单 仅当单击第二级项目的 id 时 才应显示第三级菜单 我也需要这个函数来跟踪节点的路径 如果给出第三级 id 那么树应
  • 更改 uitableviewcell 的宽度

    我尝试更改表格视图中单元格的 with 我不做自定义单元格 我只是子类化 uitableviewcell 这是班级 implementation customCell id initWithStyle UITableViewCellStyl
  • 用 Python 计算费德勒向量

    如何在 Python 中找到拉普拉斯算子 L 的场向量 我可以使用以下方法获取特征值和特征向量 特征值 特征向量 linalg eig L 我假设 python 不会按顺序返回特征值 我是否取第二大特征值 然后将其与相应的特征向量匹配 在索
  • C++ system() 引发 ENOMEM

    这个问题是一个M not WE这个的question 我写了一段代码来重现该错误 include