модуль 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, где каждая переменная окружения с именем создается или обновляется (если значение не пустое), или удаляется (если значение пустое).
Однако некоторые изменения в вызывающем процессе могут остаться, если новый процесс завершается неудачно. Например, жесткие ограничения ресурсов не восстанавливаются.
Аргумент command_line или exe_path
Требуемый строковый аргумент — один из следующих:
-
command_lineесли он начинается со служебного слова оболочки или специального встроенного, или если он содержит один или несколько метасимволов. -
exe_pathв противном случае.
Аргумент command_line
Строковый аргумент command_line — это командная строка, которая должна быть передана в оболочку; она должна начинаться со служебного слова оболочки, начинаться со специального встроенного или содержать метасимволы:
system('if true; then echo "Foo"; fi') # => true # Shell reserved word.
system('echo') # => 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 failed.
Вывод:
Mon Aug 28 11:43:10 AM CDT 2023
Параметры выполнения
Необязательный заключительный аргумент 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'.
Значением такого ключа может быть:
-
Целое число, определяющее текущие и максимальные ограничения.
-
Массив из двух целых чисел, определяющий текущие и максимальные ограничения.
Наследование дескрипторов файлов
По умолчанию новый процесс наследует дескрипторы файлов от родительского процесса.
Используйте параметр выполнения :close_others => true для изменения этого наследования путем закрытия нестандартных fd (3 и выше), которые не перенаправляются другим способом.
Оболочка выполнения
В Unix-подобной системе вызывается оболочка /bin/sh; в противном случае вызываемая оболочка определяется переменной среды ENV['RUBYSHELL'], если она определена, или ENV['COMSPEC'] в противном случае.
За исключением случая COMSPEC, вся строка command_line передается в качестве аргумента параметру оболочки -c.
Оболочка выполняет обычное расширение оболочки для командной строки:
spawn('echo C*') # => 799139
Process.wait # => 799139
Вывод:
CONTRIBUTING.md COPYING COPYING.ja
Здесь
Получатели текущего процесса
-
::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
- 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 to 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 to 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 to 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 to 4, Linux 2.5.63, FreeBSD 7.1, OpenBSD 5.4, macOS 10.12. -
:CLOCK_UPTIME: FreeBSD 7.0, OpenBSD 5.5. -
:CLOCK_UPTIME_FAST: FreeBSD 8.1. -
:CLOCK_UPTIME_PRECISE: FreeBSD 8.1. -
:CLOCK_UPTIME_RAW: macOS 10.12. -
:CLOCK_UPTIME_RAW_APPROX: macOS 10.12. -
:CLOCK_VIRTUAL: FreeBSD 3.0, OpenBSD 2.1.
Обратите внимание, что SUS означает Single Unix Specification. SUS содержит POSIX, и clock_gettime определен в части POSIX. SUS определяет :CLOCK_REALTIME как обязательный, но :CLOCK_MONOTONIC, :CLOCK_PROCESS_CPUTIME_ID и :CLOCK_THREAD_CPUTIME_ID являются необязательными.
Используются определенные эмуляции, когда заданный clock_id не поддерживается напрямую:
-
Эмуляции для
:CLOCK_REALTIME:-
:GETTIMEOFDAY_BASED_CLOCK_REALTIME: Используется gettimeofday(), определенный SUS (устарел в SUSv4). Разрешение составляет 1 микросекунду. -
:TIME_BASED_CLOCK_REALTIME: Используется time(), определенный ISO C. Разрешение составляет 1 секунду.
-
-
Эмуляции для
:CLOCK_MONOTONIC:-
:MACH_ABSOLUTE_TIME_BASED_CLOCK_MONOTONIC: Используется mach_absolute_time(), доступный на Darwin. Разрешение зависит от процессора. -
:TIMES_BASED_CLOCK_MONOTONIC: Используется возвращаемое значение times(), определенное POSIX, таким образом:При успешном завершении times() возвращает прошедшее реальное время в тактах часов с некоторой произвольной точки в прошлом (например, времени запуска системы).
Например, GNU/Linux возвращает значение на основе jiffies, и оно монотонно. Однако 4.4BSD использует gettimeofday(), и оно не монотонно. (FreeBSD использует
:CLOCK_MONOTONICвместо этого.)Разрешение — это такт часов. Команда «getconf CLK_TCK» показывает такты часов в секунду. (Такты часов в секунду определяются макросом HZ в более старых системах.) Если это 100, а clock_t — 32-битный тип целого числа, разрешение составляет 10 миллисекунд, и он не может представлять более 497 дней.
-
-
Эмуляции для
:CLOCK_PROCESS_CPUTIME_ID:-
:GETRUSAGE_BASED_CLOCK_PROCESS_CPUTIME_ID: Используется getrusage(), определенный SUS. getrusage() используется с RUSAGE_SELF для получения времени только для вызывающего процесса (исключая время для дочерних процессов). Результатом является сумма времени пользователя (ru_utime) и системного времени (ru_stime). Разрешение составляет 1 микросекунду. -
:TIMES_BASED_CLOCK_PROCESS_CPUTIME_ID: Используется times(), определенный POSIX. Результатом является сумма времени пользователя (tms_utime) и системного времени (tms_stime). tms_cutime и tms_cstime игнорируются, чтобы исключить время для дочерних процессов. Разрешение — это такт часов. Команда «getconf CLK_TCK» показывает такты часов в секунду. (Такты часов в секунду определяются макросом HZ в более старых системах.) Если это 100, разрешение составляет 10 миллисекунд. -
:CLOCK_BASED_CLOCK_PROCESS_CPUTIME_ID: Используется clock(), определенный ISO C. Разрешение составляет1/CLOCKS_PER_SEC.CLOCKS_PER_SEC— это макрос на уровне C, определенный time.h. SUS определяетCLOCKS_PER_SECкак 1000000; другие системы могут определить его по-разному. ЕслиCLOCKS_PER_SECравно 1000000 (как в SUS), разрешение составляет 1 микросекунду. ЕслиCLOCKS_PER_SECравно 1000000, а clock_t — 32-битный тип целого числа, он не может представлять более 72 минут.
-
Аргумент unit
Необязательный аргумент unit (по умолчанию :float_second) указывает единицу измерения для возвращаемого значения.
-
:float_microsecond: Количество микросекунд в виде числа с плавающей запятой. -
:float_millisecond: Количество миллисекунд в виде числа с плавающей запятой. -
:float_second: Количество секунд в виде числа с плавающей запятой. -
:microsecond: Количество микросекунд в виде целого числа. -
:millisecond: Количество миллисекунд в виде целого числа. -
:nanosecond: Количество наносекунд в виде целого числа. -
::second: Количество секунд в виде целого числа.
Примеры:
Process.clock_gettime(:CLOCK_PROCESS_CPUTIME_ID, :float_microsecond) # => 203605054.825 Process.clock_gettime(:CLOCK_PROCESS_CPUTIME_ID, :float_millisecond) # => 203643.696848 Process.clock_gettime(:CLOCK_PROCESS_CPUTIME_ID, :float_second) # => 203.762181929 Process.clock_gettime(:CLOCK_PROCESS_CPUTIME_ID, :microsecond) # => 204123212 Process.clock_gettime(:CLOCK_PROCESS_CPUTIME_ID, :millisecond) # => 204298 Process.clock_gettime(:CLOCK_PROCESS_CPUTIME_ID, :nanosecond) # => 204602286036 Process.clock_gettime(:CLOCK_PROCESS_CPUTIME_ID, :second) # => 204
Базовая функция, clock_gettime(), возвращает количество наносекунд. Объект Float (IEEE 754 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 на устройство null.
Если необязательный аргумент 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.
Ожидающий поток может вернуть идентификатор процесса отсоединившегося дочернего процесса:
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('echo') # 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 вызывает исполняемый файл напрямую без оболочки и расширения оболочки:
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 возвращает значение дважды:
-
Однажды в родительском процессе, возвращая идентификатор процесса дочернего процесса.
-
Однажды в дочернем процессе, возвращая
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.
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 — это ID для процесса, группы процессов или пользователя; ноль указывает текущий 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 является:
-
Положительным, отправляет сигнал процессу, идентификатор процесса которого равен
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);
} Устанавливает идентификатор группы процессов для процесса, заданного идентификатором процесса pid на pgid.
Не доступно на всех платформах.
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: Размер ядра (байты) (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])
Не доступно на всех платформах.
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('echo') # => 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Вывод:
Thu Aug 31 10:06:48 AM CDT 2023
-
Массив из 2 элементов, содержащий путь к исполняемому файлу и строку, используемую в качестве имени выполняемого процесса:
pid = spawn(['sleep', 'Hello!'], '1') # 2-element array. p `ps -p #{pid} -o command=`Вывод:
"Hello! 1\n"
Ruby вызывает исполняемый файл напрямую, без оболочки и без расширения оболочки.
Если один или несколько 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.waitpid.
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>]]
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.waitpid.
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–2022 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.