fork(2)
calls slow down as the parent process uses more memory due to the need
to copy page tables. In many common uses of fork(), where it is followed by one
of the exec family of functions to spawn child processes (Kernel#system
,
IO::popen
, Process::spawn
, etc.), it's possible to remove this overhead by using
the use of special process spawning interfaces (posix_spawn()
, vfork()
, etc.)
The posix-spawn library aims to implement a subset of the Ruby 1.9 Process::spawn
interface in a way that takes advantage of fast process spawning interfaces when
available and provides sane fallbacks on systems that do not.
- Fast, constant-time spawn times across a variety of platforms.
- A largish compatible subset of Ruby 1.9's
Process::spawn
interface under Ruby >= 1.8.7. - High level
POSIX::Spawn::Child
class for quick (but correct!) non-streaming IPC cases.
The following benchmarks illustrate time needed to fork/exec a child process at
increasing resident memory sizes on Linux 2.6 and MacOS X. Tests were run using
the posix-spawn-benchmark
program included with the package.
posix_spawn
is faster than fork+exec
, and executes in constant time when
used with POSIX_SPAWN_USEVFORK
.
fork+exec
is extremely slow for large parent processes.
posix_spawn
is faster than fork+exec
, but neither is affected by the size of
the parent process.
This library includes two distinct interfaces: POSIX::Spawn::spawn
, a lower
level process spawning interface based on the Process::spawn
include in Ruby
1.9, and POSIX::Spawn::Child
, a higher level class geared toward easy spawning
of processes with simple string based standard input/output/error stream
handling. The former is much more versatile, the latter requires much less
code for certain common scenarios.
The POSIX::Spawn
module (with help from the accompanying C extension)
implements a subset of the Ruby 1.9 Process::spawn interface, largely
through the use of the POSIX standard posix_spawn
family of C functions.
These are widely supported by various UNIX operating systems.
In its simplest form, the POSIX::Spawn::spawn
method can be used to execute a
child process similar to Kernel#system
:
pid = POSIX::Spawn::spawn('echo', 'hello world')
stat = Process::waitpid(pid)
The first line executes echo
with a single argument and immediately returns
the new process's pid
. The second line waits for the process to complete and
returns a Process::Status
object. Note that spawn
does not wait for the
process to finish execution like system
and does not reap the child's exit
status -- you must call Process::waitpid
(or equivalent) or the process will
become a zombie.
The spawn
method is capable of performing a large number of additional
operations, from setting up the new process's environment, to changing the
child's working directory, to redirecting arbitrary file descriptors.
See the Ruby 1.9 Process::spawn
documentation for details and the
STATUS
section below for a full account of the various Process::spawn
features supported by POSIX::Spawn::spawn
.
In addition to the spawn
method, Ruby 1.9 compatible implementations of
Kernel#system
and Kernel#
</code> are provided in the
POSIX::Spawnmodule. The
popen4` method can be used to spawn a process with redirected
stdin, stdout, and stderr objects.
The POSIX::Spawn
module can also be mixed in to classes and modules to include
spawn
and all utility methods in that namespace:
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
[TODO]
The POSIX::Spawn::spawn
method is designed to be as compatible with Ruby 1.9's
Process::spawn
as possible. Right now, it is a compatible subset. This
section documents the arguments and options that are and are not supported.
These Process::spawn
arguments are currently supported:
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)
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
current directory:
:chdir => str
These are currently NOT supported:
options: hash
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)
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)
Copyright (C) by Ryan Tomayko and Aman Gupta.
See the COPYING file for more information on license and redistribution.