Spec-Zone.ru › Ruby 4.0

модуль Process

Модуль Process представляет процесс в базовой операционной системе. Его методы позволяют управлять текущим процессом и его дочерними процессами.

Создание процессов

Каждый из следующих методов выполняет заданную команду в новом процессе или подоболочке либо несколько команд в новых процессах и/или подоболочках. Выбор между процессом и подоболочкой зависит от формы команды; см. раздел Аргумент command_line или exe_path.

  • Process.spawn, Kernel#spawn: выполняет команду; возвращает новый pid, не дожидаясь завершения.

  • Process.exec: заменяет текущий процесс, выполняя команду.

Кроме того:

  • Метод Kernel#system выполняет заданную командную строку (строку) в подоболочке; возвращает true, false или nil.

  • Метод Kernel#` выполняет заданную командную строку (строку) в подоболочке; возвращает строку $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#update с аргументом env: каждая указанная переменная среды создаётся или обновляется (если значение не равно nil) либо удаляется (если значение равно nil).

Однако некоторые изменения вызывающего процесса могут сохраниться, если новый процесс не запустится. Например, жёсткие ограничения ресурсов не восстанавливаются.

Аргумент command_line или exe_path

Обязательный строковый аргумент должен быть одним из следующих:

  • command_line, если он начинается с зарезервированного слова оболочки или специальной встроенной команды либо содержит один или несколько метасимволов.

  • exe_path в противном случае.

Аргумент command_line

Строковый аргумент command_line — это командная строка, передаваемая оболочке; она должна начинаться с зарезервированного слова оболочки или специальной встроенной команды либо содержать метасимволы:

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

Подробную информацию об оболочке см. в разделе Оболочка выполнения.

Аргумент 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 выше, поэтому необходимо заключить всё имя команды в кавычки или экранировать его с помощью оболочки способом, зависящим от платформы, либо использовать приведённую ниже форму массива.

    Если exe_path не содержит разделителей пути, исполняемый файл ищется в каталогах, указанных переменной среды PATH. Значение слова «исполняемый» здесь зависит от платформы.

    Даже если файл считается «исполняемым», его содержимое может иметь неподходящий формат исполняемого файла. В этом случае Ruby пытается запустить его с помощью /bin/sh в Unix-подобной системе, как это делает 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 не содержит метасимволов оболочки, кроме пробелов и табуляций, или задан exe_path, Ruby вызывает исполняемый файл напрямую. В этой форме оболочка не используется:

spawn("doesnt_exist")       # Raises Errno::ENOENT
spawn("doesnt_exist", "\n") # Raises Errno::ENOENT

spawn("doesnt_exist\n")     # => false
# sh: 1: doesnot_exist: not found

Сообщение об ошибке выводится оболочкой и зависит от системы.

Если после exe_path указаны один или несколько args, каждый из них является аргументом или параметром, передаваемым исполняемому файлу:

Пример:

system('echo', '<', 'C*', '|', '$SHELL', '>')   # => true

Вывод:

< C* | $SHELL >

Однако в Windows существуют исключения. См. раздел Оболочка выполнения в Windows.

Чтобы вызвать путь с пробелами без аргументов и без оболочки, необходимо использовать массив из 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), указывающий источник, или массив файловых дескрипторов, указывающий несколько источников.

Целочисленный исходный дескриптор можно указать как:

  • n: указывает файловый дескриптор n.

Для файловых дескрипторов предусмотрены следующие сокращённые обозначения:

  • :in: указывает файловый дескриптор 0 (STDIN).

  • :out: указывает файловый дескриптор 1 (STDOUT).

  • :err: указывает файловый дескриптор 2 (STDERR).

Значение, заданное для источника, должно быть одним из следующих:

  • n: перенаправляет на fd n в родительском процессе.

  • filepath: перенаправляет ввод или вывод в файл по адресу filepath с помощью open(filepath, mode, 0644), где mode — это 'r' для источника :in или 'w' для источника :out или :err.

  • [filepath]: перенаправляет ввод из файла по адресу filepath с помощью open(filepath, 'r', 0644).

  • [filepath, mode]: перенаправляет ввод или вывод в файл по адресу filepath с помощью open(filepath, mode, 0644).

  • [filepath, mode, perm]: перенаправляет ввод или вывод в файл по адресу filepath с помощью open(filepath, mode, perm).

  • [: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 и выше), которые не перенаправляются иным образом.

Оболочка выполнения

В Unix-подобной системе вызывается оболочка /bin/sh; вся строка command_line передаётся в качестве аргумента параметра оболочки -c.

Оболочка выполняет обычную подстановку в командной строке:

Пример:

system('echo $SHELL: C*') # => true

Вывод:

/bin/bash: CONTRIBUTING.md COPYING COPYING.ja

Оболочка выполнения в Windows

В Windows вызываемая оболочка определяется переменной среды RUBYSHELL, если она задана, или COMSPEC в противном случае; вся строка command_line передаётся как аргумент параметра -c для RUBYSHELL, а также /bin/sh и параметра /c для COMSPEC. Оболочка вызывается автоматически в следующих случаях:

  • Команда является встроенной командой cmd.exe, например echo.

  • Исполняемый файл является пакетным файлом; его имя заканчивается на .bat или .cmd.

Обратите внимание: даже при вызове в форме exe_path команда всё равно будет вызвана в форме command_line, поскольку cmd.exe принимает имя скрипта, как /bin/sh, а работает только с параметром /c.

Стандартная оболочка cmd.exe выполняет подстановку переменных среды, но не поддерживает подстановку шаблонов имён файлов:

Пример:

system("echo %COMSPEC%: C*")' # => true

Вывод:

C:\WINDOWS\system32\cmd.exe: C*

Что здесь представлено

Методы получения данных о текущем процессе

  • ::argv0: возвращает имя процесса в виде замороженной строки.

  • ::egid: возвращает эффективный идентификатор группы.

  • ::euid: возвращает эффективный идентификатор пользователя.

  • ::getpgrp: возвращает идентификатор группы процессов.

  • ::getrlimit: возвращает ограничение ресурса.

  • ::gid: возвращает (реальный) идентификатор группы.

  • ::pid: возвращает идентификатор процесса.

  • ::ppid: возвращает идентификатор родительского процесса.

  • ::uid: возвращает (реальный) идентификатор пользователя.

Методы изменения параметров текущего процесса

  • ::egid=: задаёт эффективный идентификатор группы.

  • ::euid=: задаёт эффективный идентификатор пользователя.

  • ::gid=: задаёт (реальный) идентификатор группы.

  • ::setproctitle: задаёт заголовок процесса.

  • ::setpgrp: устанавливает идентификатор группы процессов процесса равным нулю.

  • ::setrlimit: задаёт ограничение ресурса.

  • ::setsid: делает процесс лидером нового сеанса и новой группы процессов без управляющего терминала.

  • ::uid=: задаёт идентификатор пользователя.

Выполнение текущего процесса

  • ::abort: немедленно завершает процесс.

  • ::daemon: отсоединяет процесс от управляющего терминала и продолжает его выполнение в фоновом режиме в качестве системной службы.

  • ::exec: заменяет процесс, запуская заданную внешнюю команду.

  • ::exit: инициирует завершение процесса, вызывая исключение SystemExit (которое можно перехватить).

  • ::exit!: немедленно завершает процесс.

  • ::warmup: уведомляет виртуальную машину Ruby о завершении последовательности загрузки приложения и о том, что ВМ может начать оптимизацию приложения.

Дочерние процессы

  • ::detach: предотвращает превращение дочернего процесса в зомби.

  • ::fork: создаёт дочерний процесс.

  • ::kill: отправляет заданный сигнал процессам.

  • ::spawn: создаёт дочерний процесс.

  • ::wait, ::waitpid: ожидает завершения дочернего процесса; возвращает его идентификатор.

  • ::wait2, ::waitpid2: ожидает завершения дочернего процесса; возвращает его идентификатор и статус.

  • ::waitall: ожидает завершения всех дочерних процессов; возвращает их идентификаторы и статусы.

Группы процессов

  • ::getpgid: возвращает идентификатор группы процессов для процесса.

  • ::getpriority: возвращает приоритет планирования процесса, группы процессов или пользователя.

  • ::getsid: возвращает идентификатор сеанса для процесса.

  • ::groups: возвращает массив идентификаторов групп из списка дополнительных групп доступа для этого процесса.

  • ::groups=: задаёт список дополнительных групп доступа, используя переданный массив идентификаторов групп.

  • ::initgroups: инициализирует список дополнительных групп доступа.

  • ::last_status: возвращает статус последнего выполненного дочернего процесса в текущем потоке.

  • ::maxgroups: возвращает максимальное количество идентификаторов групп, допустимое в списке дополнительных групп доступа.

  • ::maxgroups=: задаёт максимальное количество идентификаторов групп, допустимое в списке дополнительных групп доступа.

  • ::setpgid: задаёт идентификатор группы процессов.

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

Максимальный размер файла core.

Подробности см. в системном справочном руководстве getrlimit(2).

RLIMIT_CPU

Ограничение времени процессора в секундах.

Подробности см. в системном справочном руководстве getrlimit(2).

RLIMIT_DATA

Максимальный размер сегмента данных процесса.

Подробности см. в системном справочном руководстве getrlimit(2).

RLIMIT_FSIZE

Максимальный размер файлов, которые может создать процесс.

Подробности см. в системном справочном руководстве getrlimit(2).

RLIMIT_MEMLOCK

Максимальное количество байтов памяти, которое можно заблокировать в ОЗУ.

Подробности см. в системном справочном руководстве getrlimit(2).

RLIMIT_MSGQUEUE

Задаёт ограничение на количество байтов, выделяемых для очередей сообщений POSIX для реального идентификатора пользователя вызывающего процесса.

Подробности см. в системном справочном руководстве getrlimit(2).

RLIMIT_NICE

Задаёт верхний предел, до которого можно повысить значение nice процесса.

Подробности см. в системном справочном руководстве getrlimit(2).

RLIMIT_NOFILE

Задаёт значение, на единицу превышающее максимальный номер файлового дескриптора, который может быть открыт этим процессом.

Подробности см. в системном справочном руководстве getrlimit(2).

RLIMIT_NPROC

Максимальное количество процессов, которое может быть создано для реального идентификатора пользователя вызывающего процесса.

Подробности см. в системном справочном руководстве getrlimit(2).

RLIMIT_NPTS

Максимальное количество псевдотерминалов, которое может быть создано для реального идентификатора пользователя вызывающего процесса.

Подробности см. в системном справочном руководстве getrlimit(2).

RLIMIT_RSS

Задаёт ограничение (в страницах) на резидентный набор процесса.

Подробности см. в системном справочном руководстве getrlimit(2).

RLIMIT_RTPRIO

Задаёт верхний предел приоритета реального времени, который можно установить для этого процесса.

Подробности см. в системном справочном руководстве getrlimit(2).

RLIMIT_RTTIME

Задаёт ограничение на время процессора, которое может использовать этот процесс при планировании по политике реального времени.

Подробности см. в системном справочном руководстве getrlimit(2).

RLIMIT_SBSIZE

Максимальный размер буфера сокета.

RLIMIT_SIGPENDING

Задаёт ограничение на количество сигналов, которые могут находиться в очереди для реального идентификатора пользователя вызывающего процесса.

Подробности см. в системном справочном руководстве 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 Показать исходный код
VALUE
rb_proc__fork(VALUE _obj)
{
    rb_pid_t pid = proc_fork_pid();
    return PIDT2NUM(pid);
}

Внутренний API для fork. Не вызывайте этот метод напрямую. В настоящее время он вызывается через Kernel#fork, Process.fork и IO.popen с помощью "-".

Этот метод предназначен не для обычного кода, а для библиотек мониторинга приложений. Можно добавить пользовательский код до и после событий fork, переопределив этот метод.

Примечание: Process.daemon может быть реализован с помощью fork(2), НО при этом этот метод не вызывается. Поэтому, в зависимости от причины перехвата вызова этого метода, возможно, потребуется также перехватить вызов того метода. Более подробно это обсуждается в этой задаче.

abort Показать исходный код
abort(msg = nil)
static VALUE
f_abort(int c, const VALUE *a, VALUE _)
{
    rb_f_abort(c, a);
    UNREACHABLE_RETURN(Qnil);
}

Немедленно завершает выполнение, фактически вызывая Kernel.exit(false).

Если задан строковый аргумент msg, перед завершением он записывается в STDERR; в противном случае, если было возбуждено исключение, выводятся его сообщение и трассировка стека.

argv0 → frozen_string Показать исходный код
static VALUE
proc_argv0(VALUE process)
{
    return rb_orig_progname;
}

Возвращает имя выполняемого скрипта. Значение не зависит от присвоения нового значения переменной $0.

Этот метод впервые появился в Ruby 2.1 как способ получить имя скрипта без использования глобальной переменной.

clock_getres(clock_id, unit = :float_second) → number Показать исходный код
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

Помимо значений unit, поддерживаемых в Process.clock_gettime, этот метод поддерживает :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

Точность: обратите внимание, что на некоторых платформах возвращаемое разрешение может быть неточным из-за ошибок в нижележащих компонентах. Сообщалось о неточных значениях разрешения для различных часов, включая :CLOCK_MONOTONIC и :CLOCK_MONOTONIC_RAW, в Linux, macOS, BSD и AIX при использовании процессоров ARM или виртуализации.

clock_gettime(clock_id, unit = :float_second) → number Показать исходный код
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: используется gettimeofday(), определённая в SUS (устарела в SUSv4). Разрешение составляет 1 микросекунду.

    • :TIME_BASED_CLOCK_REALTIME: используется time(), определённая в ISO C. Разрешение составляет 1 секунду.

  • Эмуляции для :CLOCK_MONOTONIC:

    • :MACH_ABSOLUTE_TIME_BASED_CLOCK_MONOTONIC: используется mach_absolute_time(), доступная в Darwin. Разрешение зависит от процессора.

    • :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: используется getrusage(), определённая в SUS. getrusage() вызывается с RUSAGE_SELF, чтобы получить время только для вызывающего процесса (без учёта времени дочерних процессов). Результат представляет собой сумму пользовательского времени (ru_utime) и системного времени (ru_stime). Разрешение составляет 1 микросекунду.

    • :TIMES_BASED_CLOCK_PROCESS_CPUTIME_ID: используется times(), определённая в POSIX. Результат представляет собой сумму пользовательского времени (tms_utime) и системного времени (tms_stime). tms_cutime и tms_cstime не учитываются, чтобы исключить время дочерних процессов. Разрешение равно такту часов. Команда «getconf CLK_TCK» показывает число тактов часов в секунду. (В старых системах число тактов в секунду определяется макросом HZ.) Если оно равно 100, разрешение составляет 10 миллисекунд.

    • :CLOCK_BASED_CLOCK_PROCESS_CPUTIME_ID: используется clock(), определённая в ISO C. Разрешение равно 1/CLOCKS_PER_SEC. CLOCKS_PER_SEC — макрос уровня C, определённый в time.h. В 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) недостаточно для представления возвращаемого значения при :CLOCK_REALTIME. Если требуется точное значение в наносекундах, используйте :nanosecond в качестве unit.

Начало отсчёта (нулевое время) возвращаемого значения зависит от системы. Например, им может быть время запуска системы, время запуска процесса, эпоха и т. д.

Начало отсчёта в :CLOCK_REALTIME определяется как эпоха: 1970-01-01 00:00:00 UTC; некоторые системы учитывают високосные секунды, а другие — нет, поэтому результат может различаться в разных системах.

daemon(nochdir = nil, noclose = nil) → 0 Показать исходный код
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 не перенаправляются.

detach(pid) → thread Показать исходный код
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
egid → integer Показать исходный код
Process::GID.eid → integer
Process::Sys.geteid → integer
static VALUE
proc_getegid(VALUE obj)
{
    rb_gid_t egid = getegid();

    return GIDT2NUM(egid);
}

Возвращает эффективный идентификатор группы текущего процесса:

Process.egid # => 500

Доступно не на всех платформах.

egid = new_egid → new_egid Показать исходный код
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
static VALUE
proc_geteuid(VALUE obj)
{
    rb_uid_t euid = geteuid();
    return UIDT2NUM(euid);
}

Возвращает эффективный идентификатор пользователя текущего процесса.

Process.euid # => 501
euid = new_euid → new_euid Показать исходный код
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 = {})
static VALUE
f_exec(int c, const VALUE *a, VALUE _)
{
    rb_f_exec(c, a);
    UNREACHABLE_RETURN(Qnil);
}

Заменяет текущий процесс одним из следующих способов:

  • Передаёт строку command_line командной оболочке.

  • Запускает исполняемый файл по пути exe_path.

При вызове с недоверенными данными этот метод может создавать уязвимости безопасности; см. раздел Инъекция команд.

Новый процесс создаётся с помощью системного вызова exec; он может унаследовать часть окружения вызывающей программы (возможно, включая открытые файловые дескрипторы).

Аргумент env, если он задан, — это хеш, влияющий на ENV нового процесса; см. раздел Окружение выполнения.

Аргумент options — это хеш параметров нового процесса; см. раздел Параметры выполнения.

Первый обязательный аргумент должен быть одним из следующих:

  • command_line, если это строка, начинающаяся с зарезервированного слова командной оболочки или специальной встроенной команды либо содержащая один или несколько метасимволов.

  • exe_path в противном случае.

Аргумент command_line

Строковый аргумент command_line — это командная строка, передаваемая командной оболочке; она должна начинаться с зарезервированного слова командной оболочки или специальной встроенной команды либо содержать метасимволы:

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

Подробные сведения о командной оболочке см. в разделе Командная оболочка выполнения.

Если новый процесс не удалось запустить, возникает исключение.

Аргумент exe_path

Аргумент exe_path может иметь одно из следующих значений:

  • Строковый путь к запускаемому исполняемому файлу.

  • Массив из двух элементов, содержащий путь к исполняемому файлу и строку, используемую в качестве имени запускаемого процесса.

Пример:

exec('/usr/bin/date')

Вывод:

Sat Aug 26 09:38:00 AM CDT 2023

Ruby запускает исполняемый файл напрямую. Эта форма не использует командную оболочку; ограничения см. в разделе Аргументы args.

exec('doesnt_exist') # Raises Errno::ENOENT

Если заданы один или несколько аргументов args, каждый из них является аргументом или параметром, передаваемым исполняемому файлу:

exec('echo', 'C*')
exec('echo', 'hello', 'world')

Вывод:

C*
hello world

Если новый процесс не удалось запустить, возникает исключение.

exit(status = true) Показать исходный код
exit(status = true)
static VALUE
f_exit(int c, const VALUE *a, VALUE _)
{
    rb_f_exit(c, a);
    UNREACHABLE_RETURN(Qnil);
}

Начинает завершение скрипта Ruby, возбуждая SystemExit; это исключение можно перехватить. Возвращает операционной системе статус завершения status.

Значения true и false аргумента status обозначают соответственно успех и неудачу; значения целых чисел зависят от системы.

Пример:

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.
exit!(status = false) Показать исходный код
exit!(status = false)
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)

Значения true и false аргумента status обозначают соответственно успех и неудачу; значения целых чисел зависят от системы.

fork { ... } → integer or nil Показать исходный код
fork → integer or nil
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);
}

Создаёт дочерний процесс.

Если передан блок, он выполняется в дочернем процессе; при выходе из блока дочерний процесс завершается со статусом zero:

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.

Если он недоступен, вместо fork можно использовать ::spawn.

getpgid(pid) → integer Показать исходный код
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

Доступно не на всех платформах.

getpgrp → integer Показать исходный код
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
}

Возвращает идентификатор группы процессов текущего процесса:

Process.getpgid(0) # => 25527
Process.getpgrp    # => 25527
getpriority(kind, id) → integer Показать исходный код
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 — это идентификатор процесса, группы процессов или пользователя; ноль указывает на текущий идентификатор для kind.

Примеры:

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

Доступно не на всех платформах.

getrlimit(resource) → [cur_limit, max_limit] Показать исходный код
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.

Аргумент resource указывает ресурс, пределы которого нужно вернуть; см. Process.setrlimit.

Каждое из возвращаемых значений cur_limit и max_limit является целым числом; см. Process.setrlimit.

Пример:

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

См. Process.setrlimit.

Доступно не на всех платформах.

getsid(pid = nil) → integer Показать исходный код
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);
}

Возвращает идентификатор сеанса указанного идентификатора процесса pid или текущего процесса, если он не задан:

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

Доступно не на всех платформах.

gid → integer Показать исходный код
Process::GID.rid → integer
Process::Sys.getgid → integer
static VALUE
proc_getgid(VALUE obj)
{
    rb_gid_t gid = getgid();
    return GIDT2NUM(gid);
}

Возвращает (реальный) идентификатор группы текущего процесса:

Process.gid # => 1000
gid = new_gid → new_gid Показать исходный код
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);
}

Устанавливает идентификатор группы текущего процесса в значение new_gid:

Process.gid = 1000 # => 1000
groups → array Показать исходный код
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;
}

Возвращает массив идентификаторов групп из списка дополнительных групп доступа для текущего процесса:

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

Следующие свойства возвращаемого массива зависят от системы:

  • Отсортирован ли массив (и каким образом).

  • Содержит ли массив эффективные идентификаторы групп.

  • Содержит ли массив повторяющиеся идентификаторы групп.

  • Превышает ли размер массива значение Process.maxgroups.

Чтобы получить отсортированный массив без повторений, используйте этот вызов:

Process.groups.uniq.sort
groups = new_groups → new_groups Показать исходный код
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);
}

Устанавливает список дополнительных групп доступа в соответствии с заданным массивом идентификаторов групп.

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]
initgroups(username, gid) → array Показать исходный код
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.

  • Идентификатор группы 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]

Доступно не на всех платформах.

kill(signal, *ids) → count Показать исходный код
static VALUE
proc_rb_f_kill(int c, const VALUE *v, VALUE _)
{
    return rb_f_kill(c, v);
}

Отправляет сигнал каждому процессу, заданному в ids (там должен быть указан хотя бы один идентификатор); возвращает количество отправленных сигналов.

Для каждого заданного id, если id имеет значение:

  • Положительное — отправляет сигнал процессу с идентификатором процесса id.

  • Ноль — отправляет сигнал всем процессам в текущей группе процессов.

  • Отрицательное — отправляет сигнал зависящему от системы набору процессов.

Аргумент signal задаёт отправляемый сигнал; аргумент может быть:

  • Целочисленным номером сигнала: например, -29, 0, 29.

  • Именем сигнала (строкой), с начальным 'SIG' или без него, а также с дополнительным префиксом в виде знака минус ('-') или без него. Например:

    • 'SIGPOLL'.

    • 'POLL',

    • '-SIGPOLL'.

    • '-POLL'.

  • Символом сигнала, с начальным 'SIG' или без него, а также с дополнительным префиксом в виде знака минус ('-') или без него. Например:

    • :SIGPOLL.

    • :POLL.

    • :'-SIGPOLL'.

    • :'-POLL'.

Если signal имеет значение:

  • Неотрицательное целое число либо имя или символ сигнала без префикса '-', сигнал отправляется каждому процессу с идентификатором процесса 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!

Исключения:

  • Вызывает Errno::EINVAL или RangeError, если signal — недопустимое целое число.

  • Вызывает ArgumentError, если signal — недопустимая строка или символ.

  • Вызывает Errno::ESRCH или RangeError, если один из элементов ids недопустим.

  • Вызывает Errno::EPERM, если отсутствуют необходимые разрешения.

В двух последних случаях сигналы могли быть отправлены некоторым процессам.

last_status → Process::Status or nil Показать исходный код
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>
maxgroups → integer Показать исходный код
static VALUE
proc_getmaxgroups(VALUE obj)
{
    return INT2FIX(maxgroups());
}

Возвращает максимальное количество идентификаторов групп, разрешённых в списке дополнительных групп доступа:

Process.maxgroups # => 32
maxgroups = new_max → new_max Показать исходный код
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 Показать исходный код
static VALUE
proc_get_pid(VALUE _)
{
    return get_pid();
}

Возвращает идентификатор текущего процесса:

Process.pid # => 15668
ppid → integer Показать исходный код
static VALUE
proc_get_ppid(VALUE _)
{
    return get_ppid();
}

Возвращает идентификатор родительского процесса текущего процесса:

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

Вывод:

Pid is 271290.
Parent pid is 271290.

На некоторых платформах возвращаемое значение может быть недостоверным.

setpgid(pid, pgid) → 0 Показать исходный код
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);
}

Устанавливает идентификатор группы процессов для процесса с идентификатором процесса pid равным pgid.

Доступно не на всех платформах.

setpgrp → 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);
}

Эквивалентно setpgid(0, 0).

Доступно не на всех платформах.

setpriority(kind, integer, priority) → 0 Показать исходный код
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

Доступно не на всех платформах.

setproctitle(string) → string Показать исходный код
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 как способ изменить заголовок процесса без использования глобальной переменной.

setrlimit(resource, cur_limit, max_limit = cur_limit) → nil Показать исходный код
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: время процессора (секунды) (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: время процессора для процесса реального времени (мкс) (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])

Доступно не на всех платформах.

setsid → integer Показать исходный код
static VALUE
proc_setsid(VALUE _)
{
    rb_pid_t pid;

    pid = setsid();
    if (pid < 0) rb_sys_fail(0);
    return PIDT2NUM(pid);
}

Создаёт для текущего процесса новый сеанс и группу процессов, делая его лидером, без управляющего терминала tty; возвращает идентификатор сеанса:

Process.setsid # => 27422

Доступно не на всех платформах.

spawn([env, ] command_line, options = {}) → pid Показать исходный код
spawn([env, ] exe_path, *args, options = {}) → pid
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 оболочке.

  • Запускает исполняемый файл по пути exe_path.

Этот метод может создавать уязвимости безопасности, если вызывается с недоверенными данными; см. Внедрение команд.

Возвращает идентификатор процесса (pid) нового процесса, не дожидаясь его завершения.

Чтобы избежать процессов-зомби, родительский процесс должен вызвать один из следующих методов:

  • Process.wait, чтобы получить статусы завершения дочерних процессов.

  • Process.detach, чтобы сообщить, что их статус не требуется.

Новый процесс создаётся с помощью системного вызова exec; он может унаследовать часть окружения вызывающей программы (возможно, включая открытые файловые дескрипторы).

Аргумент env, если он задан, представляет собой хеш, влияющий на ENV нового процесса; см. Окружение выполнения.

Аргумент options представляет собой хеш параметров нового процесса; см. Параметры выполнения.

Первый обязательный аргумент может быть одним из следующих:

  • command_line, если это строка, начинающаяся с зарезервированного слова оболочки или специальной встроенной команды либо содержащая один или несколько метасимволов.

  • exe_path в противном случае.

Аргумент command_line

Строковый аргумент command_line — это командная строка, передаваемая оболочке; она должна начинаться с зарезервированного слова оболочки или специальной встроенной команды либо содержать метасимволы:

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

Подробнее об оболочке см. в разделе Оболочка выполнения.

Если новый процесс не удалось запустить, вызывается исключение.

Аргумент exe_path

Аргумент exe_path может быть одним из следующих:

  • Строковый путь к запускаемому исполняемому файлу.

  • Массив из двух элементов, содержащий путь к запускаемому исполняемому файлу и строку, которая будет использоваться как имя выполняющегося процесса.

    spawn('/usr/bin/date') # Path to date on Unix-style system.
    Process.wait
    

    Вывод:

    Mon Aug 28 11:43:10 AM CDT 2023

Ruby запускает исполняемый файл напрямую. Эта форма не использует оболочку; см. раздел Аргументы args, где описаны возможные ограничения.

Если задан один или несколько аргументов args, каждый из них передаётся исполняемому файлу как аргумент или параметр:

spawn('echo', 'C*')             # => 799392
Process.wait                    # => 799392
spawn('echo', 'hello', 'world') # => 799393
Process.wait                    # => 799393

Вывод:

C*
hello world

Если новый процесс не удалось запустить, вызывается исключение.

times → process_tms Показать исходный код
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, содержащую пользовательское и системное время процессора для текущего процесса и его дочерних процессов:

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

Точность определяется платформой.

uid → integer Показать исходный код
Process::UID.rid → integer
Process::Sys.getuid → integer
static VALUE
proc_getuid(VALUE obj)
{
    rb_uid_t uid = getuid();
    return UIDT2NUM(uid);
}

Возвращает (реальный) идентификатор пользователя текущего процесса.

Process.uid # => 1000
uid = new_uid → new_uid Показать исходный код
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;
}

Устанавливает идентификатор пользователя текущего процесса равным new_uid:

Process.uid = 1000 # => 1000

Доступно не на всех платформах.

wait(pid = -1, flags = 0) → integer Показать исходный код
static VALUE
proc_m_wait(int c, VALUE *v, VALUE _)
{
    return proc_wait(c, v);
}

Ожидает завершения подходящего дочернего процесса, возвращает его идентификатор процесса и присваивает $? объект Process::Status, содержащий сведения об этом процессе. Выбор дочернего процесса зависит от значения заданного pid:

  • Положительное целое число: ожидает дочерний процесс с идентификатором процесса 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: ожидает любой дочерний процесс, идентификатор группы которого совпадает с идентификатором группы текущего процесса:

    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: ожидает любой дочерний процесс с идентификатором группы процессов -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 следует передать как одну из следующих констант или как результат побитового ИЛИ обеих констант:

  • Process::WNOHANG: не блокирует выполнение, если дочерний процесс недоступен.

  • Process::WUNTRACED: может вернуть остановленный дочерний процесс, даже если о нём ещё не сообщалось.

Не все флаги доступны на всех платформах.

Вызывает Errno::ECHILD, если подходящего дочернего процесса нет.

Доступно не на всех платформах.

Process.waitpid — псевдоним Process.wait.

wait2(pid = -1, flags = 0) → [pid, status] Показать исходный код
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.

waitall → array Показать исходный код
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;
}

Ожидает завершения всех дочерних процессов и возвращает массив массивов из двух элементов; каждый вложенный массив содержит целочисленный 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>]]
wait(pid = -1, flags = 0) → integer Показать исходный код
static VALUE
proc_m_wait(int c, VALUE *v, VALUE _)
{
    return proc_wait(c, v);
}

Ожидает завершения подходящего дочернего процесса, возвращает его идентификатор процесса и присваивает $? объект Process::Status, содержащий сведения об этом процессе. Выбор дочернего процесса зависит от значения заданного pid:

  • Положительное целое число: ожидает дочерний процесс с идентификатором процесса 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: ожидает любой дочерний процесс, идентификатор группы которого совпадает с идентификатором группы текущего процесса:

    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: ожидает любой дочерний процесс с идентификатором группы процессов -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 следует передать как одну из следующих констант или как результат побитового ИЛИ обеих констант:

  • Process::WNOHANG: не блокирует выполнение, если дочерний процесс недоступен.

  • Process::WUNTRACED: может вернуть остановленный дочерний процесс, даже если о нём ещё не сообщалось.

Не все флаги доступны на всех платформах.

Вызывает Errno::ECHILD, если подходящего дочернего процесса нет.

Доступно не на всех платформах.

Process.waitpid — псевдоним Process.wait.

wait2(pid = -1, flags = 0) → [pid, status] Показать исходный код
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.

warmup → true Показать исходный код
static VALUE
proc_warmup(VALUE _)
{
    RB_VM_LOCKING() {
        rb_gc_prepare_heap();
    }
    return Qtrue;
}

Уведомляет виртуальную машину Ruby о завершении загрузки и о том, что сейчас подходящее время для оптимизации приложения. Это полезно для приложений с длительным временем работы.

Предполагается, что этот метод вызывается в конце загрузки приложения. Если приложение развёртывается с использованием модели предварительного создания процессов, Process.warmup следует вызвать в исходном процессе до первого порождения дочернего процесса.

Фактически выполняемые оптимизации полностью зависят от реализации и могут в будущем измениться без предупреждения.

В CRuby метод Process.warmup выполняет следующие действия:

  • Выполняет полную сборку GC.

  • Уплотняет кучу.

  • Переводит все сохранившиеся объекты в старое поколение.

  • Предварительно вычисляет диапазон кодировки всех строк.

  • Освобождает все пустые страницы кучи и увеличивает счётчик доступных для выделения страниц на количество освобождённых страниц.

  • Вызывает malloc_trim, если он доступен, чтобы освободить пустые страницы malloc.

Ruby Core © 1993–2025 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API