Мне нужно разблокировать exec с сервера. Поскольку мой размер памяти для печати на сервере большой, я намерен использовать vfork()
/ linux clone()
. Мне также нужно открыть трубы для stdin
/ stdout
/ stderr
. Разрешено ли это с помощью clone()
/ vfork()
?
Из стандарта:
[..] поведение не определено, если процесс, созданный
vfork()
либо изменяет любые данные, отличные от переменной типаpid_t
используемые для хранения возвращаемого значения изvfork()
, либо возвращает из функции, в которой был вызванvfork()
, или вызывает любую другую функцию перед успешным вызовом функции_exit()
или одного из семейств функцийexec
.
Проблема с вызывающими функциями, такими как setuid
или pipe
заключается в том, что они могут влиять на память в адресном пространстве, разделяемом между родительским и дочерним процессами. Если вам нужно что-то сделать до exec
, лучший способ – написать небольшой процесс прокладки, который сделает все, что вам нужно, а затем exec
s для последующего дочернего процесса (возможно, аргументы, предоставленные через argv
).
shim.c ====== enum { /* initial arguments */ ARGV_FILE = 5, ARGV_ARGS }; int main(int argc, char *argv[]) { /* consume instructions from argv */ /* setuid, pipe() etc. */ return execvp(argv[ARGV_FILE], argv + ARGV_ARGS); }
Вместо этого я использовал бы clone()
, используя CLONE_VFORK|CLONE_VM
; см. man 2 clone для деталей.
Поскольку CLONE_FILES
не установлен, дочерний процесс имеет свои собственные файловые дескрипторы и может закрывать и открывать стандартные дескрипторы, не затрагивая вообще родителя.
Поскольку клонированный процесс представляет собой отдельный процесс, он имеет свои собственные идентификаторы пользователей и групп, поэтому устанавливая их через setresgid()
и setresuid()
(возможно, setgroups()
или initgroups()
чтобы установить дополнительные группы – см. Man 2 setresuid , man 2 setgroups и man 3 initgroups для деталей) никак не повлияет на родителя.
CLONE_VFORK|CLONE_VM
означают, что clone()
должен вести себя как vfork()
, при этом дочерний процесс работает в том же пространстве памяти, что и родительский процесс, до вызова execve()
.
Такой подход позволяет избежать задержек при использовании промежуточного исполняемого файла – это довольно важно – но подход полностью зависит от Linux.