fork(2)
调用会减慢,因为父进程由于需要复制页表而使用更多内存。在 fork() 的许多常见用法中,后面跟着 exec 系列函数之一来生成子进程( Kernel#system
、 IO::popen
、 Process::spawn
等),可以消除这种开销通过使用特殊的进程生成接口( posix_spawn()
、 vfork()
等)
posix-spawn 库旨在实现 Ruby 1.9 Process::spawn
接口的子集,在可用时利用快速进程生成接口,并在不可用的系统上提供合理的回退。
Process::spawn
接口的较大兼容子集以及 Ruby >= 1.8.7 下Kernel#system
、 Kernel#`
等的增强版本(目前仅适用于 MRI)。POSIX::Spawn::Child
类。 以下基准测试说明了在 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
文档;有关POSIX::Spawn::spawn
支持的各种Process::spawn
功能的完整说明,请参阅下面的STATUS
部分。
system
、 popen4
和`
除了spawn
方法之外, POSIX::Spawn
模块中还提供了Kernel#system
和Kernel#`
的Ruby 1.9兼容实现。 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
结果,因为构造函数未完成。
如果您想在进程中断时获取可用的out
和err
数据,请使用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
方法被设计为尽可能与 Ruby 1.9 的Process::spawn
兼容。现在,它是一个兼容的子集。
这些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)
Posix::Spawn::Child、Posix::Spawn#spawn、Posix::Spawn#system 和 Posix::Spawn#popen4 提供的:chdir
选项不是线程安全的,因为进程是通过 posix_spawn(2) 系统调用生成的继承调用进程的工作目录。 posix-spawn gem 通过在生成子进程之前和之后立即更改调用进程的工作目录来解决系统调用中的此限制。
版权所有 (c) Ryan Tomayko 和 Aman Gupta。
有关许可证和重新分发的更多信息,请参阅COPYING
文件。