module Process
Module Process 表示底层操作系统中的一个进程。它的方法支持对当前进程及其子进程的管理。
进程创建
以下每个方法都会在新的进程或子 shell 中执行给定的命令,或者在新的进程和/或子 shell 中执行多个命令。进程或子 shell 的选择取决于命令的形式;请参阅 参数 command_line 或 exe_path。
-
Process.spawn,Kernel#spawn:执行命令;在不等待完成的情况下返回新进程的 PID。 -
Process.exec:通过执行命令来替换当前进程。
此外
-
方法
Kernel#system在子 shell 中执行给定的命令字符串;返回true、false或nil。 -
方法
Kernel#`在子 shell 中执行给定的命令字符串;返回其 $stdout 字符串。 -
Module
Open3支持创建具有对其 $stdin、$stdout 和 $stderr 流的访问权限的子进程。
执行环境
可选的前导参数 env 是一个键值对哈希,其中每个键都是一个字符串,每个值都是一个字符串或 nil;每个键值对都会添加到新进程的 ENV 中。
Process.spawn( 'ruby -e "p ENV[\"Foo\"]"') Process.spawn({'Foo' => '0'}, 'ruby -e "p ENV[\"Foo\"]"')
输出
"0"
其效果通常与使用参数 env 调用 ENV#update 类似,其中每个命名环境变量都会被创建或更新(如果值为非 nil),或被删除(如果值为 nil)。
但是,如果新进程失败,对调用进程的某些修改可能会保留。例如,硬资源限制不会恢复。
参数 command_line 或 exe_path
必需的字符串参数是以下之一
-
command_line,如果它以 shell 保留字或特殊内置命令开头,或者包含一个或多个元字符。 -
否则为
exe_path。
参数 command_line
字符串参数 command_line 是要传递给 shell 的命令,它必须以 shell 保留字开头,以特殊内置命令开头,或包含元字符。
system('if true; then echo "Foo"; fi') # => true # Shell reserved word. system('exit') # => true # Built-in. system('date > /tmp/date.tmp') # => true # Contains meta character. system('date > /nop/date.tmp') # => false system('date > /nop/date.tmp', exception: true) # Raises RuntimeError.
命令可以包含命令的参数和选项。
system('echo "Foo"') # => true
输出
Foo
有关 shell 的详细信息,请参阅 执行 Shell。
参数 exe_path
参数 exe_path 是以下之一:
-
可执行文件的路径字符串
示例
system('/usr/bin/date') # => true # Path to date on Unix-style system. system('foo') # => nil # Command execlution failed.
输出
Thu Aug 31 10:06:48 AM CDT 2023
不带参数的包含空格的路径或命令名无法与上面的
command_line区分开,因此您必须使用 shell 以平台相关的方式引用或转义整个命令名,或者使用下面的数组形式。如果
exe_path不包含任何路径分隔符,则可执行文件会从PATH环境变量指定的目录中搜索。这里的“可执行”的含义取决于平台。即使文件被视为“可执行”,其内容也可能不是正确的可执行格式。在这种情况下,Ruby 会尝试使用
/bin/sh来运行它,就像 system(3) 所做的那样。File.write('shell_command', 'echo $SHELL', perm: 0o755) system('./shell_command') # prints "/bin/sh" or something.
-
包含可执行文件路径和将用作进程名的字符串的 2 元素数组
示例
pid = spawn(['sleep', 'Hello!'], '1') # 2-element array. p `ps -p #{pid} -o command=`
输出
"Hello! 1\n"
参数 args
如果 command_line 不包含 shell 元字符(空格和制表符除外),或者提供了 exe_path,Ruby 会直接调用可执行文件。此形式不使用 shell。
spawn("doesnt_exist") # Raises Errno::ENOENT spawn("doesnt_exist", "\n") # Raises Errno::ENOENT spawn("doesnt_exist\n") # => false # sh: 1: doesnot_exist: not found
错误消息来自 shell,并且会因系统而异。
如果 exe_path 之后提供了一个或多个 args,则每个都是传递给可执行文件的参数或选项。
示例
system('echo', '<', 'C*', '|', '$SHELL', '>') # => true
输出
< C* | $SHELL >
但是,Windows 上存在例外情况。请参阅 Windows 上的执行 Shell。
如果您想在不使用 shell 的情况下调用包含空格且不带参数的路径,则需要使用 2 元素数组 exe_path。
示例
path = '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome' spawn(path) # Raises Errno::ENOENT; No such file or directory - /Applications/Google spawn([path] * 2)
执行选项
可选的尾部参数 options 是一个执行选项的哈希。
工作目录 (:chdir)
默认情况下,新进程的工作目录与当前进程相同。
Dir.chdir('/var') Process.spawn('ruby -e "puts Dir.pwd"')
输出
/var
使用选项 :chdir 设置新进程的工作目录。
Process.spawn('ruby -e "puts Dir.pwd"', {chdir: '/tmp'})
输出
/tmp
当前进程的工作目录不会改变。
Dir.pwd # => "/var"
文件重定向(文件描述符)
在新进程中使用执行选项进行文件重定向。
此类选项的键可以是整数文件描述符(fd),指定源,也可以是 fd 数组,指定多个源。
整数源 fd 可以指定为
-
n:指定文件描述符 n。
有这些 fd 的简写符号
-
:in:指定文件描述符 0 (STDIN)。 -
:out:指定文件描述符 1 (STDOUT)。 -
:err:指定文件描述符 2 (STDERR)。
为源提供的值是以下之一
-
n:重定向到父进程中的 fd n。
-
filepath:通过open(filepath, mode, 0644)从filepath重定向到该文件或从该文件重定向,其中mode对于源:in是'r',对于源:out或:err是'w'。 -
[filepath]:通过open(filepath, 'r', 0644)从filepath文件重定向。 -
[filepath, mode]:通过open(filepath, mode, 0644)从filepath文件重定向或向其重定向。 -
[filepath, mode, perm]:通过open(filepath, mode, perm)从filepath文件重定向或向其重定向。 -
[:child, fd]:重定向到已重定向的fd。 -
:close:关闭子进程中的文件描述符。
环境变量 (:unsetenv_others)
默认情况下,新进程会继承父进程的环境变量;使用执行选项键 :unsetenv_others 并将其值设置为 true,以清除新进程中的环境变量。
执行选项 env 中指定的任何修改,都在新进程继承或清除其环境变量后进行;请参阅 执行环境。
文件创建访问 (:umask)
使用执行选项 :umask 设置新进程的文件创建访问权限;请参阅 访问模式。
command = 'ruby -e "puts sprintf(\"0%o\", File.umask)"' options = {:umask => 0644} Process.spawn(command, options)
输出
0644
进程组 (:pgroup 和 :new_pgroup)
默认情况下,新进程属于与父进程相同的 进程组。
要指定不同的进程组,请使用执行选项 :pgroup 并提供以下值之一
-
true:为新进程创建一个新的进程组。 -
pgid:在新进程中创建属于进程组 pgid 的进程。
仅在 Windows 上,使用执行选项 :new_pgroup 并将值设置为 true,为新进程创建一个新的进程组。
资源限制
使用执行选项设置资源限制。
这些选项的键是 :rlimit_resource_name 形式的符号,其中 resource_name 是在方法 Process.setrlimit 中描述的字符串资源名称的下划线形式。例如,键 :rlimit_cpu 对应资源限制 'CPU'。
此类键的值是以下之一
-
一个整数,同时指定当前和最大限制。
-
一个 2 元素整数数组,指定当前和最大限制。
文件描述符继承
默认情况下,新进程会继承父进程的文件描述符。
使用执行选项 :close_others => true 来修改继承,通过关闭未被重定向的标准文件描述符(3 及以上)来修改继承。
执行 Shell
在类 Unix 系统上,调用的 shell 是 /bin/sh;整个字符串 command_line 将作为参数传递给 shell 的 -c 选项。
shell 会对命令字符串执行正常的 shell 扩展。
示例
system('echo $SHELL: C*') # => true
输出
/bin/bash: CONTRIBUTING.md COPYING COPYING.ja
Windows 上的执行 Shell
在 Windows 上,调用的 shell 由环境变量 RUBYSHELL 确定(如果已定义),否则由 COMSPEC 确定;整个字符串 command_line 将作为参数传递给 RUBYSHELL 的 -c 选项,以及 /bin/sh,以及 COMSPEC 的 /c 选项。在以下情况下会自动调用 shell
-
命令是
cmd.exe的内置命令,例如echo。 -
可执行文件是批处理文件;其名称以
.bat或.cmd结尾。
请注意,即使以 exe_path 形式调用,命令仍将以 command_line 形式调用,因为 cmd.exe 不像 /bin/sh 那样接受脚本名,而只能与 /c 选项一起使用。
标准的 shell cmd.exe 会执行环境变量扩展,但没有 globbing 功能。
示例
system("echo %COMSPEC%: C*")' # => true
输出
C:\WINDOWS\system32\cmd.exe: C*
这里有什么
当前进程的 Getters
-
::argv0:返回进程名,是一个冻结字符串。 -
::egid:返回有效组 ID。 -
::euid:返回有效用户 ID。 -
::getpgrp:返回进程组 ID。 -
::getrlimit:返回资源限制。 -
::gid:返回(实际)组 ID。 -
::pid:返回进程 ID。 -
::ppid:返回父进程的进程 ID。 -
::uid:返回(实际)用户 ID。
当前进程的 Setters
-
::egid=:设置有效组 ID。 -
::euid=:设置有效用户 ID。 -
::gid=:设置(实际)组 ID。 -
::setproctitle:设置进程标题。 -
::setpgrp:将进程的进程组 ID 设置为零。 -
::setrlimit:设置资源限制。 -
::setsid:将进程建立为新的会话和进程组领导者,没有控制终端。 -
::uid=:设置用户 ID。
当前进程执行
-
::abort:立即终止进程。 -
::daemon:分离进程与控制终端,并将其作为系统守护进程在后台运行。 -
::exec:通过运行给定的外部命令来替换进程。 -
::exit:通过引发异常SystemExit来启动进程终止(可以捕获)。 -
::exit!:立即退出进程。 -
::warmup:通知 Ruby 虚拟机应用程序的引导序列已完成,并且 VM 可以开始优化应用程序。
子进程
-
::detach:防止子进程成为僵尸进程。 -
::fork:创建一个子进程。 -
::kill:向进程发送指定的信号。 -
::spawn:创建一个子进程。 -
::wait2,::waitpid2:等待子进程退出;返回其进程 ID 和状态。 -
::waitall:等待所有子进程退出;返回它们的进程 ID 和状态。
进程组
-
::getpgid:返回进程的进程组 ID。 -
::getpriority:返回进程、进程组或用户的调度优先级。 -
::getsid:返回进程的会话 ID。 -
::groups:返回此进程的补充组访问列表中的组 ID 数组。 -
::groups=:将补充组访问列表设置为指定的组 ID 数组。 -
::initgroups:初始化补充组访问列表。 -
::last_status:返回当前线程中最后一个执行的子进程的状态。 -
::maxgroups:返回允许在补充组访问列表中存在的最大组 ID 数。 -
::maxgroups=:设置允许在补充组访问列表中存在的最大组 ID 数。 -
::setpgid:设置进程的进程组 ID。 -
::setpriority:设置进程、进程组或用户的调度优先级。
计时
-
::clock_getres:返回系统时钟的分辨率。 -
::clock_gettime:从系统时钟返回时间。 -
::times:返回一个Process::Tms对象,其中包含当前进程及其子进程的时间。
Constants
- CLOCK_BOOTTIME
- CLOCK_BOOTTIME_ALARM
- CLOCK_MONOTONIC
- CLOCK_MONOTONIC_COARSE
- CLOCK_MONOTONIC_FAST
- CLOCK_MONOTONIC_PRECISE
- CLOCK_MONOTONIC_RAW
- CLOCK_MONOTONIC_RAW_APPROX
- CLOCK_PROCESS_CPUTIME_ID
- CLOCK_PROF
- CLOCK_REALTIME
- CLOCK_REALTIME_ALARM
- CLOCK_REALTIME_COARSE
- CLOCK_REALTIME_FAST
- CLOCK_REALTIME_PRECISE
- CLOCK_SECOND
- CLOCK_TAI
- CLOCK_THREAD_CPUTIME_ID
- CLOCK_UPTIME
- CLOCK_UPTIME_FAST
- CLOCK_UPTIME_PRECISE
- CLOCK_UPTIME_RAW
- CLOCK_UPTIME_RAW_APPROX
- CLOCK_VIRTUAL
- PRIO_PGRP
- PRIO_PROCESS
- PRIO_USER
- RLIMIT_AS
-
进程虚拟内存(地址空间)的最大大小(以字节为单位)。
有关详细信息,请参阅 getrlimit(2) 手册。
- RLIMIT_CORE
-
核心文件大小的最大值。
有关详细信息,请参阅 getrlimit(2) 手册。
- RLIMIT_CPU
-
CPU 时间限制(以秒为单位)。
有关详细信息,请参阅 getrlimit(2) 手册。
- RLIMIT_DATA
-
进程数据段的最大大小。
有关详细信息,请参阅 getrlimit(2) 手册。
- RLIMIT_FSIZE
-
进程可以创建的文件的最大大小。
有关详细信息,请参阅 getrlimit(2) 手册。
- RLIMIT_MEMLOCK
-
可以锁定在 RAM 中的内存的最大字节数。
有关详细信息,请参阅 getrlimit(2) 手册。
- RLIMIT_MSGQUEUE
-
指定调用进程的真实用户 ID 可以为 POSIX 消息队列分配的字节数限制。
有关详细信息,请参阅 getrlimit(2) 手册。
- RLIMIT_NICE
-
指定进程的 nice 值可以提高到的上限。
有关详细信息,请参阅 getrlimit(2) 手册。
- RLIMIT_NOFILE
-
指定此进程可以打开的最大文件描述符编号加一。
有关详细信息,请参阅 getrlimit(2) 手册。
- RLIMIT_NPROC
-
调用进程的真实用户 ID 可以创建的最大进程数。
有关详细信息,请参阅 getrlimit(2) 手册。
- RLIMIT_NPTS
-
调用进程的真实用户 ID 可以创建的最大伪终端数。
有关详细信息,请参阅 getrlimit(2) 手册。
- RLIMIT_RSS
-
指定进程常驻集大小的限制(以页为单位)。
有关详细信息,请参阅 getrlimit(2) 手册。
- RLIMIT_RTPRIO
-
指定可以为该进程设置的实时优先级的上限。
有关详细信息,请参阅 getrlimit(2) 手册。
- RLIMIT_RTTIME
-
指定使用实时调度策略调度的此进程可以消耗的 CPU 时间限制。
有关详细信息,请参阅 getrlimit(2) 手册。
- RLIMIT_SBSIZE
-
套接字缓冲区大小的最大值。
- RLIMIT_SIGPENDING
-
指定可以为调用进程的真实用户 ID 排队的信号数量限制。
有关详细信息,请参阅 getrlimit(2) 手册。
- RLIMIT_STACK
-
堆栈的最大大小(以字节为单位)。
有关详细信息,请参阅 getrlimit(2) 手册。
- RLIM_INFINITY
- RLIM_SAVED_CUR
- RLIM_SAVED_MAX
- WNOHANG
-
请参阅
Process.wait - WUNTRACED
-
请参阅
Process.wait
Public Class Methods
Source
VALUE
rb_proc__fork(VALUE _obj)
{
rb_pid_t pid = proc_fork_pid();
return PIDT2NUM(pid);
}
fork 的内部 API。请勿直接调用此方法。当前,它通过 Kernel#fork、Process.fork 和使用 "-" 的 IO.popen 调用。
此方法并非用于普通代码,而是用于应用程序监控库。通过覆盖此方法,您可以为 fork 事件添加自定义代码。
注意:Process.daemon 可能使用 fork(2) 实现,但不通过此方法。因此,根据您挂钩此方法的原因,您可能还想挂钩该方法。有关此问题的详细讨论,请参阅 此问题。
Source
static VALUE
f_abort(int c, const VALUE *a, VALUE _)
{
rb_f_abort(c, a);
UNREACHABLE_RETURN(Qnil);
}
立即终止执行,实际上是通过调用 Kernel.exit(false)。
如果提供了字符串参数 msg,则在终止前将其写入 STDERR;否则,如果引发了异常,则打印其消息和回溯。
Source
static VALUE
proc_argv0(VALUE process)
{
return rb_orig_progname;
}
返回正在执行的脚本的名称。将新值赋给 $0 不会影响此值。
此方法首次出现在 Ruby 2.1 中,用作一种不依赖全局变量的方式来获取脚本名称。
Source
static VALUE
rb_clock_getres(int argc, VALUE *argv, VALUE _)
{
int ret;
struct timetick tt;
timetick_int_t numerators[2];
timetick_int_t denominators[2];
int num_numerators = 0;
int num_denominators = 0;
#ifdef HAVE_CLOCK_GETRES
clockid_t c;
#endif
VALUE unit = (rb_check_arity(argc, 1, 2) == 2) ? argv[1] : Qnil;
VALUE clk_id = argv[0];
if (SYMBOL_P(clk_id)) {
#ifdef CLOCK_REALTIME
if (clk_id == RUBY_CLOCK_REALTIME) {
c = CLOCK_REALTIME;
goto getres;
}
#endif
#ifdef CLOCK_MONOTONIC
if (clk_id == RUBY_CLOCK_MONOTONIC) {
c = CLOCK_MONOTONIC;
goto getres;
}
#endif
#ifdef CLOCK_PROCESS_CPUTIME_ID
if (clk_id == RUBY_CLOCK_PROCESS_CPUTIME_ID) {
c = CLOCK_PROCESS_CPUTIME_ID;
goto getres;
}
#endif
#ifdef CLOCK_THREAD_CPUTIME_ID
if (clk_id == RUBY_CLOCK_THREAD_CPUTIME_ID) {
c = CLOCK_THREAD_CPUTIME_ID;
goto getres;
}
#endif
#ifdef RUBY_GETTIMEOFDAY_BASED_CLOCK_REALTIME
if (clk_id == RUBY_GETTIMEOFDAY_BASED_CLOCK_REALTIME) {
tt.giga_count = 0;
tt.count = 1000;
denominators[num_denominators++] = 1000000000;
goto success;
}
#endif
#ifdef RUBY_TIME_BASED_CLOCK_REALTIME
if (clk_id == RUBY_TIME_BASED_CLOCK_REALTIME) {
tt.giga_count = 1;
tt.count = 0;
denominators[num_denominators++] = 1000000000;
goto success;
}
#endif
#ifdef RUBY_TIMES_BASED_CLOCK_MONOTONIC
if (clk_id == RUBY_TIMES_BASED_CLOCK_MONOTONIC) {
tt.count = 1;
tt.giga_count = 0;
denominators[num_denominators++] = get_clk_tck();
goto success;
}
#endif
#ifdef RUBY_GETRUSAGE_BASED_CLOCK_PROCESS_CPUTIME_ID
if (clk_id == RUBY_GETRUSAGE_BASED_CLOCK_PROCESS_CPUTIME_ID) {
tt.giga_count = 0;
tt.count = 1000;
denominators[num_denominators++] = 1000000000;
goto success;
}
#endif
#ifdef RUBY_TIMES_BASED_CLOCK_PROCESS_CPUTIME_ID
if (clk_id == RUBY_TIMES_BASED_CLOCK_PROCESS_CPUTIME_ID) {
tt.count = 1;
tt.giga_count = 0;
denominators[num_denominators++] = get_clk_tck();
goto success;
}
#endif
#ifdef RUBY_CLOCK_BASED_CLOCK_PROCESS_CPUTIME_ID
if (clk_id == RUBY_CLOCK_BASED_CLOCK_PROCESS_CPUTIME_ID) {
tt.count = 1;
tt.giga_count = 0;
denominators[num_denominators++] = CLOCKS_PER_SEC;
goto success;
}
#endif
#ifdef RUBY_MACH_ABSOLUTE_TIME_BASED_CLOCK_MONOTONIC
if (clk_id == RUBY_MACH_ABSOLUTE_TIME_BASED_CLOCK_MONOTONIC) {
const mach_timebase_info_data_t *info = get_mach_timebase_info();
tt.count = 1;
tt.giga_count = 0;
numerators[num_numerators++] = info->numer;
denominators[num_denominators++] = info->denom;
denominators[num_denominators++] = 1000000000;
goto success;
}
#endif
}
else if (NUMERIC_CLOCKID) {
#if defined(HAVE_CLOCK_GETRES)
struct timespec ts;
c = NUM2CLOCKID(clk_id);
getres:
ret = clock_getres(c, &ts);
if (ret == -1)
clock_failed("getres", errno, clk_id);
tt.count = (int32_t)ts.tv_nsec;
tt.giga_count = ts.tv_sec;
denominators[num_denominators++] = 1000000000;
goto success;
#endif
}
else {
rb_unexpected_type(clk_id, T_SYMBOL);
}
clock_failed("getres", EINVAL, clk_id);
success:
if (unit == ID2SYM(id_hertz)) {
return timetick2dblnum_reciprocal(&tt, numerators, num_numerators, denominators, num_denominators);
}
else {
return make_clock_result(&tt, numerators, num_numerators, denominators, num_denominators, unit);
}
}
返回一个由 POSIX 函数 clock_getres() 确定的时钟分辨率。
Process.clock_getres(:CLOCK_REALTIME) # => 1.0e-09
有关 clock_id 和 unit 的值,请参阅 Process.clock_gettime。
示例
Process.clock_getres(:CLOCK_PROCESS_CPUTIME_ID, :float_microsecond) # => 0.001 Process.clock_getres(:CLOCK_PROCESS_CPUTIME_ID, :float_millisecond) # => 1.0e-06 Process.clock_getres(:CLOCK_PROCESS_CPUTIME_ID, :float_second) # => 1.0e-09 Process.clock_getres(:CLOCK_PROCESS_CPUTIME_ID, :microsecond) # => 0 Process.clock_getres(:CLOCK_PROCESS_CPUTIME_ID, :millisecond) # => 0 Process.clock_getres(:CLOCK_PROCESS_CPUTIME_ID, :nanosecond) # => 1 Process.clock_getres(:CLOCK_PROCESS_CPUTIME_ID, :second) # => 0
除了 Process.clock_gettime 中支持的 unit 值外,此方法还支持 :hertz,即每秒的时钟滴答数(它是 :float_second 的倒数)。
Process.clock_getres(:TIMES_BASED_CLOCK_PROCESS_CPUTIME_ID, :hertz) # => 100.0 Process.clock_getres(:TIMES_BASED_CLOCK_PROCESS_CPUTIME_ID, :float_second) # => 0.01
精度:请注意,由于底层错误,返回的分辨率在某些平台上可能不准确。在 Linux、macOS、BSD 或 AIX 平台、使用 ARM 处理器或使用虚拟化时,已报告了各种时钟(包括 :CLOCK_MONOTONIC 和 :CLOCK_MONOTONIC_RAW)的分辨率不准确。
Source
static VALUE
rb_clock_gettime(int argc, VALUE *argv, VALUE _)
{
int ret;
struct timetick tt;
timetick_int_t numerators[2];
timetick_int_t denominators[2];
int num_numerators = 0;
int num_denominators = 0;
VALUE unit = (rb_check_arity(argc, 1, 2) == 2) ? argv[1] : Qnil;
VALUE clk_id = argv[0];
#ifdef HAVE_CLOCK_GETTIME
clockid_t c;
#endif
if (SYMBOL_P(clk_id)) {
#ifdef CLOCK_REALTIME
if (clk_id == RUBY_CLOCK_REALTIME) {
c = CLOCK_REALTIME;
goto gettime;
}
#endif
#ifdef CLOCK_MONOTONIC
if (clk_id == RUBY_CLOCK_MONOTONIC) {
c = CLOCK_MONOTONIC;
goto gettime;
}
#endif
#ifdef CLOCK_PROCESS_CPUTIME_ID
if (clk_id == RUBY_CLOCK_PROCESS_CPUTIME_ID) {
c = CLOCK_PROCESS_CPUTIME_ID;
goto gettime;
}
#endif
#ifdef CLOCK_THREAD_CPUTIME_ID
if (clk_id == RUBY_CLOCK_THREAD_CPUTIME_ID) {
c = CLOCK_THREAD_CPUTIME_ID;
goto gettime;
}
#endif
/*
* Non-clock_gettime clocks are provided by symbol clk_id.
*/
#ifdef HAVE_GETTIMEOFDAY
/*
* GETTIMEOFDAY_BASED_CLOCK_REALTIME is used for
* CLOCK_REALTIME if clock_gettime is not available.
*/
#define RUBY_GETTIMEOFDAY_BASED_CLOCK_REALTIME ID2SYM(id_GETTIMEOFDAY_BASED_CLOCK_REALTIME)
if (clk_id == RUBY_GETTIMEOFDAY_BASED_CLOCK_REALTIME) {
struct timeval tv;
ret = gettimeofday(&tv, 0);
if (ret != 0)
rb_sys_fail("gettimeofday");
tt.giga_count = tv.tv_sec;
tt.count = (int32_t)tv.tv_usec * 1000;
denominators[num_denominators++] = 1000000000;
goto success;
}
#endif
#define RUBY_TIME_BASED_CLOCK_REALTIME ID2SYM(id_TIME_BASED_CLOCK_REALTIME)
if (clk_id == RUBY_TIME_BASED_CLOCK_REALTIME) {
time_t t;
t = time(NULL);
if (t == (time_t)-1)
rb_sys_fail("time");
tt.giga_count = t;
tt.count = 0;
denominators[num_denominators++] = 1000000000;
goto success;
}
#ifdef HAVE_TIMES
#define RUBY_TIMES_BASED_CLOCK_MONOTONIC \
ID2SYM(id_TIMES_BASED_CLOCK_MONOTONIC)
if (clk_id == RUBY_TIMES_BASED_CLOCK_MONOTONIC) {
struct tms buf;
clock_t c;
unsigned_clock_t uc;
c = times(&buf);
if (c == (clock_t)-1)
rb_sys_fail("times");
uc = (unsigned_clock_t)c;
tt.count = (int32_t)(uc % 1000000000);
tt.giga_count = (uc / 1000000000);
denominators[num_denominators++] = get_clk_tck();
goto success;
}
#endif
#ifdef RUSAGE_SELF
#define RUBY_GETRUSAGE_BASED_CLOCK_PROCESS_CPUTIME_ID \
ID2SYM(id_GETRUSAGE_BASED_CLOCK_PROCESS_CPUTIME_ID)
if (clk_id == RUBY_GETRUSAGE_BASED_CLOCK_PROCESS_CPUTIME_ID) {
struct rusage usage;
int32_t usec;
ret = getrusage(RUSAGE_SELF, &usage);
if (ret != 0)
rb_sys_fail("getrusage");
tt.giga_count = usage.ru_utime.tv_sec + usage.ru_stime.tv_sec;
usec = (int32_t)(usage.ru_utime.tv_usec + usage.ru_stime.tv_usec);
if (1000000 <= usec) {
tt.giga_count++;
usec -= 1000000;
}
tt.count = usec * 1000;
denominators[num_denominators++] = 1000000000;
goto success;
}
#endif
#ifdef HAVE_TIMES
#define RUBY_TIMES_BASED_CLOCK_PROCESS_CPUTIME_ID \
ID2SYM(id_TIMES_BASED_CLOCK_PROCESS_CPUTIME_ID)
if (clk_id == RUBY_TIMES_BASED_CLOCK_PROCESS_CPUTIME_ID) {
struct tms buf;
unsigned_clock_t utime, stime;
if (times(&buf) == (clock_t)-1)
rb_sys_fail("times");
utime = (unsigned_clock_t)buf.tms_utime;
stime = (unsigned_clock_t)buf.tms_stime;
tt.count = (int32_t)((utime % 1000000000) + (stime % 1000000000));
tt.giga_count = (utime / 1000000000) + (stime / 1000000000);
if (1000000000 <= tt.count) {
tt.count -= 1000000000;
tt.giga_count++;
}
denominators[num_denominators++] = get_clk_tck();
goto success;
}
#endif
#define RUBY_CLOCK_BASED_CLOCK_PROCESS_CPUTIME_ID \
ID2SYM(id_CLOCK_BASED_CLOCK_PROCESS_CPUTIME_ID)
if (clk_id == RUBY_CLOCK_BASED_CLOCK_PROCESS_CPUTIME_ID) {
clock_t c;
unsigned_clock_t uc;
errno = 0;
c = clock();
if (c == (clock_t)-1)
rb_sys_fail("clock");
uc = (unsigned_clock_t)c;
tt.count = (int32_t)(uc % 1000000000);
tt.giga_count = uc / 1000000000;
denominators[num_denominators++] = CLOCKS_PER_SEC;
goto success;
}
#ifdef __APPLE__
if (clk_id == RUBY_MACH_ABSOLUTE_TIME_BASED_CLOCK_MONOTONIC) {
const mach_timebase_info_data_t *info = get_mach_timebase_info();
uint64_t t = mach_absolute_time();
tt.count = (int32_t)(t % 1000000000);
tt.giga_count = t / 1000000000;
numerators[num_numerators++] = info->numer;
denominators[num_denominators++] = info->denom;
denominators[num_denominators++] = 1000000000;
goto success;
}
#endif
}
else if (NUMERIC_CLOCKID) {
#if defined(HAVE_CLOCK_GETTIME)
struct timespec ts;
c = NUM2CLOCKID(clk_id);
gettime:
ret = clock_gettime(c, &ts);
if (ret == -1)
clock_failed("gettime", errno, clk_id);
tt.count = (int32_t)ts.tv_nsec;
tt.giga_count = ts.tv_sec;
denominators[num_denominators++] = 1000000000;
goto success;
#endif
}
else {
rb_unexpected_type(clk_id, T_SYMBOL);
}
clock_failed("gettime", EINVAL, clk_id);
success:
return make_clock_result(&tt, numerators, num_numerators, denominators, num_denominators, unit);
}
返回一个由 POSIX 函数 clock_gettime() 确定的时钟时间。
Process.clock_gettime(:CLOCK_PROCESS_CPUTIME_ID) # => 198.650379677
参数 clock_id 应为一个符号或常量,用于指定要返回其时间的时钟;请参阅下文。
可选参数 unit 应为一个符号,用于指定返回的时钟时间使用的单位;请参阅下文。
参数 clock_id
参数 clock_id 指定要返回其时间的时钟;它可以是常量,例如 Process::CLOCK_REALTIME,或符号简写,例如 :CLOCK_REALTIME。
支持的时钟取决于底层操作系统;此方法在指示的平台上支持以下时钟(如果使用不受支持的时钟调用,则引发 Errno::EINVAL)
-
:CLOCK_BOOTTIME:Linux 2.6.39。 -
:CLOCK_BOOTTIME_ALARM:Linux 3.0。 -
:CLOCK_MONOTONIC:SUSv3 到 4、Linux 2.5.63、FreeBSD 3.0、NetBSD 2.0、OpenBSD 3.4、macOS 10.12、Windows-2000。 -
:CLOCK_MONOTONIC_COARSE:Linux 2.6.32。 -
:CLOCK_MONOTONIC_FAST:FreeBSD 8.1。 -
:CLOCK_MONOTONIC_PRECISE:FreeBSD 8.1。 -
:CLOCK_MONOTONIC_RAW:Linux 2.6.28、macOS 10.12。 -
:CLOCK_MONOTONIC_RAW_APPROX:macOS 10.12。 -
:CLOCK_PROCESS_CPUTIME_ID:SUSv3 到 4、Linux 2.5.63、FreeBSD 9.3、OpenBSD 5.4、macOS 10.12。 -
:CLOCK_PROF:FreeBSD 3.0、OpenBSD 2.1。 -
:CLOCK_REALTIME:SUSv2 到 4、Linux 2.5.63、FreeBSD 3.0、NetBSD 2.0、OpenBSD 2.1、macOS 10.12、Windows-8/Server-2012。建议使用Time.now而非 +:CLOCK_REALTIME:。 -
:CLOCK_REALTIME_ALARM:Linux 3.0。 -
:CLOCK_REALTIME_COARSE:Linux 2.6.32。 -
:CLOCK_REALTIME_FAST:FreeBSD 8.1。 -
:CLOCK_REALTIME_PRECISE:FreeBSD 8.1。 -
:CLOCK_SECOND:FreeBSD 8.1。 -
:CLOCK_TAI:Linux 3.10。 -
:CLOCK_THREAD_CPUTIME_ID:SUSv3 到 4、Linux 2.5.63、FreeBSD 7.1、OpenBSD 5.4、macOS 10.12。 -
:CLOCK_UPTIME:FreeBSD 7.0、OpenBSD 5.5。 -
:CLOCK_UPTIME_FAST:FreeBSD 8.1。 -
:CLOCK_UPTIME_PRECISE:FreeBSD 8.1。 -
:CLOCK_UPTIME_RAW:macOS 10.12。 -
:CLOCK_UPTIME_RAW_APPROX:macOS 10.12。 -
:CLOCK_VIRTUAL:FreeBSD 3.0、OpenBSD 2.1。
请注意,SUS 代表 Single Unix Specification。SUS 包含 POSIX,而 clock_gettime 定义在 POSIX 部分。SUS 将 :CLOCK_REALTIME 定义为强制性,但 :CLOCK_MONOTONIC、:CLOCK_PROCESS_CPUTIME_ID 和 :CLOCK_THREAD_CPUTIME_ID 是可选的。
当给定的 clock_id 不直接支持时,会使用某些仿真。
-
:CLOCK_REALTIME的仿真-
:GETTIMEOFDAY_BASED_CLOCK_REALTIME:使用 SUS 定义的 gettimeofday()(SUSv4 中已弃用)。分辨率为 1 微秒。 -
:TIME_BASED_CLOCK_REALTIME:使用 ISO C 定义的 time()。分辨率为 1 秒。
-
-
:CLOCK_MONOTONIC的仿真-
:MACH_ABSOLUTE_TIME_BASED_CLOCK_MONOTONIC:使用 mach_absolute_time(),在 Darwin 上可用。分辨率取决于 CPU。 -
:TIMES_BASED_CLOCK_MONOTONIC:使用 times() 的结果值,由 POSIX 定义,因此成功完成后,times() 应返回自某个任意过去点(例如,系统启动时间)以来的经过的真实时间(以时钟滴答为单位)。
例如,GNU/Linux 返回一个基于 jiffies 的值,并且它是单调的。但是,4.4BSD 使用 gettimeofday() 并且它不是单调的。(不过,FreeBSD 使用
:CLOCK_MONOTONIC。)分辨率是时钟滴答。`getconf CLK_TCK` 命令显示每秒的时钟滴答数。(在旧系统中,每秒的时钟滴答数由 HZ 宏定义。)如果它是 100 并且 clock_t 是 32 位整数类型,则分辨率为 10 毫秒,无法表示超过 497 天。
-
-
:CLOCK_PROCESS_CPUTIME_ID的仿真-
:GETRUSAGE_BASED_CLOCK_PROCESS_CPUTIME_ID:使用 SUS 定义的 getrusage()。getrusage() 与 RUSAGE_SELF 一起使用,以仅获取调用进程的时间(不包括子进程的时间)。结果是用户时间 (ru_utime) 和系统时间 (ru_stime) 的总和。分辨率为 1 微秒。 -
:TIMES_BASED_CLOCK_PROCESS_CPUTIME_ID:使用 POSIX 定义的 times()。结果是用户时间 (tms_utime) 和系统时间 (tms_stime) 的总和。忽略 tms_cutime 和 tms_cstime 以排除子进程的时间。分辨率是时钟滴答。`getconf CLK_TCK` 命令显示每秒的时钟滴答数。(在旧系统中,每秒的时钟滴答数由 HZ 宏定义。)如果它是 100,则分辨率为 10 毫秒。 -
:CLOCK_BASED_CLOCK_PROCESS_CPUTIME_ID:使用 ISO C 定义的 clock()。分辨率为1/CLOCKS_PER_SEC。CLOCKS_PER_SEC是 time.h 定义的 C 级宏。SUS 将CLOCKS_PER_SEC定义为 1000000;其他系统可能定义不同。如果CLOCKS_PER_SEC是 1000000(如 SUS 中),则分辨率为 1 微秒。如果CLOCKS_PER_SEC是 1000000 并且 clock_t 是 32 位整数类型,则无法表示超过 72 分钟。
-
参数 unit
可选参数 unit(默认为 :float_second)指定返回值的单位。
-
:float_microsecond:微秒数,以浮点数表示。 -
:float_millisecond:毫秒数,以浮点数表示。 -
:float_second:秒数,以浮点数表示。 -
:microsecond:微秒数,以整数表示。 -
:millisecond:毫秒数,以整数表示。 -
:nanosecond:纳秒数,以整数表示。 -
:second:秒数,以整数表示。
示例
Process.clock_gettime(:CLOCK_PROCESS_CPUTIME_ID, :float_microsecond) # => 203605054.825 Process.clock_gettime(:CLOCK_PROCESS_CPUTIME_ID, :float_millisecond) # => 203643.696848 Process.clock_gettime(:CLOCK_PROCESS_CPUTIME_ID, :float_second) # => 203.762181929 Process.clock_gettime(:CLOCK_PROCESS_CPUTIME_ID, :microsecond) # => 204123212 Process.clock_gettime(:CLOCK_PROCESS_CPUTIME_ID, :millisecond) # => 204298 Process.clock_gettime(:CLOCK_PROCESS_CPUTIME_ID, :nanosecond) # => 204602286036 Process.clock_gettime(:CLOCK_PROCESS_CPUTIME_ID, :second) # => 204
底层函数 clock_gettime() 返回纳秒数。对于 :CLOCK_REALTIME 的返回值,Float 对象(IEEE 754 双精度)不足以表示。如果需要精确的纳秒值,请使用 :nanosecond 作为 unit。
返回值(时间零点)是系统依赖的,可能是系统启动时间、进程启动时间、Epoch 等。
:CLOCK_REALTIME 的原点定义为 Epoch:1970-01-01 00:00:00 UTC;某些系统计算闰秒,而另一些则不计算,因此结果可能因系统而异。
Source
static VALUE
proc_daemon(int argc, VALUE *argv, VALUE _)
{
int n, nochdir = FALSE, noclose = FALSE;
switch (rb_check_arity(argc, 0, 2)) {
case 2: noclose = TO_BOOL(argv[1], "noclose");
case 1: nochdir = TO_BOOL(argv[0], "nochdir");
}
prefork();
n = rb_daemon(nochdir, noclose);
if (n < 0) rb_sys_fail("daemon");
return INT2FIX(n);
}
将当前进程与控制终端分离,并将其作为系统守护进程在后台运行;返回零。
默认情况下
-
将当前工作目录更改为根目录。
-
将 $stdin、$stdout 和 $stderr 重定向到空设备。
如果可选参数 nochdir 为 true,则不更改当前工作目录。
如果可选参数 noclose 为 true,则不重定向 $stdin、$stdout 或 $stderr。
Source
static VALUE
proc_detach(VALUE obj, VALUE pid)
{
return rb_detach_process(NUM2PIDT(pid));
}
避免子进程可能成为 僵尸进程。 Process.detach 通过设置一个单独的 Ruby 线程来防止这种情况,该线程的唯一任务是在进程 pid 终止时获取其状态。
只有当父进程永远不会等待子进程时,才需要此方法。
此示例不会回收第二个子进程;该进程在进程状态 (ps) 输出中显示为僵尸进程。
pid = Process.spawn('ruby', '-e', 'exit 13') # => 312691 sleep(1) # Find zombies. system("ps -ho pid,state -p #{pid}")
输出
312716 Z
此示例也不会回收第二个子进程,但它会分离该进程,使其不会成为僵尸进程。
pid = Process.spawn('ruby', '-e', 'exit 13') # => 313213 thread = Process.detach(pid) sleep(1) # => #<Process::Waiter:0x00007f038f48b838 run> system("ps -ho pid,state -p #{pid}") # Finds no zombies.
等待线程可以返回已分离子进程的 pid。
thread.join.pid # => 313262
Source
static VALUE
proc_getegid(VALUE obj)
{
rb_gid_t egid = getegid();
return GIDT2NUM(egid);
}
返回当前进程的有效组 ID。
Process.egid # => 500
Not available on all platforms.
Source
static VALUE
proc_setegid(VALUE obj, VALUE egid)
{
#if defined(HAVE_SETRESGID) || defined(HAVE_SETREGID) || defined(HAVE_SETEGID) || defined(HAVE_SETGID)
rb_gid_t gid;
#endif
check_gid_switch();
#if defined(HAVE_SETRESGID) || defined(HAVE_SETREGID) || defined(HAVE_SETEGID) || defined(HAVE_SETGID)
gid = OBJ2GID(egid);
#endif
#if defined(HAVE_SETRESGID)
if (setresgid(-1, gid, -1) < 0) rb_sys_fail(0);
#elif defined HAVE_SETREGID
if (setregid(-1, gid) < 0) rb_sys_fail(0);
#elif defined HAVE_SETEGID
if (setegid(gid) < 0) rb_sys_fail(0);
#elif defined HAVE_SETGID
if (gid == getgid()) {
if (setgid(gid) < 0) rb_sys_fail(0);
}
else {
rb_notimplement();
}
#else
rb_notimplement();
#endif
return egid;
}
设置当前进程的有效组 ID。
Not available on all platforms.
Source
static VALUE
proc_geteuid(VALUE obj)
{
rb_uid_t euid = geteuid();
return UIDT2NUM(euid);
}
返回当前进程的有效用户 ID。
Process.euid # => 501
Source
static VALUE
proc_seteuid_m(VALUE mod, VALUE euid)
{
check_uid_switch();
proc_seteuid(OBJ2UID(euid));
return euid;
}
设置当前进程的有效用户 ID。
Not available on all platforms.
Source
static VALUE
f_exec(int c, const VALUE *a, VALUE _)
{
rb_f_exec(c, a);
UNREACHABLE_RETURN(Qnil);
}
通过执行以下操作之一来替换当前进程
-
将字符串
command_line传递给 shell。 -
调用
exe_path处的可执行文件。
This method has potential security vulnerabilities if called with untrusted input; see Command Injection.
新进程是使用 exec 系统调用创建的;它可能会继承其部分环境,来自调用程序(可能包括打开的文件描述符)。
如果提供了参数 env,则它是一个哈希,它会影响新进程的 ENV;请参阅 执行环境。
参数 options 是新进程的选项哈希;请参阅 执行选项。
第一个必需参数是以下之一
-
command_line,如果它是字符串,并且以 shell 保留字或特殊内置命令开头,或者包含一个或多个元字符。 -
否则为
exe_path。
参数 command_line
字符串参数 command_line 是要传递给 shell 的命令,它必须以 shell 保留字开头,以特殊内置命令开头,或包含元字符。
exec('if true; then echo "Foo"; fi') # Shell reserved word. exec('exit') # Built-in. exec('date > date.tmp') # Contains meta character.
命令可以包含命令的参数和选项。
exec('echo "Foo"')
输出
Foo
有关 shell 的详细信息,请参阅 执行 Shell。
如果新进程无法执行,则引发异常。
参数 exe_path
参数 exe_path 是以下之一:
-
要调用的可执行文件的字符串路径。
-
一个包含可执行文件路径和将用作执行进程名称的字符串的 2 元素数组。
示例
exec('/usr/bin/date')
输出
Sat Aug 26 09:38:00 AM CDT 2023
Ruby 会直接调用可执行文件。此形式不使用 shell;有关注意事项,请参阅 参数 args。
exec('doesnt_exist') # Raises Errno::ENOENT
如果给定一个或多个 args,则每个参数或选项都将传递给可执行文件。
exec('echo', 'C*') exec('echo', 'hello', 'world')
输出
C* hello world
如果新进程无法执行,则引发异常。
Source
static VALUE
f_exit(int c, const VALUE *a, VALUE _)
{
rb_f_exit(c, a);
UNREACHABLE_RETURN(Qnil);
}
通过引发 SystemExit 来启动 Ruby 脚本的终止;可以捕获该异常。将退出状态 status 返回给底层操作系统。
参数 status 的值 true 和 false 分别表示成功和失败;整数值的含义取决于系统。
示例
begin exit puts 'Never get here.' rescue SystemExit puts 'Rescued a SystemExit exception.' end puts 'After begin block.'
输出
Rescued a SystemExit exception. After begin block.
在最终终止之前,Ruby 会执行任何退出时过程(请参阅 Kernel::at_exit)和任何对象终结器(请参阅 ObjectSpace::define_finalizer)。
示例
at_exit { puts 'In at_exit function.' } ObjectSpace.define_finalizer('string', proc { puts 'In finalizer.' }) exit
输出
In at_exit function. In finalizer.
Source
static VALUE
rb_f_exit_bang(int argc, VALUE *argv, VALUE obj)
{
int istatus;
if (rb_check_arity(argc, 0, 1) == 1) {
istatus = exit_status_code(argv[0]);
}
else {
istatus = EXIT_FAILURE;
}
_exit(istatus);
UNREACHABLE_RETURN(Qnil);
}
立即退出进程;不调用退出处理程序。将退出状态 status 返回给底层操作系统。
Process.exit!(true)
参数 status 的值 true 和 false 分别表示成功和失败;整数值的含义取决于系统。
Source
static VALUE
rb_f_fork(VALUE obj)
{
rb_pid_t pid;
pid = rb_call_proc__fork();
if (pid == 0) {
if (rb_block_given_p()) {
int status;
rb_protect(rb_yield, Qundef, &status);
ruby_stop(status);
}
return Qnil;
}
return PIDT2NUM(pid);
}
创建一个子进程。
如果提供了块,则在子进程中运行该块;块退出时,子进程以状态零终止。
puts "Before the fork: #{Process.pid}" fork do puts "In the child process: #{Process.pid}" end # => 382141 puts "After the fork: #{Process.pid}"
输出
Before the fork: 420496 After the fork: 420496 In the child process: 420520
如果不提供块,fork 调用会返回两次
-
一次在父进程中,返回子进程的 pid。
-
一次在子进程中,返回
nil。
示例
puts "This is the first line before the fork (pid #{Process.pid})" puts fork puts "This is the second line after the fork (pid #{Process.pid})"
输出
This is the first line before the fork (pid 420199) 420223 This is the second line after the fork (pid 420199) This is the second line after the fork (pid 420223)
无论哪种情况,子进程都可以使用 Kernel.exit! 退出,以避免调用 Kernel#at_exit。
为避免僵尸进程,父进程应调用以下任一方法
-
Process.wait,用于收集其子进程的终止状态。 -
Process.detach,用于注销对其状态不感兴趣。
调用 fork 的线程是创建的子进程中唯一的线程;fork 不会复制其他线程。
请注意,方法 fork 在某些平台可用,而在其他平台则不可用。
Process.respond_to?(:fork) # => true # Would be false on some.
如果不可用,您可以使用 ::spawn 代替 fork。
Source
static VALUE
proc_getpgid(VALUE obj, VALUE pid)
{
rb_pid_t i;
i = getpgid(NUM2PIDT(pid));
if (i < 0) rb_sys_fail(0);
return PIDT2NUM(i);
}
Returns the process group ID for the given process ID +pid+: Process.getpgid(Process.ppid) # => 25527
Not available on all platforms.
Source
static VALUE
proc_getpgrp(VALUE _)
{
rb_pid_t pgrp;
#if defined(HAVE_GETPGRP) && defined(GETPGRP_VOID)
pgrp = getpgrp();
if (pgrp < 0) rb_sys_fail(0);
return PIDT2NUM(pgrp);
#else /* defined(HAVE_GETPGID) */
pgrp = getpgid(0);
if (pgrp < 0) rb_sys_fail(0);
return PIDT2NUM(pgrp);
#endif
}
返回当前进程的进程组 ID。
Process.getpgid(0) # => 25527 Process.getpgrp # => 25527
Source
static VALUE
proc_getpriority(VALUE obj, VALUE which, VALUE who)
{
int prio, iwhich, iwho;
iwhich = NUM2INT(which);
iwho = NUM2INT(who);
errno = 0;
prio = getpriority(iwhich, iwho);
if (errno) rb_sys_fail(0);
return INT2FIX(prio);
}
返回指定进程、进程组或用户的调度优先级。
参数 kind 是以下选项之一:
-
Process::PRIO_PROCESS:返回进程的优先级。 -
Process::PRIO_PGRP:返回进程组的优先级。 -
Process::PRIO_USER:返回用户的优先级。
参数 id 是进程、进程组或用户的 ID;零表示 kind 的当前 ID。
示例
Process.getpriority(Process::PRIO_USER, 0) # => 19 Process.getpriority(Process::PRIO_PROCESS, 0) # => 19
Not available on all platforms.
Source
static VALUE
proc_getrlimit(VALUE obj, VALUE resource)
{
struct rlimit rlim;
if (getrlimit(rlimit_resource_type(resource), &rlim) < 0) {
rb_sys_fail("getrlimit");
}
return rb_assoc_new(RLIM2NUM(rlim.rlim_cur), RLIM2NUM(rlim.rlim_max));
}
返回给定 resource 的当前(软)限制和最大(硬)限制的 2 元素数组。
参数 resource 指定要返回其限制的资源;请参阅 Process.setrlimit。
返回的每个值 cur_limit 和 max_limit 都是整数;请参阅 Process.setrlimit。
示例
Process.getrlimit(:CORE) # => [0, 18446744073709551615]
请参阅 Process.setrlimit。
Not available on all platforms.
Source
static VALUE
proc_getsid(int argc, VALUE *argv, VALUE _)
{
rb_pid_t sid;
rb_pid_t pid = 0;
if (rb_check_arity(argc, 0, 1) == 1 && !NIL_P(argv[0]))
pid = NUM2PIDT(argv[0]);
sid = getsid(pid);
if (sid < 0) rb_sys_fail(0);
return PIDT2NUM(sid);
}
返回给定进程 ID pid 的会话 ID,如果未给定则返回当前进程的会话 ID。
Process.getsid # => 27422 Process.getsid(0) # => 27422 Process.getsid(Process.pid()) # => 27422
Not available on all platforms.
Source
static VALUE
proc_getgid(VALUE obj)
{
rb_gid_t gid = getgid();
return GIDT2NUM(gid);
}
返回当前进程的(实际)组 ID。
Process.gid # => 1000
Source
static VALUE
proc_setgid(VALUE obj, VALUE id)
{
rb_gid_t gid;
check_gid_switch();
gid = OBJ2GID(id);
#if defined(HAVE_SETRESGID)
if (setresgid(gid, -1, -1) < 0) rb_sys_fail(0);
#elif defined HAVE_SETREGID
if (setregid(gid, -1) < 0) rb_sys_fail(0);
#elif defined HAVE_SETRGID
if (setrgid(gid) < 0) rb_sys_fail(0);
#elif defined HAVE_SETGID
{
if (getegid() == gid) {
if (setgid(gid) < 0) rb_sys_fail(0);
}
else {
rb_notimplement();
}
}
#endif
return GIDT2NUM(gid);
}
将当前进程的组 ID 设置为 new_gid。
Process.gid = 1000 # => 1000
Source
static VALUE
proc_getgroups(VALUE obj)
{
VALUE ary, tmp;
int i, ngroups;
rb_gid_t *groups;
ngroups = getgroups(0, NULL);
if (ngroups == -1)
rb_sys_fail(0);
groups = ALLOCV_N(rb_gid_t, tmp, ngroups);
ngroups = getgroups(ngroups, groups);
if (ngroups == -1)
rb_sys_fail(0);
ary = rb_ary_new();
for (i = 0; i < ngroups; i++)
rb_ary_push(ary, GIDT2NUM(groups[i]));
ALLOCV_END(tmp);
return ary;
}
返回当前进程的补充组访问列表中的组 ID 数组。
Process.groups # => [4, 24, 27, 30, 46, 122, 135, 136, 1000]
返回数组的这些属性是系统相关的。
-
数组是否已排序(以及如何排序)。
-
数组是否包含有效组 ID。
-
数组是否包含重复的组 ID。
-
数组大小是否超过
Process.maxgroups的值。
使用此调用获取一个已排序且唯一的数组。
Process.groups.uniq.sort
Source
static VALUE
proc_setgroups(VALUE obj, VALUE ary)
{
int ngroups, i;
rb_gid_t *groups;
VALUE tmp;
PREPARE_GETGRNAM;
Check_Type(ary, T_ARRAY);
ngroups = RARRAY_LENINT(ary);
if (ngroups > maxgroups())
rb_raise(rb_eArgError, "too many groups, %d max", maxgroups());
groups = ALLOCV_N(rb_gid_t, tmp, ngroups);
for (i = 0; i < ngroups; i++) {
VALUE g = RARRAY_AREF(ary, i);
groups[i] = OBJ2GID1(g);
}
FINISH_GETGRNAM;
if (setgroups(ngroups, groups) == -1) /* ngroups <= maxgroups */
rb_sys_fail(0);
ALLOCV_END(tmp);
return proc_getgroups(obj);
}
将补充组访问列表设置为给定的组 ID 数组。
Process.groups # => [0, 1, 2, 3, 4, 6, 10, 11, 20, 26, 27] Process.groups = [27, 6, 10, 11] # => [27, 6, 10, 11] Process.groups # => [27, 6, 10, 11]
Source
static VALUE
proc_initgroups(VALUE obj, VALUE uname, VALUE base_grp)
{
if (initgroups(StringValueCStr(uname), OBJ2GID(base_grp)) != 0) {
rb_sys_fail(0);
}
return proc_getgroups(obj);
}
设置补充组访问列表;新列表将包含:
-
给定用户
username所属的组的组 ID。 -
组 ID
gid。
示例
Process.groups # => [0, 1, 2, 3, 4, 6, 10, 11, 20, 26, 27] Process.initgroups('me', 30) # => [30, 6, 10, 11] Process.groups # => [30, 6, 10, 11]
Not available on all platforms.
Source
static VALUE
proc_rb_f_kill(int c, const VALUE *v, VALUE _)
{
return rb_f_kill(c, v);
}
向 ids 指定的每个进程发送一个信号(ids 必须至少指定一个 ID);返回发送信号的计数。
对于每个给定的 id,如果 id 是:
-
正数,则将信号发送到进程 ID 为
id的进程。 -
零,则将信号发送到当前进程组中的所有进程。
-
负数,则将信号发送到系统依赖的进程集合。
参数 signal 指定要发送的信号;参数可以是:
-
整数信号编号:例如,
-29、0、29。 -
信号名称(字符串),带有或不带前缀
'SIG',以及带有或不带进一步前缀减号 ('-'):例如,-
'SIGPOLL'. -
'POLL', -
'-SIGPOLL'. -
'-POLL'.
-
-
信号符号,带有或不带前缀
'SIG',以及带有或不带进一步前缀减号 ('-'):例如,-
:SIGPOLL. -
:POLL. -
:'-SIGPOLL'. -
:'-POLL'.
-
如果 signal 是:
-
非负整数,或者不带前缀
'-'的信号名称或符号,则向进程 ID 为id的每个进程发送信号。 -
负整数,或者带前缀
'-'的信号名称或符号,则向组 ID 为id的每个进程组发送信号。
使用方法 Signal.list 查看 Ruby 在底层平台上支持哪些信号;该方法返回一个包含支持信号的字符串名称和非负整数值的哈希。返回哈希的大小和内容因平台而异。
此外,信号 0 可用于确定进程是否存在。
示例
pid = fork do Signal.trap('HUP') { puts 'Ouch!'; exit } # ... do some work ... end # ... Process.kill('HUP', pid) Process.wait
输出
Ouch!
异常
-
如果
signal是整数但无效,则引发 Errno::EINVAL 或RangeError。 -
如果
signal是字符串或符号但无效,则引发ArgumentError。 -
如果
ids中的一个无效,则引发 Errno::ESRCH 或RangeError。 -
如果权限不足,则引发 Errno::EPERM。
在后两种情况下,信号可能已发送到某些进程。
Source
static VALUE
proc_s_last_status(VALUE mod)
{
return rb_last_status_get();
}
返回一个 Process::Status 对象,表示当前线程中最近退出的子进程,如果没有则返回 nil。
Process.spawn('ruby', '-e', 'exit 13') Process.wait Process.last_status # => #<Process::Status: pid 14396 exit 13> Process.spawn('ruby', '-e', 'exit 14') Process.wait Process.last_status # => #<Process::Status: pid 4692 exit 14> Process.spawn('ruby', '-e', 'exit 15') # 'exit 15' has not been reaped by #wait. Process.last_status # => #<Process::Status: pid 4692 exit 14> Process.wait Process.last_status # => #<Process::Status: pid 1380 exit 15>
Source
static VALUE
proc_getmaxgroups(VALUE obj)
{
return INT2FIX(maxgroups());
}
返回补充组访问列表中允许的最大组 ID 数。
Process.maxgroups # => 32
Source
static VALUE
proc_setmaxgroups(VALUE obj, VALUE val)
{
int ngroups = FIX2INT(val);
int ngroups_max = get_sc_ngroups_max();
if (ngroups <= 0)
rb_raise(rb_eArgError, "maxgroups %d should be positive", ngroups);
if (ngroups > RB_MAX_GROUPS)
ngroups = RB_MAX_GROUPS;
if (ngroups_max > 0 && ngroups > ngroups_max)
ngroups = ngroups_max;
_maxgroups = ngroups;
return INT2FIX(_maxgroups);
}
设置补充组访问列表中允许的最大组 ID 数。
Source
static VALUE
proc_get_pid(VALUE _)
{
return get_pid();
}
返回当前进程的进程 ID。
Process.pid # => 15668
Source
static VALUE
proc_get_ppid(VALUE _)
{
return get_ppid();
}
返回当前进程的父进程 ID。
puts "Pid is #{Process.pid}." fork { puts "Parent pid is #{Process.ppid}." }
输出
Pid is 271290. Parent pid is 271290.
在某些平台上可能无法返回可信的值。
Source
static VALUE
proc_setpgid(VALUE obj, VALUE pid, VALUE pgrp)
{
rb_pid_t ipid, ipgrp;
ipid = NUM2PIDT(pid);
ipgrp = NUM2PIDT(pgrp);
if (setpgid(ipid, ipgrp) < 0) rb_sys_fail(0);
return INT2FIX(0);
}
将进程 ID 为 pid 的进程的进程组 ID 设置为 pgid。
Not available on all platforms.
Source
static VALUE
proc_setpgrp(VALUE _)
{
/* check for posix setpgid() first; this matches the posix */
/* getpgrp() above. It appears that configure will set SETPGRP_VOID */
/* even though setpgrp(0,0) would be preferred. The posix call avoids */
/* this confusion. */
#ifdef HAVE_SETPGID
if (setpgid(0,0) < 0) rb_sys_fail(0);
#elif defined(HAVE_SETPGRP) && defined(SETPGRP_VOID)
if (setpgrp() < 0) rb_sys_fail(0);
#endif
return INT2FIX(0);
}
等同于 setpgid(0, 0)。
Not available on all platforms.
Source
static VALUE
proc_setpriority(VALUE obj, VALUE which, VALUE who, VALUE prio)
{
int iwhich, iwho, iprio;
iwhich = NUM2INT(which);
iwho = NUM2INT(who);
iprio = NUM2INT(prio);
if (setpriority(iwhich, iwho, iprio) < 0)
rb_sys_fail(0);
return INT2FIX(0);
}
请参阅 Process.getpriority。
示例
Process.setpriority(Process::PRIO_USER, 0, 19) # => 0 Process.setpriority(Process::PRIO_PROCESS, 0, 19) # => 0 Process.getpriority(Process::PRIO_USER, 0) # => 19 Process.getpriority(Process::PRIO_PROCESS, 0) # => 19
Not available on all platforms.
Source
static VALUE
proc_setproctitle(VALUE process, VALUE title)
{
return ruby_setproctitle(title);
}
设置 ps(1) 命令中显示的进程标题。不一定在所有平台上都有效。无论结果如何,都不会引发异常,即使平台不支持该功能也不会引发 NotImplementedError。
调用此方法不会影响 $0 的值。
Process.setproctitle('myapp: worker #%d' % worker_id)
此方法首次出现在 Ruby 2.1 中,作为一种全局变量自由的方式来更改进程标题。
Source
static VALUE
proc_setrlimit(int argc, VALUE *argv, VALUE obj)
{
VALUE resource, rlim_cur, rlim_max;
struct rlimit rlim;
rb_check_arity(argc, 2, 3);
resource = argv[0];
rlim_cur = argv[1];
if (argc < 3 || NIL_P(rlim_max = argv[2]))
rlim_max = rlim_cur;
rlim.rlim_cur = rlimit_resource_value(rlim_cur);
rlim.rlim_max = rlimit_resource_value(rlim_max);
if (setrlimit(rlimit_resource_type(resource), &rlim) < 0) {
rb_sys_fail("setrlimit");
}
return Qnil;
}
为当前进程设置给定 resource 的限制为 cur_limit(软限制)和 max_limit(硬限制);返回 nil。
参数 resource 指定要设置其限制的资源;参数可以作为符号、字符串或以 Process::RLIMIT_ 开头的常量(例如 :CORE、'CORE' 或 Process::RLIMIT_CORE)给出。
可用的和支持的资源因系统而异,可能包括(此处以符号表示):
-
:AS:可用总内存(字节)(SUSv3、NetBSD、FreeBSD、OpenBSD 除 4.4BSD-Lite 外)。 -
:CORE:核心大小(字节)(SUSv3)。 -
:CPU:CPU 时间(秒)(SUSv3)。 -
:DATA:Data段(字节)(SUSv3)。 -
:FSIZE:File大小(字节)(SUSv3)。 -
:MEMLOCK:mlock(2) 的总大小(字节)(4.4BSD、GNU/Linux)。 -
:MSGQUEUE:POSIX 消息队列的分配(字节)(GNU/Linux)。 -
:NICE:进程 nice(2) 值的上限(数字)(GNU/Linux)。 -
:NOFILE:File描述符(数字)(SUSv3)。 -
:NPROC:用户进程数(数字)(4.4BSD、GNU/Linux)。 -
:NPTS:伪终端数(数字)(FreeBSD)。 -
:RSS:驻留内存大小(字节)(4.2BSD、GNU/Linux)。 -
:RTPRIO:进程实时优先级的上限(数字)(GNU/Linux)。 -
:RTTIME:实时进程的 CPU 时间(微秒)(GNU/Linux)。 -
:SBSIZE:所有套接字缓冲区(字节)(NetBSD、FreeBSD)。 -
:SIGPENDING:允许的排队信号数(信号)(GNU/Linux)。 -
:STACK:堆栈大小(字节)(SUSv3)。
参数 cur_limit 和 max_limit 可以是:
-
整数(
max_limit不应小于cur_limit)。 -
Symbol:SAVED_MAX、字符串'SAVED_MAX'或常量Process::RLIM_SAVED_MAX:已保存的最大限制。 -
Symbol:SAVED_CUR、字符串'SAVED_CUR'或常量Process::RLIM_SAVED_CUR:已保存的当前限制。 -
Symbol:INFINITY、字符串'INFINITY'或常量Process::RLIM_INFINITY:资源无限制。
此示例将核心大小的软限制提高到硬限制,以尝试使核心转储成为可能。
Process.setrlimit(:CORE, Process.getrlimit(:CORE)[1])
Not available on all platforms.
Source
static VALUE
proc_setsid(VALUE _)
{
rb_pid_t pid;
pid = setsid();
if (pid < 0) rb_sys_fail(0);
return PIDT2NUM(pid);
}
将当前进程建立为一个新的会话和进程组领导者,没有控制终端;返回会话 ID。
Process.setsid # => 27422
Not available on all platforms.
Source
static VALUE
rb_f_spawn(int argc, VALUE *argv, VALUE _)
{
rb_pid_t pid;
char errmsg[CHILD_ERRMSG_BUFLEN] = { '\0' };
VALUE execarg_obj, fail_str;
struct rb_execarg *eargp;
execarg_obj = rb_execarg_new(argc, argv, TRUE, FALSE);
eargp = rb_execarg_get(execarg_obj);
fail_str = eargp->use_shell ? eargp->invoke.sh.shell_script : eargp->invoke.cmd.command_name;
pid = rb_execarg_spawn(execarg_obj, errmsg, sizeof(errmsg));
if (pid == -1) {
int err = errno;
rb_exec_fail(eargp, err, errmsg);
RB_GC_GUARD(execarg_obj);
rb_syserr_fail_str(err, fail_str);
}
#if defined(HAVE_WORKING_FORK) || defined(HAVE_SPAWNV)
return PIDT2NUM(pid);
#else
return Qnil;
#endif
}
通过在子进程中执行以下操作之一来创建一个新的子进程:
-
将字符串
command_line传递给 shell。 -
调用
exe_path处的可执行文件。
This method has potential security vulnerabilities if called with untrusted input; see Command Injection.
返回新进程的进程 ID (pid),而不等待其完成。
为避免僵尸进程,父进程应调用以下任一方法
-
Process.wait,用于收集其子进程的终止状态。 -
Process.detach,用于注销对其状态不感兴趣。
新进程是使用 exec 系统调用创建的;它可能会继承其部分环境,来自调用程序(可能包括打开的文件描述符)。
如果提供了参数 env,则它是一个哈希,它会影响新进程的 ENV;请参阅 执行环境。
参数 options 是新进程的选项哈希;请参阅 执行选项。
第一个必需参数是以下之一
-
command_line,如果它是字符串,并且以 shell 保留字或特殊内置命令开头,或者包含一个或多个元字符。 -
否则为
exe_path。
参数 command_line
字符串参数 command_line 是要传递给 shell 的命令,它必须以 shell 保留字开头,以特殊内置命令开头,或包含元字符。
spawn('if true; then echo "Foo"; fi') # => 798847 # Shell reserved word. Process.wait # => 798847 spawn('exit') # => 798848 # Built-in. Process.wait # => 798848 spawn('date > /tmp/date.tmp') # => 798879 # Contains meta character. Process.wait # => 798849 spawn('date > /nop/date.tmp') # => 798882 # Issues error message. Process.wait # => 798882
命令可以包含命令的参数和选项。
spawn('echo "Foo"') # => 799031 Process.wait # => 799031
输出
Foo
有关 shell 的详细信息,请参阅 执行 Shell。
如果新进程无法执行,则引发异常。
参数 exe_path
参数 exe_path 是以下之一:
-
要调用的可执行文件的字符串路径。
-
一个包含可执行文件路径和用作执行进程名称的字符串的 2 元素数组。
spawn('/usr/bin/date') # Path to date on Unix-style system. Process.wait
输出
Mon Aug 28 11:43:10 AM CDT 2023
Ruby 会直接调用可执行文件。此形式不使用 shell;有关注意事项,请参阅 参数 args。
如果给定一个或多个 args,则每个参数或选项都将传递给可执行文件。
spawn('echo', 'C*') # => 799392 Process.wait # => 799392 spawn('echo', 'hello', 'world') # => 799393 Process.wait # => 799393
输出
C* hello world
如果新进程无法执行,则引发异常。
Source
VALUE
rb_proc_times(VALUE obj)
{
VALUE utime, stime, cutime, cstime, ret;
#if defined(RUSAGE_SELF) && defined(RUSAGE_CHILDREN)
struct rusage usage_s, usage_c;
if (getrusage(RUSAGE_SELF, &usage_s) != 0 || getrusage(RUSAGE_CHILDREN, &usage_c) != 0)
rb_sys_fail("getrusage");
utime = DBL2NUM((double)usage_s.ru_utime.tv_sec + (double)usage_s.ru_utime.tv_usec/1e6);
stime = DBL2NUM((double)usage_s.ru_stime.tv_sec + (double)usage_s.ru_stime.tv_usec/1e6);
cutime = DBL2NUM((double)usage_c.ru_utime.tv_sec + (double)usage_c.ru_utime.tv_usec/1e6);
cstime = DBL2NUM((double)usage_c.ru_stime.tv_sec + (double)usage_c.ru_stime.tv_usec/1e6);
#else
const double hertz = (double)get_clk_tck();
struct tms buf;
times(&buf);
utime = DBL2NUM(buf.tms_utime / hertz);
stime = DBL2NUM(buf.tms_stime / hertz);
cutime = DBL2NUM(buf.tms_cutime / hertz);
cstime = DBL2NUM(buf.tms_cstime / hertz);
#endif
ret = rb_struct_new(rb_cProcessTms, utime, stime, cutime, cstime);
RB_GC_GUARD(utime);
RB_GC_GUARD(stime);
RB_GC_GUARD(cutime);
RB_GC_GUARD(cstime);
return ret;
}
返回一个 Process::Tms 结构,其中包含当前进程及其子进程的用户和系统 CPU 时间。
Process.times # => #<struct Process::Tms utime=55.122118, stime=35.533068, cutime=0.0, cstime=0.002846>
精度由平台定义。
Source
static VALUE
proc_getuid(VALUE obj)
{
rb_uid_t uid = getuid();
return UIDT2NUM(uid);
}
返回当前进程的(实际)用户 ID。
Process.uid # => 1000
Source
static VALUE
proc_setuid(VALUE obj, VALUE id)
{
rb_uid_t uid;
check_uid_switch();
uid = OBJ2UID(id);
#if defined(HAVE_SETRESUID)
if (setresuid(uid, -1, -1) < 0) rb_sys_fail(0);
#elif defined HAVE_SETREUID
if (setreuid(uid, -1) < 0) rb_sys_fail(0);
#elif defined HAVE_SETRUID
if (setruid(uid) < 0) rb_sys_fail(0);
#elif defined HAVE_SETUID
{
if (geteuid() == uid) {
if (setuid(uid) < 0) rb_sys_fail(0);
}
else {
rb_notimplement();
}
}
#endif
return id;
}
将当前进程的(用户)用户 ID 设置为 new_uid。
Process.uid = 1000 # => 1000
Not available on all platforms.
Source
static VALUE
proc_m_wait(int c, VALUE *v, VALUE _)
{
return proc_wait(c, v);
}
等待一个合适的子进程退出,返回其进程 ID,并将 $? 设置为一个包含该进程信息的 Process::Status 对象。等待哪个子进程取决于给定 pid 的值:
-
正整数:等待进程 ID 为
pid的子进程。pid0 = Process.spawn('ruby', '-e', 'exit 13') # => 230866 pid1 = Process.spawn('ruby', '-e', 'exit 14') # => 230891 Process.wait(pid0) # => 230866 $? # => #<Process::Status: pid 230866 exit 13> Process.wait(pid1) # => 230891 $? # => #<Process::Status: pid 230891 exit 14> Process.wait(pid0) # Raises Errno::ECHILD
-
0:等待与当前进程组 ID 相同的任何子进程。parent_pgpid = Process.getpgid(Process.pid) puts "Parent process group ID is #{parent_pgpid}." child0_pid = fork do puts "Child 0 pid is #{Process.pid}" child0_pgid = Process.getpgid(Process.pid) puts "Child 0 process group ID is #{child0_pgid} (same as parent's)." end child1_pid = fork do puts "Child 1 pid is #{Process.pid}" Process.setpgid(0, Process.pid) child1_pgid = Process.getpgid(Process.pid) puts "Child 1 process group ID is #{child1_pgid} (different from parent's)." end retrieved_pid = Process.wait(0) puts "Process.wait(0) returned pid #{retrieved_pid}, which is child 0 pid." begin Process.wait(0) rescue Errno::ECHILD => x puts "Raised #{x.class}, because child 1 process group ID differs from parent process group ID." end
输出
Parent process group ID is 225764. Child 0 pid is 225788 Child 0 process group ID is 225764 (same as parent's). Child 1 pid is 225789 Child 1 process group ID is 225789 (different from parent's). Process.wait(0) returned pid 225788, which is child 0 pid. Raised Errno::ECHILD, because child 1 process group ID differs from parent process group ID.
-
-1(默认):等待任何子进程。parent_pgpid = Process.getpgid(Process.pid) puts "Parent process group ID is #{parent_pgpid}." child0_pid = fork do puts "Child 0 pid is #{Process.pid}" child0_pgid = Process.getpgid(Process.pid) puts "Child 0 process group ID is #{child0_pgid} (same as parent's)." end child1_pid = fork do puts "Child 1 pid is #{Process.pid}" Process.setpgid(0, Process.pid) child1_pgid = Process.getpgid(Process.pid) puts "Child 1 process group ID is #{child1_pgid} (different from parent's)." sleep 3 # To force child 1 to exit later than child 0 exit. end child_pids = [child0_pid, child1_pid] retrieved_pid = Process.wait(-1) puts child_pids.include?(retrieved_pid) retrieved_pid = Process.wait(-1) puts child_pids.include?(retrieved_pid)
输出
Parent process group ID is 228736. Child 0 pid is 228758 Child 0 process group ID is 228736 (same as parent's). Child 1 pid is 228759 Child 1 process group ID is 228759 (different from parent's). true true
-
小于
-1:等待进程组 ID 为-pid的任何子进程。parent_pgpid = Process.getpgid(Process.pid) puts "Parent process group ID is #{parent_pgpid}." child0_pid = fork do puts "Child 0 pid is #{Process.pid}" child0_pgid = Process.getpgid(Process.pid) puts "Child 0 process group ID is #{child0_pgid} (same as parent's)." end child1_pid = fork do puts "Child 1 pid is #{Process.pid}" Process.setpgid(0, Process.pid) child1_pgid = Process.getpgid(Process.pid) puts "Child 1 process group ID is #{child1_pgid} (different from parent's)." end sleep 1 retrieved_pid = Process.wait(-child1_pid) puts "Process.wait(-child1_pid) returned pid #{retrieved_pid}, which is child 1 pid." begin Process.wait(-child1_pid) rescue Errno::ECHILD => x puts "Raised #{x.class}, because there's no longer a child with process group id #{child1_pid}." end
输出
Parent process group ID is 230083. Child 0 pid is 230108 Child 0 process group ID is 230083 (same as parent's). Child 1 pid is 230109 Child 1 process group ID is 230109 (different from parent's). Process.wait(-child1_pid) returned pid 230109, which is child 1 pid. Raised Errno::ECHILD, because there's no longer a child with process group id 230109.
参数 flags 应给出为以下常量之一,或两者的逻辑 OR:
-
Process::WNOHANG:如果没有可用的子进程,则不阻塞。 -
Process::WUNTRACED:即使尚未报告,也可能返回已停止的子进程。
并非所有标志在所有平台上都可用。
如果不存在合适的子进程,则引发 Errno::ECHILD。
Not available on all platforms.
Process.waitpid 是 Process.wait 的别名。
Source
static VALUE
proc_wait2(int argc, VALUE *argv, VALUE _)
{
VALUE pid = proc_wait(argc, argv);
if (NIL_P(pid)) return Qnil;
return rb_assoc_new(pid, rb_last_status_get());
}
与 Process.waitpid 类似,但返回一个包含子进程 pid 和 Process::Status status 的数组。
pid = Process.spawn('ruby', '-e', 'exit 13') # => 309581 Process.wait2(pid) # => [309581, #<Process::Status: pid 309581 exit 13>]
Process.waitpid2 是 Process.wait2 的别名。
Source
static VALUE
proc_waitall(VALUE _)
{
VALUE result;
rb_pid_t pid;
int status;
result = rb_ary_new();
rb_last_status_clear();
for (pid = -1;;) {
pid = rb_waitpid(-1, &status, 0);
if (pid == -1) {
int e = errno;
if (e == ECHILD)
break;
rb_syserr_fail(e, 0);
}
rb_ary_push(result, rb_assoc_new(PIDT2NUM(pid), rb_last_status_get()));
}
return result;
}
等待所有子进程,返回一个包含 2 元素数组的数组;每个子数组包含一个已回收子进程的整数 pid 和 Process::Status status。
pid0 = Process.spawn('ruby', '-e', 'exit 13') # => 325470 pid1 = Process.spawn('ruby', '-e', 'exit 14') # => 325495 Process.waitall # => [[325470, #<Process::Status: pid 325470 exit 13>], [325495, #<Process::Status: pid 325495 exit 14>]]
Source
static VALUE
proc_m_wait(int c, VALUE *v, VALUE _)
{
return proc_wait(c, v);
}
等待一个合适的子进程退出,返回其进程 ID,并将 $? 设置为一个包含该进程信息的 Process::Status 对象。等待哪个子进程取决于给定 pid 的值:
-
正整数:等待进程 ID 为
pid的子进程。pid0 = Process.spawn('ruby', '-e', 'exit 13') # => 230866 pid1 = Process.spawn('ruby', '-e', 'exit 14') # => 230891 Process.wait(pid0) # => 230866 $? # => #<Process::Status: pid 230866 exit 13> Process.wait(pid1) # => 230891 $? # => #<Process::Status: pid 230891 exit 14> Process.wait(pid0) # Raises Errno::ECHILD
-
0:等待与当前进程组 ID 相同的任何子进程。parent_pgpid = Process.getpgid(Process.pid) puts "Parent process group ID is #{parent_pgpid}." child0_pid = fork do puts "Child 0 pid is #{Process.pid}" child0_pgid = Process.getpgid(Process.pid) puts "Child 0 process group ID is #{child0_pgid} (same as parent's)." end child1_pid = fork do puts "Child 1 pid is #{Process.pid}" Process.setpgid(0, Process.pid) child1_pgid = Process.getpgid(Process.pid) puts "Child 1 process group ID is #{child1_pgid} (different from parent's)." end retrieved_pid = Process.wait(0) puts "Process.wait(0) returned pid #{retrieved_pid}, which is child 0 pid." begin Process.wait(0) rescue Errno::ECHILD => x puts "Raised #{x.class}, because child 1 process group ID differs from parent process group ID." end
输出
Parent process group ID is 225764. Child 0 pid is 225788 Child 0 process group ID is 225764 (same as parent's). Child 1 pid is 225789 Child 1 process group ID is 225789 (different from parent's). Process.wait(0) returned pid 225788, which is child 0 pid. Raised Errno::ECHILD, because child 1 process group ID differs from parent process group ID.
-
-1(默认):等待任何子进程。parent_pgpid = Process.getpgid(Process.pid) puts "Parent process group ID is #{parent_pgpid}." child0_pid = fork do puts "Child 0 pid is #{Process.pid}" child0_pgid = Process.getpgid(Process.pid) puts "Child 0 process group ID is #{child0_pgid} (same as parent's)." end child1_pid = fork do puts "Child 1 pid is #{Process.pid}" Process.setpgid(0, Process.pid) child1_pgid = Process.getpgid(Process.pid) puts "Child 1 process group ID is #{child1_pgid} (different from parent's)." sleep 3 # To force child 1 to exit later than child 0 exit. end child_pids = [child0_pid, child1_pid] retrieved_pid = Process.wait(-1) puts child_pids.include?(retrieved_pid) retrieved_pid = Process.wait(-1) puts child_pids.include?(retrieved_pid)
输出
Parent process group ID is 228736. Child 0 pid is 228758 Child 0 process group ID is 228736 (same as parent's). Child 1 pid is 228759 Child 1 process group ID is 228759 (different from parent's). true true
-
小于
-1:等待进程组 ID 为-pid的任何子进程。parent_pgpid = Process.getpgid(Process.pid) puts "Parent process group ID is #{parent_pgpid}." child0_pid = fork do puts "Child 0 pid is #{Process.pid}" child0_pgid = Process.getpgid(Process.pid) puts "Child 0 process group ID is #{child0_pgid} (same as parent's)." end child1_pid = fork do puts "Child 1 pid is #{Process.pid}" Process.setpgid(0, Process.pid) child1_pgid = Process.getpgid(Process.pid) puts "Child 1 process group ID is #{child1_pgid} (different from parent's)." end sleep 1 retrieved_pid = Process.wait(-child1_pid) puts "Process.wait(-child1_pid) returned pid #{retrieved_pid}, which is child 1 pid." begin Process.wait(-child1_pid) rescue Errno::ECHILD => x puts "Raised #{x.class}, because there's no longer a child with process group id #{child1_pid}." end
输出
Parent process group ID is 230083. Child 0 pid is 230108 Child 0 process group ID is 230083 (same as parent's). Child 1 pid is 230109 Child 1 process group ID is 230109 (different from parent's). Process.wait(-child1_pid) returned pid 230109, which is child 1 pid. Raised Errno::ECHILD, because there's no longer a child with process group id 230109.
参数 flags 应给出为以下常量之一,或两者的逻辑 OR:
-
Process::WNOHANG:如果没有可用的子进程,则不阻塞。 -
Process::WUNTRACED:即使尚未报告,也可能返回已停止的子进程。
并非所有标志在所有平台上都可用。
如果不存在合适的子进程,则引发 Errno::ECHILD。
Not available on all platforms.
Process.waitpid 是 Process.wait 的别名。
Source
static VALUE
proc_wait2(int argc, VALUE *argv, VALUE _)
{
VALUE pid = proc_wait(argc, argv);
if (NIL_P(pid)) return Qnil;
return rb_assoc_new(pid, rb_last_status_get());
}
与 Process.waitpid 类似,但返回一个包含子进程 pid 和 Process::Status status 的数组。
pid = Process.spawn('ruby', '-e', 'exit 13') # => 309581 Process.wait2(pid) # => [309581, #<Process::Status: pid 309581 exit 13>]
Process.waitpid2 是 Process.wait2 的别名。
Source
static VALUE
proc_warmup(VALUE _)
{
RB_VM_LOCKING() {
rb_gc_prepare_heap();
}
return Qtrue;
}
通知 Ruby 虚拟机引导序列已完成,现在是优化应用程序的好时机。这对于长时间运行的应用程序很有用。
此方法应在应用程序引导结束时调用。如果应用程序使用预分叉模型部署,则应在第一次分叉之前在原始进程中调用 Process.warmup。
执行的实际优化完全是实现特定的,并且将来可能会在不通知的情况下进行更改。
在 CRuby 上,Process.warmup:
-
执行主要
GC。 -
压缩堆。
-
将所有存活对象提升到旧代。
-
预先计算所有字符串的代码范围。
-
释放所有空的堆页,并将可分配页计数器增加已释放页的数量。
-
如果可用,调用
malloc_trim来释放空的 malloc 页。