Вызовы fork(2)
замедляются, поскольку родительский процесс использует больше памяти из-за необходимости копирования таблиц страниц. Во многих распространенных случаях использования fork(), где за ним следует одна из функций семейства exec для создания дочерних процессов ( Kernel#system
, IO::popen
, Process::spawn
и т. д.), можно удалить эти накладные расходы. используя специальные интерфейсы создания процессов ( posix_spawn()
, vfork()
и т. д.)
Библиотека posix-spawn призвана реализовать подмножество интерфейса Ruby 1.9 Process::spawn
таким образом, чтобы использовать преимущества интерфейсов быстрого создания процессов, когда они доступны, и обеспечивать разумные резервные варианты для систем, которые этого не делают.
Process::spawn
Ruby 1.9 и расширенные версии Kernel#system
, Kernel#`
и т. д. для Ruby >= 1.8.7 (в настоящее время только MRI).POSIX::Spawn::Child
для быстрых (но правильных!) сценариев IPC без потоковой передачи. Следующие тесты иллюстрируют время, необходимое для разветвления/выполнения дочернего процесса при увеличении размера резидентной памяти в Linux 2.6 и MacOS X. Тесты проводились с использованием программы posix-spawn-benchmark
включенной в пакет.
posix_spawn
быстрее, чем fork+exec
, и выполняется за постоянное время при использовании с POSIX_SPAWN_USEVFORK
.
fork+exec
работает очень медленно для больших родительских процессов.
posix_spawn
работает быстрее, чем fork+exec
, но на него не влияет размер родительского процесса.
Эта библиотека включает в себя два различных интерфейса: POSIX::Spawn::spawn
, интерфейс создания процессов более низкого уровня, основанный на новом методе Ruby 1.9 Process::spawn
, и POSIX::Spawn::Child
, класс более высокого уровня, предназначенный для простого создания процессов. процессов с простой обработкой стандартного потока ввода/вывода/ошибок на основе строк. Первый гораздо более универсален, второй требует гораздо меньше кода для некоторых распространенных сценариев.
Модуль POSIX::Spawn
(с помощью сопутствующего расширения C) реализует подмножество интерфейса Ruby 1.9 Process::spawn, в основном за счет использования системных интерфейсов IEEE Std 1003.1 posix_spawn(2)
. Они широко поддерживаются различными операционными системами UNIX.
В своей простейшей форме метод POSIX::Spawn::spawn
можно использовать для выполнения дочернего процесса, аналогичного Kernel#system
:
require 'posix/spawn'
pid = POSIX::Spawn::spawn('echo', 'hello world')
stat = Process::waitpid(pid)
Первая строка выполняет echo
с одним аргументом и немедленно возвращает pid
нового процесса. Вторая строка ожидает завершения процесса и возвращает объект Process::Status
. Обратите внимание, что spawn
не ждет завершения выполнения процесса, как system
, и не получает статус выхода дочернего процесса - вы должны вызвать Process::waitpid
(или его эквивалент), иначе процесс станет зомби.
Метод spawn
способен выполнять большое количество дополнительных операций: от настройки среды нового процесса до изменения рабочего каталога дочернего процесса и перенаправления произвольных файловых дескрипторов.
Подробности смотрите в документации Ruby 1.9 Process::spawn
, а в разделе STATUS
ниже — полный отчет о различных функциях Process::spawn
, поддерживаемых POSIX::Spawn::spawn
.
system
, popen4
и `
В дополнение к методу spawn
в модуле POSIX::Spawn
предусмотрены совместимые с Ruby 1.9 реализации Kernel#system
и Kernel#`
. Метод popen4
можно использовать для создания процесса с перенаправленными объектами stdin, stdout и stderr.
Модуль POSIX::Spawn
также можно смешивать с классами и модулями, чтобы включить spawn
и все служебные методы в это пространство имен:
require 'posix/spawn'
class YourGreatClass
include POSIX::Spawn
def speak(message)
pid = spawn('echo', message)
Process::waitpid(pid)
end
def calculate(expression)
pid, in, out, err = popen4('bc')
in.write(expression)
in.close
out.read
ensure
[in, out, err].each { |io| io.close if !io.closed? }
Process::waitpid(pid)
end
end
POSIX::Spawn::Child
включает логику для выполнения дочерних процессов и чтения/записи из их стандартных потоков ввода, вывода и ошибок. Он предназначен для приема всех входных данных в одной строке и обеспечивает весь вывод в виде отдельных строк и поэтому не очень подходит для потоковой передачи больших объемов данных в команды и из них. Тем не менее, у него есть некоторые преимущества:
select(2)
) — обрабатывает все случаи зависания канала из-за превышения ограничений PIPE_BUF
для одного или нескольких потоков. POSIX::Spawn::Child
принимает стандартные аргументы spawn
при создании экземпляра и запускает процесс до завершения после записи всех входных данных и чтения всех выходных данных:
>> require 'posix/spawn'
>> child = POSIX::Spawn::Child.new('git', '--help')
Получите выходные данные процесса, записанные в stdout/stderr, или проверьте статус завершения процесса:
>> child.out
=> "usage: git [--version] [--exec-path[=GIT_EXEC_PATH]]n ..."
>> child.err
=> ""
>> child.status
=> #<Process::Status: pid=80718,exited(0)>
Используйте опцию :input
для записи данных в стандартный ввод нового процесса сразу после создания:
>> child = POSIX::Spawn::Child.new('bc', :input => '40 + 2')
>> child.out
"42n"
Дополнительные параметры можно использовать для указания максимального размера вывода ( :max
) и времени выполнения ( :timeout
) перед тем, как дочерний процесс будет прерван. Дополнительную информацию см. в документации POSIX::Spawn::Child
.
POSIX::Spawn::Child.new
запускает процесс сразу после создания экземпляра. В результате, если он прерывается исключением (либо из-за достижения максимального выходного размера, либо из-за ограничения по времени, либо из-за другого фактора), невозможно получить доступ к результатам out
или err
, поскольку конструктор не завершился.
Если вы хотите получить данные err
и out
, когда процесс был прерван, используйте альтернативную форму POSIX::Spawn::Child.build
чтобы создать дочерний процесс без немедленного запуска процесса. Позвоните exec!
чтобы запустить команду в месте, где вы можете перехватить любые исключения:
>> child = POSIX::Spawn::Child.build('git', 'log', :max => 100)
>> begin
?> child.exec!
?> rescue POSIX::Spawn::MaximumOutputExceeded
?> # limit was reached
?> end
>> child.out
"commit fa54abe139fd045bf6dc1cc259c0f4c06a9285bbn..."
Обратите внимание, что при возникновении исключения MaximumOutputExceeded
фактические объединенные out
данные и данные err
могут быть немного длиннее, чем значение :max
из-за внутренней буферизации.
Метод POSIX::Spawn::spawn
разработан так, чтобы быть максимально совместимым с Process::spawn
в Ruby 1.9. На данный момент это совместимое подмножество.
Эти аргументы Process::spawn
в настоящее время поддерживаются любым из Spawn::spawn
, Spawn::system
, Spawn::popen4
и Spawn::Child.new
:
env: hash
name => val : set the environment variable
name => nil : unset the environment variable
command...:
commandline : command line string which is passed to a shell
cmdname, arg1, ... : command name and one or more arguments (no shell)
[cmdname, argv0], arg1, ... : command name, argv[0] and zero or more arguments (no shell)
options: hash
clearing environment variables:
:unsetenv_others => true : clear environment variables except specified by env
:unsetenv_others => false : don't clear (default)
current directory:
:chdir => str : Not thread-safe when using posix_spawn (see below)
process group:
:pgroup => true or 0 : make a new process group
:pgroup => pgid : join to specified process group
:pgroup => nil : don't change the process group (default)
redirection:
key:
FD : single file descriptor in child process
[FD, FD, ...] : multiple file descriptor in child process
value:
FD : redirect to the file descriptor in parent process
:close : close the file descriptor in child process
string : redirect to file with open(string, "r" or "w")
[string] : redirect to file with open(string, File::RDONLY)
[string, open_mode] : redirect to file with open(string, open_mode, 0644)
[string, open_mode, perm] : redirect to file with open(string, open_mode, perm)
FD is one of follows
:in : the file descriptor 0 which is the standard input
:out : the file descriptor 1 which is the standard output
:err : the file descriptor 2 which is the standard error
integer : the file descriptor of specified the integer
io : the file descriptor specified as io.fileno
Эти параметры в настоящее время НЕ поддерживаются:
options: hash
resource limit: resourcename is core, cpu, data, etc. See Process.setrlimit.
:rlimit_resourcename => limit
:rlimit_resourcename => [cur_limit, max_limit]
umask:
:umask => int
redirection:
value:
[:child, FD] : redirect to the redirected file descriptor
file descriptor inheritance: close non-redirected non-standard fds (3, 4, 5, ...) or not
:close_others => false : inherit fds (default for system and exec)
:close_others => true : don't inherit (default for spawn and IO.popen)
Опция :chdir
, предоставляемая Posix::Spawn::Child, Posix::Spawn#spawn, Posix::Spawn#system и Posix::Spawn#popen4, не является потокобезопасной, поскольку процессы создаются с помощью системного вызова posix_spawn(2). наследовать рабочий каталог вызывающего процесса. Гем posix-spawn обходит это ограничение в системном вызове, изменяя рабочий каталог вызывающего процесса непосредственно до и после создания дочернего процесса.
Авторские права (c) принадлежат Райану Томайко и Аману Гупте.
Дополнительную информацию о лицензии и распространении см. в файле COPYING
.