Las llamadas fork(2)
se ralentizan a medida que el proceso principal utiliza más memoria debido a la necesidad de copiar tablas de páginas. En muchos usos comunes de fork(), donde va seguido de una de la familia de funciones exec para generar procesos secundarios ( Kernel#system
, IO::popen
, Process::spawn
, etc.), es posible eliminar esta sobrecarga mediante el uso de interfaces de generación de procesos especiales ( posix_spawn()
, vfork()
, etc.)
La biblioteca posix-spawn tiene como objetivo implementar un subconjunto de la Process::spawn
de Ruby 1.9 de una manera que aproveche las interfaces de generación de procesos rápidos cuando estén disponibles y proporcione respaldos sensatos en sistemas que no lo hacen.
Process::spawn
de Ruby 1.9 y versiones mejoradas de Kernel#system
, Kernel#`
, etc. bajo Ruby >= 1.8.7 (actualmente solo MRI).POSIX::Spawn::Child
de alto nivel para escenarios IPC rápidos (¡pero correctos!) sin transmisión. Los siguientes puntos de referencia ilustran el tiempo necesario para bifurcar/ejecutar un proceso secundario con tamaños de memoria residente cada vez mayores en Linux 2.6 y MacOS X. Las pruebas se ejecutaron utilizando el programa posix-spawn-benchmark
incluido con el paquete.
posix_spawn
es más rápido que fork+exec
y se ejecuta en tiempo constante cuando se usa con POSIX_SPAWN_USEVFORK
.
fork+exec
es extremadamente lento para procesos principales grandes.
posix_spawn
es más rápido que fork+exec
, pero ninguno se ve afectado por el tamaño del proceso principal.
Esta biblioteca incluye dos interfaces distintas: POSIX::Spawn::spawn
, una interfaz de generación de procesos de nivel inferior basada en el nuevo método Ruby 1.9 Process::spawn
, y POSIX::Spawn::Child
, una clase de nivel superior orientada a una generación fácil de procesos con manejo de flujo de entrada/salida/error estándar basado en cadenas simples. El primero es mucho más versátil, el segundo requiere mucho menos código para ciertos escenarios comunes.
El módulo POSIX::Spawn
(con la ayuda de la extensión C que lo acompaña) implementa un subconjunto de la interfaz Ruby 1.9 Process::spawn, en gran medida mediante el uso de las interfaces de sistemas IEEE Std 1003.1 posix_spawn(2)
. Estos son ampliamente compatibles con varios sistemas operativos UNIX.
En su forma más simple, el método POSIX::Spawn::spawn
se puede utilizar para ejecutar un proceso hijo similar a Kernel#system
:
require 'posix/spawn'
pid = POSIX::Spawn::spawn('echo', 'hello world')
stat = Process::waitpid(pid)
La primera línea ejecuta echo
con un solo argumento e inmediatamente devuelve el pid
del nuevo proceso. La segunda línea espera a que se complete el proceso y devuelve un objeto Process::Status
. Tenga en cuenta que spawn
no espera a que el proceso finalice la ejecución como system
y no obtiene el estado de salida del niño; debe llamar Process::waitpid
(o equivalente) o el proceso se convertirá en un zombi.
El método spawn
es capaz de realizar una gran cantidad de operaciones adicionales, desde configurar el entorno del nuevo proceso hasta cambiar el directorio de trabajo del niño y redirigir descriptores de archivos arbitrarios.
Consulte la documentación de Ruby 1.9 Process::spawn
para obtener más detalles y la sección STATUS
a continuación para obtener una descripción completa de las diversas funciones Process::spawn
admitidas por POSIX::Spawn::spawn
.
system
, popen4
y `
Además del método spawn
, en el módulo POSIX::Spawn
se proporcionan implementaciones compatibles con Ruby 1.9 de Kernel#system
y Kernel#`
. El método popen4
se puede utilizar para generar un proceso con objetos stdin, stdout y stderr redirigidos.
El módulo POSIX::Spawn
también se puede mezclar con clases y módulos para incluir spawn
y todos los métodos de utilidad en ese espacio de nombres:
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
La clase POSIX::Spawn::Child
incluye lógica para ejecutar procesos secundarios y leer/escribir desde sus flujos de entrada, salida y error estándar. Está diseñado para recibir todas las entradas en una sola cadena y proporciona todas las salidas como cadenas individuales y, por lo tanto, no es adecuado para transmitir grandes cantidades de datos dentro y fuera de los comandos. Dicho esto, tiene algunos beneficios:
select(2)
): maneja todos los casos de bloqueo de tuberías debido a que se exceden los límites PIPE_BUF
en una o más secuencias. POSIX::Spawn::Child
toma los argumentos spawn
estándar cuando se crea una instancia y ejecuta el proceso hasta su finalización después de escribir todas las entradas y leer todas las salidas:
>> require 'posix/spawn'
>> child = POSIX::Spawn::Child.new('git', '--help')
Recupere la salida del proceso escrita en stdout/stderr, o inspeccione el estado de salida del proceso:
>> child.out
=> "usage: git [--version] [--exec-path[=GIT_EXEC_PATH]]n ..."
>> child.err
=> ""
>> child.status
=> #<Process::Status: pid=80718,exited(0)>
Utilice la opción :input
para escribir datos en la entrada estándar del nuevo proceso inmediatamente después de generarse:
>> child = POSIX::Spawn::Child.new('bc', :input => '40 + 2')
>> child.out
"42n"
Se pueden usar opciones adicionales para especificar el tamaño máximo de salida ( :max
) y el tiempo de ejecución ( :timeout
) antes de que se cancele el proceso hijo. Consulte los documentos POSIX::Spawn::Child
para obtener más información.
POSIX::Spawn::Child.new
genera el proceso inmediatamente cuando se crea una instancia. Como resultado, si es interrumpido por una excepción (ya sea por alcanzar el tamaño máximo de salida, el límite de tiempo u otro factor), no es posible acceder a los resultados out
o err
porque el constructor no se completó.
Si desea obtener los datos err
out
estaban disponibles cuando se interrumpió el proceso, use el formulario alternativo POSIX::Spawn::Child.build
para crear el hijo sin generar inmediatamente el proceso. ¡Llame exec!
para ejecutar el comando en un lugar donde pueda detectar cualquier excepción:
>> child = POSIX::Spawn::Child.build('git', 'log', :max => 100)
>> begin
?> child.exec!
?> rescue POSIX::Spawn::MaximumOutputExceeded
?> # limit was reached
?> end
>> child.out
"commit fa54abe139fd045bf6dc1cc259c0f4c06a9285bbn..."
Tenga en cuenta que cuando se genera la excepción MaximumOutputExceeded
, los datos reales combinados out
y err
pueden ser un poco más largos que el valor :max
debido al almacenamiento en búfer interno.
El método POSIX::Spawn::spawn
está diseñado para ser lo más compatible posible con Process::spawn
de Ruby 1.9. En este momento, es un subconjunto compatible.
Estos argumentos Process::spawn
son actualmente compatibles con cualquiera de Spawn::spawn
, Spawn::system
, Spawn::popen4
y 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
Actualmente NO se admiten estas opciones:
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)
La opción :chdir
proporcionada por Posix::Spawn::Child, Posix::Spawn#spawn, Posix::Spawn#system y Posix::Spawn#popen4 no es segura para subprocesos porque los procesos generados con la llamada al sistema posix_spawn(2) hereda el directorio de trabajo del proceso de llamada. La gema posix-spawn soluciona esta limitación en la llamada al sistema cambiando el directorio de trabajo del proceso de llamada inmediatamente antes y después de generar el proceso hijo.
Copyright (c) de Ryan Tomayko y Aman Gupta.
Consulte el archivo COPYING
para obtener más información sobre licencia y redistribución.