Spec-Zone.ru › Ruby 3.4

модуль 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), указывающий источник, или массив fd, указывающий несколько источников.

Целое число исходного fd может быть указано как:

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

Есть эти сокращенные обозначения для fd:

  • :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, чтобы изменить это наследование, закрывая нестандартные fd (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 option для COMSPEC. Оболочка вызывается автоматически в следующих случаях:

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

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

Обратите внимание, что команда по-прежнему будет вызываться в форме command_line даже при вызове в форме exe_path, потому что 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, что последовательность запуска приложения завершена и что VM может начать оптимизацию приложения.

Процессы потомков

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

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

См. системную справку 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

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

См. системную справку 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

END_OF_DOCUMENT_MARKER

Методы публичного класса

_fork → целое число
Исходный код
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 → неизменяемая строка
Исходный код
static VALUE
proc_argv0(VALUE process)
{
    return rb_orig_progname;
}

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

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

clock_getres(clock_id, unit = :float_second) → число
Исходный код
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

См. Process.clock_gettime для значений clock_id и unit.

Примеры:

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, разрешение составляет 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 double) недостаточно для представления возвращаемого значения для :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 на устройство null.

Если необязательный аргумент 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 → целое
Process::UID.eid → целое
Process::Sys.geteuid → целое
Исходный код
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 нового процесса; см. ENV.

Аргумент options — хеш с параметрами для нового процесса; см. 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

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

Foo
.

Вызывает исключение, если новый процесс не может быть запущен.

Аргумент exe_path

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

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

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

Пример:

exec('/usr/bin/date')

Вывод:

Sat Aug 26 09:38:00 AM CDT 2023

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

exec('doesnt_exist') # Raises Errno::ENOENT
для замечаний.

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 { ... } → целое или nil
fork → целое или 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);
}

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

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

puts "Before the fork: #{Process.pid}"
fork do
  puts "In the child process: #{Process.pid}"
end                   # => 382141
puts "After the fork: #{Process.pid}"

Вывод:

Before the fork: 420496
After the fork: 420496
In the child process: 420520

Без указанного блока функция fork вызывается дважды:

  • Один раз в родительском процессе, возвращая pid дочернего процесса.

  • Один раз в дочернем процессе, возвращая nil.

Пример:

puts "This is the first line before the fork (pid #{Process.pid})"
puts fork
puts "This is the second line after the fork (pid #{Process.pid})"

Вывод:

This is the first line before the fork (pid 420199)
420223
This is the second line after the fork (pid 420199)

This is the second line after the fork (pid 420223)

В любом случае, дочерний процесс может завершиться с помощью Kernel.exit!, чтобы избежать вызова Kernel#at_exit.

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

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

  • Process.detach, чтобы исключить интерес к их статусам.

Поток, вызывающий fork, является единственным потоком в созданном дочернем процессе; fork не копирует другие потоки.

Обратите внимание, что метод fork доступен на некоторых платформах, но не на всех:

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

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

getpgid(pid) → целое
Исходный код
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 → целое
Исходный код
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) → целое
Исходный код
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) → целое
Исходный код
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 → целое
Process::GID.rid → целое
Process::Sys.getgid → целое
Исходный код
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 → массив
Исходный код
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) → массив
Исходный код
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:

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

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

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

    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;
}

Ожидает завершения всех дочерних процессов, возвращает массив 2-элементных массивов; каждый подмассив содержит целочисленный 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>]]
END_OF_DOCUMENT_MARKER
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_LOCK_ENTER();
    rb_gc_prepare_heap();
    RB_VM_LOCK_LEAVE();
    return Qtrue;
}

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

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

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

В CRuby Process.warmup:

  • Выполняет основную GC.

  • Компактует кучу.

  • Перемещает все выжившие объекты в старое поколение.

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

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

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

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

Spec-Zone.ru

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