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
文件。