模块 Process

模块 Process 表示底层操作系统中的一个进程。它的方法支持管理当前进程及其子进程。

进程创建

以下每个方法都会在一个新进程或子 Shell 中执行给定的命令,或在新进程和/或子 Shell 中执行多个命令。进程或子 Shell 的选择取决于命令的形式;请参阅参数 command_line 或 exe_path

此外

  • 方法 Kernel#system 在子 Shell 中执行给定的命令行(字符串);返回 truefalsenil

  • 方法 Kernel#` 在子 Shell 中执行给定的命令行(字符串);返回其 $stdout 字符串。

  • 模块 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_lineexe_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 尝试像 system(3) 一样,在类 Unix 系统上使用 /bin/sh 运行它。

    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,则每个 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:在新进程中创建进程组,其 ID 为 pgid

仅在 Windows 上,使用执行选项 :new_pgroup,值为 true,为新进程创建新的进程组。

资源限制

使用执行选项设置资源限制。

这些选项的键是 :rlimit_resource_name 形式的符号,其中 resource_name 是方法 Process.setrlimit 中描述的字符串资源名称的小写形式。例如,键 :rlimit_cpu 对应于资源限制 'CPU'

此类键的值是以下之一

  • 整数,指定当前限制和最大限制。

  • 包含整数的 2 元素数组,指定当前限制和最大限制。

文件描述符继承

默认情况下,新进程从父进程继承文件描述符。

使用执行选项 :close_others => true 修改该继承,方法是关闭未以其他方式重定向的非标准 fd(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*

本节内容

当前进程获取器

  • ::argv0: 返回进程名称,作为冻结字符串。

  • ::egid: 返回有效组 ID。

  • ::euid: 返回有效用户 ID。

  • ::getpgrp: 返回进程组 ID。

  • ::getrlimit: 返回资源限制。

  • ::gid: 返回(真实)组 ID。

  • ::pid: 返回进程 ID。

  • ::ppid: 返回父进程的进程 ID。

  • ::uid: 返回(真实)用户 ID。

当前进程设置器

  • ::egid=: 设置有效组 ID。

  • ::euid=: 设置有效用户 ID。

  • ::gid=: 设置(真实)组 ID。

  • ::setproctitle: 设置进程标题。

  • ::setpgrp: 将进程的进程组 ID 设置为零。

  • ::setrlimit: 设置资源限制。

  • ::setsid: 将进程建立为新的会话和进程组领导者,且没有控制 tty。

  • ::uid=: 设置用户 ID。

当前进程执行

  • ::abort: 立即终止进程。

  • ::daemon: 将进程从其控制终端分离,并在后台作为系统守护进程继续运行。

  • ::exec: 通过运行给定的外部命令来替换进程。

  • ::exit: 通过引发异常 SystemExit(可能被捕获)来启动进程终止。

  • ::exit!: 立即退出进程。

  • ::warmup: 通知 Ruby 虚拟机应用程序的启动序列已完成,并且 VM 可以开始优化应用程序。

子进程

  • ::detach: 防止子进程成为僵尸进程。

  • ::fork: 创建子进程。

  • ::kill: 向进程发送给定的信号。

  • ::spawn: 创建子进程。

  • ::wait, ::waitpid: 等待子进程退出;返回其进程 ID。

  • ::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 对象,其中包含当前进程及其子进程的时间。

常量

CLOCK_BOOTTIME

请参阅 Process.clock_gettime

CLOCK_BOOTTIME_ALARM

请参阅 Process.clock_gettime

CLOCK_MONOTONIC

请参阅 Process.clock_gettime

CLOCK_MONOTONIC_COARSE

请参阅 Process.clock_gettime

CLOCK_MONOTONIC_FAST

请参阅 Process.clock_gettime

CLOCK_MONOTONIC_PRECISE

请参阅 Process.clock_gettime

CLOCK_MONOTONIC_RAW

请参阅 Process.clock_gettime

CLOCK_MONOTONIC_RAW_APPROX

请参阅 Process.clock_gettime

CLOCK_PROCESS_CPUTIME_ID

请参阅 Process.clock_gettime

CLOCK_PROF

请参阅 Process.clock_gettime

CLOCK_REALTIME

请参阅 Process.clock_gettime

CLOCK_REALTIME_ALARM

请参阅 Process.clock_gettime

CLOCK_REALTIME_COARSE

请参阅 Process.clock_gettime

CLOCK_REALTIME_FAST

请参阅 Process.clock_gettime

CLOCK_REALTIME_PRECISE

请参阅 Process.clock_gettime

CLOCK_SECOND

请参阅 Process.clock_gettime

CLOCK_TAI

请参阅 Process.clock_gettime

CLOCK_THREAD_CPUTIME_ID

请参阅 Process.clock_gettime

CLOCK_UPTIME

请参阅 Process.clock_gettime

CLOCK_UPTIME_FAST

请参阅 Process.clock_gettime

CLOCK_UPTIME_PRECISE

请参阅 Process.clock_gettime

CLOCK_UPTIME_RAW

请参阅 Process.clock_gettime

CLOCK_UPTIME_RAW_APPROX

请参阅 Process.clock_gettime

CLOCK_VIRTUAL

请参阅 Process.clock_gettime

PRIO_PGRP

请参阅 Process.setpriority

PRIO_PROCESS

请参阅 Process.setpriority

PRIO_USER

请参阅 Process.setpriority

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

指定一个值,该值比此进程可以打开的最大文件描述符数大 1。

有关详细信息,请参阅系统 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

请参阅 Process.setrlimit

RLIM_SAVED_CUR

请参阅 Process.setrlimit

RLIM_SAVED_MAX

请参阅 Process.setrlimit

WNOHANG

请参阅 Process.wait

WUNTRACED

请参阅 Process.wait

公共类方法

_fork → integer 点击以切换源

fork 的内部 API。不要直接调用此方法。目前,这是通过 Kernel#forkProcess.forkIO.popen 使用 "-" 调用的。

此方法不适用于临时代码,而是适用于应用程序监视库。您可以通过重写此方法,在 fork 事件之前和之后添加自定义代码。

注意:Process.daemon 可能会使用 fork(2) 实现,但不会通过此方法。因此,根据您挂钩到此方法的原因,您可能还需要挂钩到该方法。有关此的更详细讨论,请参阅此问题

VALUE
rb_proc__fork(VALUE _obj)
{
    rb_pid_t pid = proc_fork_pid();
    return PIDT2NUM(pid);
}
abort 点击以切换源
abort(msg = nil)

立即终止执行,实际上是通过调用 Kernel.exit(false)

如果给出了字符串参数 msg,则在终止之前将其写入 STDERR;否则,如果引发了异常,则打印其消息和回溯。

static VALUE
f_abort(int c, const VALUE *a, VALUE _)
{
    rb_f_abort(c, a);
    UNREACHABLE_RETURN(Qnil);
}
argv0 → frozen_string 点击以切换源

返回正在执行的脚本的名称。该值不受为 $0 分配新值的影响。

此方法首次出现在 Ruby 2.1 中,作为一种无需全局变量即可获取脚本名称的方法。

static VALUE
proc_argv0(VALUE process)
{
    return rb_orig_progname;
}
clock_getres(clock_id, unit = :float_second) → number 点击以切换源

返回由 POSIX 函数 clock_getres() 确定的时钟分辨率

Process.clock_getres(:CLOCK_REALTIME) # => 1.0e-09

有关 clock_idunit 的值,请参阅 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

准确性:请注意,由于底层错误,返回的分辨率在某些平台上可能不准确。在使用 ARM 处理器或使用虚拟化时,已报告各种时钟(包括 Linux、macOS、BSD 或 AIX 平台上的 :CLOCK_MONOTONIC:CLOCK_MONOTONIC_RAW)的分辨率不准确。

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);
    }
}
clock_gettime(clock_id, unit = :float_second) → number 点击以切换源

返回由 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(单一 UNIX 规范)。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:使用 POSIX 定义的 times() 的结果值,因此

      成功完成时,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_SECCLOCKS_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() 返回纳秒数。Float 对象 (IEEE 754 double) 不足以表示 :CLOCK_REALTIME 的返回值。如果需要确切的纳秒值,请使用 :nanosecond 作为 unit

返回值的原点(时间零)取决于系统,并且可能例如是系统启动时间、进程启动时间、Epoch 等。

:CLOCK_REALTIME 中的原点定义为 Epoch:1970-01-01 00:00:00 UTC;某些系统计算闰秒,而另一些系统不计算,因此结果可能因系统而异。

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);
}
daemon(nochdir = nil, noclose = nil) → 0 点击以切换源代码

将当前进程从其控制终端分离,并在后台作为系统守护进程运行;返回零。

默认情况下

  • 将当前工作目录更改为根目录。

  • 将 $stdin、$stdout 和 $stderr 重定向到空设备。

如果可选参数 nochdirtrue,则不更改当前工作目录。

如果可选参数 noclosetrue,则不重定向 $stdin、$stdout 或 $stderr。

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);
}
detach(pid) → thread 点击以切换源代码

避免子进程成为 僵尸进程 的可能性。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
static VALUE
proc_detach(VALUE obj, VALUE pid)
{
    return rb_detach_process(NUM2PIDT(pid));
}
egid → integer 点击以切换源代码
Process::GID.eid → integer
Process::Sys.geteid → integer

返回当前进程的有效组 ID

Process.egid # => 500

并非在所有平台上都可用。

static VALUE
proc_getegid(VALUE obj)
{
    rb_gid_t egid = getegid();

    return GIDT2NUM(egid);
}
egid = new_egid → new_egid 点击以切换源代码

设置当前进程的有效组 ID。

并非在所有平台上都可用。

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;
}
euid → integer 点击以切换源代码
Process::UID.eid → integer
Process::Sys.geteuid → integer

返回当前进程的有效用户 ID。

Process.euid # => 501
static VALUE
proc_geteuid(VALUE obj)
{
    rb_uid_t euid = geteuid();
    return UIDT2NUM(euid);
}
euid = new_euid → new_euid 点击以切换源代码

设置当前进程的有效用户 ID。

并非在所有平台上都可用。

static VALUE
proc_seteuid_m(VALUE mod, VALUE euid)
{
    check_uid_switch();
    proc_seteuid(OBJ2UID(euid));
    return euid;
}
exec([env, ] command_line, options = {}) 点击以切换源代码
exec([env, ] exe_path, *args, options = {})

通过执行以下操作之一来替换当前进程

  • 将字符串 command_line 传递给 shell。

  • 调用位于 exe_path 的可执行文件。

如果使用不受信任的输入调用此方法,则该方法可能存在安全漏洞;请参阅 命令注入

新进程是使用 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

如果新进程无法执行,则引发异常。

static VALUE
f_exec(int c, const VALUE *a, VALUE _)
{
    rb_f_exec(c, a);
    UNREACHABLE_RETURN(Qnil);
}
exit(status = true) 点击以切换源代码
exit(status = true)

通过引发 SystemExit 来启动 Ruby 脚本的终止;该异常可能会被捕获。将退出状态 status 返回到底层操作系统。

参数 status 的值 truefalse 分别表示成功和失败;整数值的含义取决于系统。

示例

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 会执行任何 at-exit 过程(请参阅 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.
static VALUE
f_exit(int c, const VALUE *a, VALUE _)
{
    rb_f_exit(c, a);
    UNREACHABLE_RETURN(Qnil);
}
exit!(status = false) 点击以切换源代码
exit!(status = false)

立即退出进程;不调用任何退出处理程序。将退出状态 status 返回到底层操作系统。

Process.exit!(true)

参数 status 的值 truefalse 分别表示成功和失败;整数值的含义取决于系统。

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);
}
fork { ... } → integer or nil 点击以切换源代码
fork → integer 或 nil

创建子进程。

如果给定块,则在子进程中运行该块;在块退出时,子进程终止,状态为零

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

为了避免僵尸进程,父进程应调用以下任一方法

调用 fork 的线程是创建的子进程中的唯一线程;fork 不会复制其他线程。

请注意,方法 fork 在某些平台上可用,但在其他平台上不可用

Process.respond_to?(:fork) # => true # Would be false on some.

如果不可用,可以使用 ::spawn 而不是 fork

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);
}
getpgid(pid) → integer 点击以切换源代码
Returns the process group ID for the given process ID +pid+:

  Process.getpgid(Process.ppid) # => 25527

并非在所有平台上都可用。

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);
}
getpgrp → integer 点击以切换源代码

返回当前进程的进程组 ID

Process.getpgid(0) # => 25527
Process.getpgrp    # => 25527
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
}
getpriority(kind, id) → integer 点击以切换源代码

返回指定进程、进程组或用户的调度优先级。

参数 kind 是以下之一

参数 id 是进程、进程组或用户的 ID;零指定 kind 的当前 ID。

示例

Process.getpriority(Process::PRIO_USER, 0)    # => 19
Process.getpriority(Process::PRIO_PROCESS, 0) # => 19

并非在所有平台上都可用。

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);
}
getrlimit(resource) → [cur_limit, max_limit] 点击以切换源代码

返回给定 resource 的当前(软)限制和最大(硬)限制的 2 元素数组。

参数 resource 指定要返回其限制的资源;请参阅 Process.setrlimit

每个返回值 cur_limitmax_limit 都是整数;请参阅 Process.setrlimit

示例

Process.getrlimit(:CORE) # => [0, 18446744073709551615]

请参阅 Process.setrlimit

并非在所有平台上都可用。

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));
}
getsid(pid = nil) → integer 点击以切换源代码

返回给定进程 ID pid 的会话 ID,如果未给出 pid,则返回当前进程的会话 ID

Process.getsid                # => 27422
Process.getsid(0)             # => 27422
Process.getsid(Process.pid()) # => 27422

并非在所有平台上都可用。

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);
}
gid → integer 点击以切换源代码
Process::GID.rid → integer
Process::Sys.getgid → integer

返回当前进程的(真实)组 ID

Process.gid # => 1000
static VALUE
proc_getgid(VALUE obj)
{
    rb_gid_t gid = getgid();
    return GIDT2NUM(gid);
}
gid = new_gid → new_gid 点击以切换源代码

将当前进程的组 ID 设置为 new_gid

Process.gid = 1000 # => 1000
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);
}
groups → array 点击以切换源代码

返回当前进程的辅助组访问列表中的组 ID 数组

Process.groups # => [4, 24, 27, 30, 46, 122, 135, 136, 1000]

返回的数组的这些属性是与系统相关的

  • 数组是否(以及如何)排序。

  • 数组是否包含有效组 ID。

  • 数组是否包含重复的组 ID。

  • 数组大小是否超过 Process.maxgroups 的值。

使用此调用获取排序且唯一的数组

Process.groups.uniq.sort
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;
}
groups = new_groups → new_groups 点击以切换源代码

将辅助组访问列表设置为给定的组 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]
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);
}
initgroups(username, gid) → array 点击以切换源代码

设置辅助组访问列表;新列表包括

  • 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]

并非在所有平台上都可用。

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);
}
kill(signal, *ids) → count 点击以切换源代码

ids 指定的每个进程发送信号(必须至少指定一个 ID);返回发送的信号数。

对于每个给定的 id,如果 id

  • 正数,则将信号发送到进程 ID 为 id 的进程。

  • 零,则将信号发送到当前进程组中的所有进程。

  • 负数,则将信号发送到与系统相关的进程集合。

参数 signal 指定要发送的信号;参数可以是

  • 整数信号编号:例如,-29029

  • 信号名称(字符串),带或不带前导 '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。

在最后两种情况下,可能已向某些进程发送了信号。

static VALUE
proc_rb_f_kill(int c, const VALUE *v, VALUE _)
{
    return rb_f_kill(c, v);
}
last_status → Process::Status 或 nil 点击以切换源代码

返回一个 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>
static VALUE
proc_s_last_status(VALUE mod)
{
    return rb_last_status_get();
}
maxgroups → integer 点击以切换源代码

返回辅助组访问列表中允许的最大组 ID 数

Process.maxgroups # => 32
static VALUE
proc_getmaxgroups(VALUE obj)
{
    return INT2FIX(maxgroups());
}
maxgroups = new_max → new_max 点击以切换源代码

设置辅助组访问列表中允许的最大组 ID 数。

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);
}
pid → integer 点击以切换源代码

返回当前进程的进程 ID

Process.pid # => 15668
static VALUE
proc_get_pid(VALUE _)
{
    return get_pid();
}
ppid → integer 点击以切换源代码

返回当前进程的父进程的进程 ID

puts "Pid is #{Process.pid}."
fork { puts "Parent pid is #{Process.ppid}." }

输出

Pid is 271290.
Parent pid is 271290

在某些平台上可能不会返回可信的值。

static VALUE
proc_get_ppid(VALUE _)
{
    return get_ppid();
}
setpgid(pid, pgid) → 0 点击以切换源代码

将进程 ID 为 pid 的进程的进程组 ID 设置为 pgid

并非在所有平台上都可用。

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);
}
setpgrp → 0 点击以切换源代码

等效于 setpgid(0, 0)

并非在所有平台上都可用。

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);
}
setpriority(kind, integer, priority) → 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

并非在所有平台上都可用。

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);
}
setproctitle(string) → string 点击以切换源代码

设置 ps(1) 命令中显示的进程标题。并非在所有平台上都有效。无论结果如何,都不会引发异常,即使该平台不支持该功能,也不会引发 NotImplementedError

调用此方法不会影响 $0 的值。

Process.setproctitle('myapp: worker #%d' % worker_id)

此方法首次出现在 Ruby 2.1 中,作为一种无全局变量的方式来更改进程标题。

static VALUE
proc_setproctitle(VALUE process, VALUE title)
{
    return ruby_setproctitle(title);
}
setrlimit(resource, cur_limit, max_limit = cur_limit) → nil 点击以切换源代码

为给定 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)。

  • :DATAData 段(字节)(SUSv3)。

  • :FSIZEFile 大小(字节)(SUSv3)。

  • :MEMLOCK:mlock(2) 的总大小(字节)(4.4BSD、GNU/Linux)。

  • :MSGQUEUE:用于 POSIX 消息队列的分配(字节)(GNU/Linux)。

  • :NICE:进程 nice(2) 值的上限(数字)(GNU/Linux)。

  • :NOFILEFile 描述符(数字)(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_limitmax_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])

并非在所有平台上都可用。

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;
}
setsid → integer 点击以切换源代码

将当前进程建立为新的会话和进程组领导者,没有控制 tty;返回会话 ID

Process.setsid # => 27422

并非在所有平台上都可用。

static VALUE
proc_setsid(VALUE _)
{
    rb_pid_t pid;

    pid = setsid();
    if (pid < 0) rb_sys_fail(0);
    return PIDT2NUM(pid);
}
spawn([env, ] command_line, options = {}) → pid 点击以切换源代码
spawn([env, ] exe_path, *args, options = {}) → pid

通过在该进程中执行以下操作之一来创建新的子进程

  • 将字符串 command_line 传递给 shell。

  • 调用位于 exe_path 的可执行文件。

如果使用不受信任的输入调用此方法,则该方法可能存在安全漏洞;请参阅 命令注入

返回新进程的进程 ID (pid),而不等待其完成。

为了避免僵尸进程,父进程应调用以下任一方法

新进程是使用 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

如果新进程无法执行,则引发异常。

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
}
times → process_tms 点击以切换源代码

返回一个 Process::Tms 结构,其中包含当前进程及其子进程的用户和系统 CPU 时间

Process.times
# => #<struct Process::Tms utime=55.122118, stime=35.533068, cutime=0.0, cstime=0.002846>

精度是平台定义的。

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;
}
uid → integer 点击以切换源代码
Process::UID.rid → integer
Process::Sys.getuid → integer

返回当前进程的(真实)用户 ID。

Process.uid # => 1000
static VALUE
proc_getuid(VALUE obj)
{
    rb_uid_t uid = getuid();
    return UIDT2NUM(uid);
}
uid = new_uid → new_uid 点击以切换源代码

将当前进程的(用户)用户 ID 设置为 new_uid

Process.uid = 1000 # => 1000

并非在所有平台上都可用。

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;
}
wait(pid = -1, flags = 0) → integer 点击以切换源代码

等待合适的子进程退出,返回其进程 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 应作为以下常量之一给出,或作为两者的逻辑或给出

并非所有标志在所有平台上都可用。

如果没有合适的子进程,则引发 Errno::ECHILD。

并非在所有平台上都可用。

Process.waitpidProcess.wait 的别名。

static VALUE
proc_m_wait(int c, VALUE *v, VALUE _)
{
    return proc_wait(c, v);
}
wait2(pid = -1, flags = 0) → [pid, status] 点击以切换源代码

类似于 Process.waitpid,但返回一个包含子进程 pidProcess::Status status 的数组。

pid = Process.spawn('ruby', '-e', 'exit 13') # => 309581
Process.wait2(pid)
# => [309581, #<Process::Status: pid 309581 exit 13>]

Process.waitpid2Process.wait2 的别名。

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());
}
waitall → array 点击以切换源

等待所有子进程结束,返回一个包含二维数组的数组;每个子数组包含一个已回收子进程的整数 pid 和 Process::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>]]
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;
}
wait(pid = -1, flags = 0) → integer 点击以切换源代码

等待合适的子进程退出,返回其进程 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 应作为以下常量之一给出,或作为两者的逻辑或给出

并非所有标志在所有平台上都可用。

如果没有合适的子进程,则引发 Errno::ECHILD。

并非在所有平台上都可用。

Process.waitpidProcess.wait 的别名。

static VALUE
proc_m_wait(int c, VALUE *v, VALUE _)
{
    return proc_wait(c, v);
}
wait2(pid = -1, flags = 0) → [pid, status] 点击以切换源代码

类似于 Process.waitpid,但返回一个包含子进程 pidProcess::Status status 的数组。

pid = Process.spawn('ruby', '-e', 'exit 13') # => 309581
Process.wait2(pid)
# => [309581, #<Process::Status: pid 309581 exit 13>]

Process.waitpid2Process.wait2 的别名。

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());
}
warmup → true 点击以切换源

通知 Ruby 虚拟机引导序列已完成,现在是优化应用程序的好时机。这对于长时间运行的应用程序非常有用。

此方法应在应用程序引导结束时调用。如果应用程序使用预先分叉模型部署,则应在首次分叉之前的原始进程中调用 Process.warmup

实际执行的优化完全取决于实现,并且可能在未来发生更改,恕不另行通知。

在 CRuby 上,Process.warmup

  • 执行一次主要的 GC

  • 压缩堆。

  • 将所有存活对象提升到老年代。

  • 预先计算所有字符串的编码范围。

  • 释放所有空堆页面,并将可分配页面计数器增加释放的页面数。

  • 如果可用,则调用 malloc_trim 以释放空的 malloc 页面。

static VALUE
proc_warmup(VALUE _)
{
    RB_VM_LOCK_ENTER();
    rb_gc_prepare_heap();
    RB_VM_LOCK_LEAVE();
    return Qtrue;
}