c ++ 11 — c ++ system () вызывает ENOMEM

Этот вопрос М (не) мы этого вопрос. Я написал код, который воспроизводит ошибку:

#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<<system(NULL)<<std::endl;
}

Это должно быть скомпилировано с g++ -ggdb -std=c++11 (g ++ 4.9 на Debian). Заметка
что 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) возвращает 1, как и должно быть, когда доступна оболочка.

Почему флаг ENOMEM поднял? Почему код не использует память подкачки? Это ошибка? Есть ли обходной путь? Было бы popen или же 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

0

Решение

system() Функция работает, сначала создав новую копию процесса с fork() или аналогичный (в Linux это заканчивается clone() системный вызов, как вы показываете), а затем, в дочернем процессе, вызов exec создать оболочку, запустив нужную команду.

fork() Вызов может завершиться неудачно, если для нового процесса недостаточно виртуальной памяти (даже если вы намерены немедленно заменить ее на гораздо меньшую площадь, ядро ​​не может этого знать). Некоторые системы позволяют вам обменять возможность форкировать большие процессы для уменьшения гарантий, что сбой страниц может завершиться с копированием при записи (vfork()) или переполнение памяти (/proc/sys/vm/overcommit_memory а также /proc/sys/vm/overcommit_ratio).

Обратите внимание, что вышесказанное в равной степени относится к любой библиотечной функции, которая может создавать новые процессы, например popen(), Хотя и не exec(), как заменяет процесс и не клонировать его.

Если предоставленные механизмы не подходят для вашего варианта использования, вам может потребоваться реализовать свой собственный 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;
}

Вы захотите добавить соответствующие проверки ошибок повсюду, ссылаясь на справочные страницы. Возможно, вы захотите сделать его более объектно-ориентированным и, возможно, использовать iostreams для операций чтения и записи и т. Д.

2

Другие решения

Ваша линия

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

вероятно неправильно Вы вызываете конструктор для станд :: вектор который принимает размер вектора и инициализирующий элемент. 7e8 преобразуется в огромный размер (то есть в 700000000 элементов).

Возможно, вы захотите построить двухэлементный вектор, поэтому используйте

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

И с вашим огромным вектором, Система (3) вызовет библиотечную функцию вилка (2) системный вызов, который терпит неудачу с:

ENOMEM fork() не удалось выделить необходимые структуры ядра
потому что память тесна.

Может быть, вы достигли какого-то предела, например установить с помощью setrlimit (2) где-нибудь еще.
Пытаться cat /proc/self/limits найти их (в Linux).

использование Трассирование (1) (например, как strace -f yourprogram) выяснить, что происходит; осмотреться fork или же clone линия…

КСТАТИ, Система (3) должен вернуть код ошибки при сбое. Вы должны проверить это. И вы можете позвонить system("echo here pid $$"); вместо system(NULL);

0

По вопросам рекламы [email protected]