модуль 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.
Обратите внимание: команда всё равно будет вызвана в форме 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: создаёт для процесса новый сеанс и делает его лидером группы процессов без управляющего терминала tty. -
::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
- CLOCK_BOOTTIME_ALARM
- CLOCK_MONOTONIC
- CLOCK_MONOTONIC_COARSE
- CLOCK_MONOTONIC_FAST
- CLOCK_MONOTONIC_PRECISE
- CLOCK_MONOTONIC_RAW
- CLOCK_MONOTONIC_RAW_APPROX
- CLOCK_PROCESS_CPUTIME_ID
- CLOCK_PROF
- CLOCK_REALTIME
- CLOCK_REALTIME_ALARM
- CLOCK_REALTIME_COARSE
- CLOCK_REALTIME_FAST
- CLOCK_REALTIME_PRECISE
- CLOCK_SECOND
- CLOCK_TAI
- CLOCK_THREAD_CPUTIME_ID
- CLOCK_UPTIME
- CLOCK_UPTIME_FAST
- CLOCK_UPTIME_PRECISE
- CLOCK_UPTIME_RAW
- CLOCK_UPTIME_RAW_APPROX
- CLOCK_VIRTUAL
- PRIO_PGRP
- PRIO_PROCESS
- PRIO_USER
- RLIMIT_AS
-
Максимальный размер виртуальной памяти (адресного пространства) процесса в байтах.
Подробности см. в системном руководстве getrlimit(2).
- RLIMIT_CORE
-
Максимальный размер файла 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
- RLIM_SAVED_CUR
- RLIM_SAVED_MAX
- WNOHANG
-
см.
Process.wait - WUNTRACED
-
см.
Process.wait
Публичные методы класса
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), НО не вызывает этот метод. Поэтому, в зависимости от причины перехвата этого метода, возможно, вам также следует перехватывать и тот. Более подробно это обсуждается в этом обсуждении.
static VALUE
f_abort(int c, const VALUE *a, VALUE _)
{
rb_f_abort(c, a);
UNREACHABLE_RETURN(Qnil);
} Немедленно завершает выполнение, фактически вызывая Kernel.exit(false).
Если передан строковый аргумент msg, он записывается в STDERR перед завершением; в противном случае, если было возбуждено исключение, выводятся его сообщение и трассировка стека.
static VALUE
proc_argv0(VALUE process)
{
return rb_orig_progname;
} Возвращает имя выполняемого скрипта. Это значение не меняется при присваивании нового значения $0.
Этот метод впервые появился в Ruby 2.1, чтобы предоставить способ получить имя скрипта без использования глобальной переменной.
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 или виртуализации.
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 (спецификация Single UNIX). SUS включает POSIX, а clock_gettime определена в части POSIX. SUS определяет :CLOCK_REALTIME как обязательную, а :CLOCK_MONOTONIC, :CLOCK_PROCESS_CPUTIME_ID и :CLOCK_THREAD_CPUTIME_ID — как необязательные.
Если указанные clock_id напрямую не поддерживаются, используются некоторые эмуляции:
-
Эмуляция для
:CLOCK_REALTIME:-
:GETTIMEOFDAY_BASED_CLOCK_REALTIME: используется 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 double) недостаточно для точного представления возвращаемого значения для :CLOCK_REALTIME. Если требуется точное значение в наносекундах, используйте :nanosecond в качестве unit.
Начальная точка (нулевое время) возвращаемого значения зависит от системы; это может быть, например, время запуска системы или процесса, эпоха и т. д.
Начальная точка для :CLOCK_REALTIME определяется как эпоха: 1970-01-01 00:00:00 UTC; некоторые системы учитывают високосные секунды, а другие — нет, поэтому результаты могут различаться в разных системах.
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 не выполняется.
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
static VALUE
proc_getegid(VALUE obj)
{
rb_gid_t egid = getegid();
return GIDT2NUM(egid);
} Возвращает эффективный идентификатор группы для текущего процесса:
Process.egid # => 500
Доступно не на всех платформах.
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;
} Устанавливает эффективный идентификатор группы для текущего процесса.
Доступно не на всех платформах.
static VALUE
proc_geteuid(VALUE obj)
{
rb_uid_t euid = geteuid();
return UIDT2NUM(euid);
} Возвращает эффективный идентификатор пользователя для текущего процесса.
Process.euid # => 501
static VALUE
proc_seteuid_m(VALUE mod, VALUE euid)
{
check_uid_switch();
proc_seteuid(OBJ2UID(euid));
return euid;
} Устанавливает эффективный идентификатор пользователя для текущего процесса.
Доступно не на всех платформах.
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
Возбуждает исключение, если новый процесс не удалось запустить.
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.
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 обозначают соответственно успех и неудачу; значения целых чисел зависят от системы.
static VALUE
rb_f_fork(VALUE obj)
{
rb_pid_t pid;
pid = rb_call_proc__fork();
if (pid == 0) {
if (rb_block_given_p()) {
int status;
rb_protect(rb_yield, Qundef, &status);
ruby_stop(status);
}
return Qnil;
}
return PIDT2NUM(pid);
} Создаёт дочерний процесс.
Если передан блок, он выполняется в дочернем процессе; после завершения блока дочерний процесс завершается со статусом ноль:
puts "Before the fork: #{Process.pid}"
fork do
puts "In the child process: #{Process.pid}"
end # => 382141
puts "After the fork: #{Process.pid}"
Вывод:
Before the fork: 420496 After the fork: 420496 In the child process: 420520
Если блок не передан, вызов fork возвращает управление дважды:
-
Один раз в родительском процессе, возвращая pid дочернего процесса.
-
Один раз в дочернем процессе, возвращая
nil.
Пример:
puts "This is the first line before the fork (pid #{Process.pid})"
puts fork
puts "This is the second line after the fork (pid #{Process.pid})"
Вывод:
This is the first line before the fork (pid 420199) 420223 This is the second line after the fork (pid 420199) This is the second line after the fork (pid 420223)
В обоих случаях дочерний процесс может завершиться с помощью Kernel.exit!, чтобы избежать вызова Kernel#at_exit.
Чтобы избежать процессов-зомби, родительскому процессу следует вызвать один из следующих методов:
-
Process.wait, чтобы получить статусы завершения дочерних процессов. -
Process.detach, чтобы отказаться от получения их статусов.
Поток, вызвавший fork, — единственный поток в созданном дочернем процессе; fork не копирует остальные потоки.
Обратите внимание, что метод fork доступен на одних платформах, но не на других:
Process.respond_to?(:fork) # => true # Would be false on some.
Если он недоступен, вместо fork можно использовать ::spawn.
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
Доступно не на всех платформах.
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
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
Доступно не на всех платформах.
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.
Доступно не на всех платформах.
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
Доступно не на всех платформах.
static VALUE
proc_getgid(VALUE obj)
{
rb_gid_t gid = getgid();
return GIDT2NUM(gid);
} Возвращает (реальный) идентификатор группы для текущего процесса:
Process.gid # => 1000
static VALUE
proc_setgid(VALUE obj, VALUE id)
{
rb_gid_t gid;
check_gid_switch();
gid = OBJ2GID(id);
#if defined(HAVE_SETRESGID)
if (setresgid(gid, -1, -1) < 0) rb_sys_fail(0);
#elif defined HAVE_SETREGID
if (setregid(gid, -1) < 0) rb_sys_fail(0);
#elif defined HAVE_SETRGID
if (setrgid(gid) < 0) rb_sys_fail(0);
#elif defined HAVE_SETGID
{
if (getegid() == gid) {
if (setgid(gid) < 0) rb_sys_fail(0);
}
else {
rb_notimplement();
}
}
#endif
return GIDT2NUM(gid);
} Устанавливает идентификатор группы текущего процесса в значение new_gid:
Process.gid = 1000 # => 1000
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
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]
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]
Доступно не на всех платформах.
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, если отсутствуют необходимые разрешения.
В двух последних случаях сигналы могли быть отправлены некоторым процессам.
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>
static VALUE
proc_getmaxgroups(VALUE obj)
{
return INT2FIX(maxgroups());
} Возвращает максимальное количество идентификаторов групп, допустимое в списке дополнительных групп доступа:
Process.maxgroups # => 32
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);
} Задает максимальное количество идентификаторов групп, допустимое в списке дополнительных групп доступа.
static VALUE
proc_get_pid(VALUE _)
{
return get_pid();
} Возвращает идентификатор текущего процесса:
Process.pid # => 15668
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.
На некоторых платформах возвращаемое значение может быть недостоверным.
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);
} Задает идентификатор группы процессов pgid для процесса с идентификатором pid.
Доступно не на всех платформах.
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).
Доступно не на всех платформах.
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
Доступно не на всех платформах.
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 как способ изменить заголовок процесса без использования глобальных переменных.
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: размер 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: отсутствие ограничения для ресурса.
В этом примере мягкое ограничение размера core-файла повышается до жесткого, чтобы попытаться разрешить создание дампа памяти:
Process.setrlimit(:CORE, Process.getrlimit(:CORE)[1])
Доступно не на всех платформах.
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
Доступно не на всех платформах.
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
Вызывает исключение, если новый процесс не удалось запустить.
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>
Точность определяется платформой.
static VALUE
proc_getuid(VALUE obj)
{
rb_uid_t uid = getuid();
return UIDT2NUM(uid);
} Возвращает (реальный) идентификатор пользователя текущего процесса.
Process.uid # => 1000
static VALUE
proc_setuid(VALUE obj, VALUE id)
{
rb_uid_t uid;
check_uid_switch();
uid = OBJ2UID(id);
#if defined(HAVE_SETRESUID)
if (setresuid(uid, -1, -1) < 0) rb_sys_fail(0);
#elif defined HAVE_SETREUID
if (setreuid(uid, -1) < 0) rb_sys_fail(0);
#elif defined HAVE_SETRUID
if (setruid(uid) < 0) rb_sys_fail(0);
#elif defined HAVE_SETUID
{
if (geteuid() == uid) {
if (setuid(uid) < 0) rb_sys_fail(0);
}
else {
rb_notimplement();
}
}
#endif
return id;
} Задает идентификатор пользователя текущего процесса равным new_uid:
Process.uid = 1000 # => 1000
Доступно не на всех платформах.
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.
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.
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>]]
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.
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.
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–2025 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.