модуль Process
Константы
- CLOCK_BOOTTIME
-
см. ::clock_gettime
- CLOCK_BOOTTIME_ALARM
-
см. ::clock_gettime
- CLOCK_MONOTONIC
-
см. ::clock_gettime
- CLOCK_MONOTONIC_COARSE
-
см. ::clock_gettime
- CLOCK_MONOTONIC_FAST
-
см. ::clock_gettime
- CLOCK_MONOTONIC_PRECISE
-
см. ::clock_gettime
- CLOCK_MONOTONIC_RAW
-
см. ::clock_gettime
- CLOCK_PROCESS_CPUTIME_ID
-
см. ::clock_gettime
- CLOCK_PROF
-
см. ::clock_gettime
- CLOCK_REALTIME
-
см. ::clock_gettime
- CLOCK_REALTIME_ALARM
-
см. ::clock_gettime
- CLOCK_REALTIME_COARSE
-
см. ::clock_gettime
- CLOCK_REALTIME_FAST
-
см. ::clock_gettime
- CLOCK_REALTIME_PRECISE
-
см. ::clock_gettime
- CLOCK_SECOND
-
см. ::clock_gettime
- CLOCK_THREAD_CPUTIME_ID
-
см. ::clock_gettime
- CLOCK_UPTIME
-
см. ::clock_gettime
- CLOCK_UPTIME_FAST
-
см. ::clock_gettime
- CLOCK_UPTIME_PRECISE
-
см. ::clock_gettime
- CLOCK_VIRTUAL
-
см. ::clock_gettime
- PRIO_PGRP
-
см. ::setpriority
- PRIO_PROCESS
-
см. ::setpriority
- PRIO_USER
-
см. ::setpriority
- RLIMIT_AS
-
Максимальный размер виртуальной памяти (адресного пространства) процесса в байтах.
см. системную справку getrlimit(2) для подробностей.
- RLIMIT_CORE
-
Максимальный размер файла кода.
см. системную справку getrlimit(2) для подробностей.
- RLIMIT_CPU
-
Предел времени использования процессора в секундах.
см. системную справку getrlimit(2) для подробностей.
- RLIMIT_DATA
-
Максимальный размер сегмента данных процесса.
см. системную справку getrlimit(2) для подробностей.
- RLIMIT_FSIZE
-
Максимальный размер файлов, которые может создать процесс.
см. системную справку getrlimit(2) для подробностей.
- RLIMIT_MEMLOCK
-
Максимальное количество байтов памяти, которое может быть заблокировано в ОЗУ.
см. системную справку getrlimit(2) для подробностей.
- RLIMIT_MSGQUEUE
-
Устанавливает лимит на количество байт, которые могут быть выделены для очередей сообщений POSIX для реального идентификатора пользователя вызывающего процесса.
см. системную справку getrlimit(2) для подробностей.
- RLIMIT_NICE
-
Устанавливает потолок, до которого может быть повышено значение nice процесса.
см. системную справку getrlimit(2) для подробностей.
- RLIMIT_NOFILE
-
Устанавливает значение, на единицу больше максимального номера дескриптора файла, который может быть открыт данным процессом.
см. системную справку getrlimit(2) для подробностей.
- RLIMIT_NPROC
-
Максимальное количество процессов, которые могут быть созданы для реального идентификатора пользователя вызывающего процесса.
см. системную справку 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
-
см. ::setrlimit
- RLIM_SAVED_CUR
-
см. ::setrlimit
- RLIM_SAVED_MAX
-
см. ::setrlimit
- WNOHANG
-
см. ::wait
- WUNTRACED
-
см. ::wait
Публичные методы класса
VALUE
rb_f_abort(int argc, const VALUE *argv)
{
rb_check_arity(argc, 0, 1);
if (argc == 0) {
if (!NIL_P(GET_THREAD()->errinfo)) {
ruby_error_print();
}
rb_exit(EXIT_FAILURE);
}
else {
VALUE args[2];
args[1] = args[0] = argv[0];
StringValue(args[0]);
rb_io_puts(1, args, rb_stderr);
args[0] = INT2NUM(EXIT_FAILURE);
rb_exc_raise(rb_class_new_instance(2, args, rb_eSystemExit));
}
UNREACHABLE;
} Немедленно завершить выполнение, фактически вызвав Kernel.exit(false). Если указано msg, оно выводится в STDERR перед завершением.
static VALUE
proc_argv0(VALUE process)
{
return rb_orig_progname;
} Возвращает имя выполняемого скрипта. Значение не изменяется при присваивании нового значения $0.
Этот метод впервые появился в Ruby 2.1, чтобы служить средством получения имени скрипта без использования глобальных переменных.
VALUE
rb_clock_getres(int argc, VALUE *argv)
{
VALUE clk_id, unit;
struct timetick tt;
timetick_int_t numerators[2];
timetick_int_t denominators[2];
int num_numerators = 0;
int num_denominators = 0;
rb_scan_args(argc, argv, "11", &clk_id, &unit);
if (SYMBOL_P(clk_id)) {
#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) {
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 defined(HAVE_CLOCK_GETRES)
struct timespec ts;
clockid_t c = NUM2CLOCKID(clk_id);
int ret = clock_getres(c, &ts);
if (ret == -1)
rb_sys_fail("clock_getres");
tt.count = (int32_t)ts.tv_nsec;
tt.giga_count = ts.tv_sec;
denominators[num_denominators++] = 1000000000;
goto success;
#endif
}
/* EINVAL emulates clock_getres behavior when clock_id is invalid. */
rb_syserr_fail(EINVAL, 0);
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().
clock_id указывает тип часов. Подробности см. в документации Process.clock_gettime.
clock_id может быть символом, как Process.clock_gettime. Однако результат может быть неточным. Например, +Process.clock_getres(:GETTIMEOFDAY_BASED_CLOCK_REALTIME)+ возвращает 1.0e-06, что означает 1 микросекунду, но фактическое разрешение может быть более грубым.
Если данный clock_id не поддерживается, генерируется исключение Errno::EINVAL.
unit указывает тип возвращаемого значения. Process.clock_getres принимает unit как Process.clock_gettime. Значение по умолчанию, :float_second, также совпадает с Process.clock_gettime.
Process.clock_getres также принимает :hertz как unit. :hertz означает обратную величину :float_second.
:hertz можно использовать для получения точного значения тактов часов в секунду для функции times() и CLOCKS_PER_SEC для функции clock().
+Process.clock_getres(:TIMES_BASED_CLOCK_PROCESS_CPUTIME_ID, :hertz)+ возвращает такты часов в секунду.
+Process.clock_getres(:CLOCK_BASED_CLOCK_PROCESS_CPUTIME_ID, :hertz)+ возвращает CLOCKS_PER_SEC.
p Process.clock_getres(Process::CLOCK_MONOTONIC) #=> 1.0e-09
VALUE
rb_clock_gettime(int argc, VALUE *argv)
{
VALUE clk_id, unit;
int ret;
struct timetick tt;
timetick_int_t numerators[2];
timetick_int_t denominators[2];
int num_numerators = 0;
int num_denominators = 0;
rb_scan_args(argc, argv, "11", &clk_id, &unit);
if (SYMBOL_P(clk_id)) {
/*
* Non-clock_gettime clocks are provided by symbol clk_id.
*
* gettimeofday is always available on platforms supported by Ruby.
* 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;
}
#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__
#define RUBY_MACH_ABSOLUTE_TIME_BASED_CLOCK_MONOTONIC ID2SYM(id_MACH_ABSOLUTE_TIME_BASED_CLOCK_MONOTONIC)
if (clk_id == RUBY_MACH_ABSOLUTE_TIME_BASED_CLOCK_MONOTONIC) {
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 defined(HAVE_CLOCK_GETTIME)
struct timespec ts;
clockid_t c;
c = NUM2CLOCKID(clk_id);
ret = clock_gettime(c, &ts);
if (ret == -1)
rb_sys_fail("clock_gettime");
tt.count = (int32_t)ts.tv_nsec;
tt.giga_count = ts.tv_sec;
denominators[num_denominators++] = 1000000000;
goto success;
#endif
}
/* EINVAL emulates clock_gettime behavior when clock_id is invalid. */
rb_syserr_fail(EINVAL, 0);
success:
return make_clock_result(&tt, numerators, num_numerators, denominators, num_denominators, unit);
} Возвращает время, возвращаемое функцией POSIX ::clock_gettime().
p Process.clock_gettime(Process::CLOCK_MONOTONIC) #=> 896053.968060096
clock_id указывает тип часов. Он задается как константа, начинающаяся с Process::CLOCK_, например Process::CLOCK_REALTIME и Process::CLOCK_MONOTONIC.
Поддерживаемые константы зависят от операционной системы и версии. Ruby предоставляет следующие типы clock_id, если они доступны.
- CLOCK_REALTIME
-
SUSv2 to 4, Linux 2.5.63, FreeBSD 3.0, NetBSD 2.0, OpenBSD 2.1
- CLOCK_MONOTONIC
-
SUSv3 to 4, Linux 2.5.63, FreeBSD 3.0, NetBSD 2.0, OpenBSD 3.4
- CLOCK_PROCESS_CPUTIME_ID
-
SUSv3 to 4, Linux 2.5.63, OpenBSD 5.4
- CLOCK_THREAD_CPUTIME_ID
-
SUSv3 to 4, Linux 2.5.63, FreeBSD 7.1, OpenBSD 5.4
- CLOCK_VIRTUAL
-
FreeBSD 3.0, OpenBSD 2.1
- CLOCK_PROF
-
FreeBSD 3.0, OpenBSD 2.1
- CLOCK_REALTIME_FAST
-
FreeBSD 8.1
- CLOCK_REALTIME_PRECISE
-
FreeBSD 8.1
- CLOCK_REALTIME_COARSE
-
Linux 2.6.32
- CLOCK_REALTIME_ALARM
-
Linux 3.0
- CLOCK_MONOTONIC_FAST
-
FreeBSD 8.1
- CLOCK_MONOTONIC_PRECISE
-
FreeBSD 8.1
- CLOCK_MONOTONIC_COARSE
-
Linux 2.6.32
- CLOCK_MONOTONIC_RAW
-
Linux 2.6.28
- CLOCK_BOOTTIME
-
Linux 2.6.39
- CLOCK_BOOTTIME_ALARM
-
Linux 3.0
- CLOCK_UPTIME
-
FreeBSD 7.0, OpenBSD 5.5
- CLOCK_UPTIME_FAST
-
FreeBSD 8.1
- CLOCK_UPTIME_PRECISE
-
FreeBSD 8.1
- CLOCK_SECOND
-
FreeBSD 8.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_gettime().
Например, Process::CLOCK_REALTIME определяется как :GETTIMEOFDAY_BASED_CLOCK_REALTIME, когда ::clock_gettime() недоступен.
Эмуляции для 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. POSIX определяет его как «times() должно возвращать прошедшее реальное время в тактах с произвольной точки в прошлом (например, время запуска системы)». Например, GNU/Linux возвращает значение, основанное на jiffies, и оно монотонно. Однако 4.4BSD использует gettimeofday(), и оно не монотонно. (FreeBSD вместо этого использует ::clock_gettime(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. Хотя не-Unix системы могут определить его с другим значением. Если CLOCKS_PER_SEC равен 1000000 как в SUS, то разрешение составляет 1 микросекунду. Если CLOCKS_PER_SEC равен 1000000, а clock_t имеет 32-битный целочисленный тип, то нельзя представить более 72 минут.
Если данный clock_id не поддерживается, генерируется исключение Errno::EINVAL.
unit указывает тип возвращаемого значения.
- :float_second
-
число секунд как число с плавающей точкой (по умолчанию)
- :float_millisecond
-
число миллисекунд как число с плавающей точкой
- :float_microsecond
-
число микросекунд как число с плавающей точкой
- :second
-
число секунд как целое число
- :millisecond
-
число миллисекунд как целое число
- :microsecond
-
число микросекунд как целое число
- :nanosecond
-
число наносекунд как целое число
Базовая функция, ::clock_gettime(), возвращает число наносекунд. Объект Float (IEEE 754 double) недостаточно для представления возвращаемого значения для CLOCK_REALTIME. Если требуется точное значение в наносекундах, используйте :nanoseconds в качестве unit.
Начало отсчета (ноль) возвращаемого значения варьируется. Например, время запуска системы, время запуска процесса, эпоха и т. д.
Начало отсчета в CLOCK_REALTIME определяется как эпоха (1970-01-01 00:00:00 UTC). Но некоторые системы учитывают високосные секунды, а другие — нет. Таким образом, результат может интерпретироваться по-разному на разных системах. Time.now рекомендуется вместо CLOCK_REALTIME.
static VALUE
proc_daemon(int argc, VALUE *argv)
{
VALUE nochdir, noclose;
int n;
rb_scan_args(argc, argv, "02", &nochdir, &noclose);
prefork();
n = rb_daemon(RTEST(nochdir), RTEST(noclose));
if (n < 0) rb_sys_fail("daemon");
return INT2FIX(n);
} Отсоединяет процесс от управляющего терминала и запускает его в фоновом режиме как системный демон. Если аргумент nochdir имеет значение true (т. е. не false), он меняет текущий рабочий каталог на корневой (“/”). Если аргумент noclose имеет значение true, daemon() перенаправит стандартный ввод, стандартный вывод и стандартную ошибку в /dev/null. Возвращает ноль в случае успеха или вызывает одно из исключений Errno::*.
static VALUE
proc_detach(VALUE obj, VALUE pid)
{
return rb_detach_process(NUM2PIDT(pid));
} Некоторые операционные системы сохраняют статус завершенных дочерних процессов до тех пор, пока родительский процесс не соберет этот статус (обычно с использованием какой-либо разновидности wait()). Если родительский процесс никогда не собирает этот статус, дочерний процесс остается как процесс-зомби. Process::detach предотвращает это, создавая отдельный поток Ruby, единственная задача которого — собрать статус процесса pid при его завершении. Используйте detach только в том случае, если вы не хотите явно ожидать завершения дочернего процесса.
Ожидающий поток возвращает код завершения отсоединённого процесса при его завершении, поэтому вы можете использовать Thread#join для получения результата. Если указанный pid не является корректным идентификатором дочернего процесса, поток возвращает nil немедленно.
Ожидающий поток имеет метод pid, который возвращает pid.
В этом первом примере мы не собираем первый дочерний процесс, поэтому он отображается как процесс-зомби в списке процессов.
p1 = fork { sleep 0.1 }
p2 = fork { sleep 0.2 }
Process.waitpid(p2)
sleep 2
system("ps -ho pid,state -p #{p1}")
выводит:
27389 Z
В следующем примере используется Process::detach для автоматического сбора дочернего процесса.
p1 = fork { sleep 0.1 }
p2 = fork { sleep 0.2 }
Process.detach(p1)
Process.waitpid(p2)
sleep 2
system("ps -ho pid,state -p #{p1}")
(не выводит ничего)
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;
} Устанавливает эффективный идентификатор пользователя для данного процесса. Не доступно на всех платформах.
VALUE
rb_f_exec(int argc, const VALUE *argv)
{
VALUE execarg_obj, fail_str;
struct rb_execarg *eargp;
#define CHILD_ERRMSG_BUFLEN 80
char errmsg[CHILD_ERRMSG_BUFLEN] = { '\0' };
int err;
execarg_obj = rb_execarg_new(argc, argv, TRUE);
eargp = rb_execarg_get(execarg_obj);
before_exec(); /* stop timer thread before redirects */
rb_execarg_parent_start(execarg_obj);
fail_str = eargp->use_shell ? eargp->invoke.sh.shell_script : eargp->invoke.cmd.command_name;
rb_exec_async_signal_safe(eargp, errmsg, sizeof(errmsg));
err = errno;
after_exec(); /* restart timer thread */
rb_exec_fail(eargp, err, errmsg);
RB_GC_GUARD(execarg_obj);
rb_syserr_fail_str(err, fail_str);
return Qnil; /* dummy */
} Заменяет текущий процесс, выполняя указанную внешнюю команду, которая может иметь один из следующих форматов:
-
exec(commandline) -
Строка командной строки, которая передаётся стандартной оболочке
-
exec(cmdname, arg1, ...) -
Имя команды и один или несколько аргументов (без оболочки)
-
exec([cmdname, argv0], arg1, ...) -
Имя команды, argv и ноль или более аргументов (без оболочки)
В первом формате строка рассматривается как командная строка, которая подвергается расширению оболочки перед выполнением.
Стандартная оболочка всегда означает "/bin/sh" на системах Unix-подобных системах, так же как ENV["RUBYSHELL"] (или ENV["COMSPEC"] в системах Windows NT), и аналогично.
Если строка из первого формата (exec("command")) соответствует этим простым правилам:
-
нет метасимволов
-
нет зарезервированных слов оболочки и нет специальных встроенных функций
-
Ruby вызывает команду напрямую без оболочки
Вы можете принудительно вызвать оболочку, добавив “;” в строку (потому что “;” — метасимвол).
Обратите внимание, что это поведение можно наблюдать по pid, полученному (значение возврата spawn() и IO#pid для IO.popen), которое является pid вызванной команды, а не оболочки.
Во втором формате (exec("command1", "arg1",
...)), первый элемент принимается как имя команды, а остальные передаются как параметры команде без расширения оболочки.
В третьем формате (exec(["command", "argv0"],
"arg1", ...)), начиная с массива из двух элементов в начале команды, первый элемент — это команда, которая должна быть выполнена, а второй аргумент используется как значение argv[0], которое может отображаться в списках процессов.
Для выполнения команды используются вызовы системы exec(2), поэтому выполняемая команда может унаследовать часть среды исходной программы (включая открытые дескрипторы файлов).
Это поведение изменяется заданными параметрами env и options. Подробности см. в ::spawn.
Если команда не выполняется (обычно Errno::ENOENT при её отсутствии), возникает исключение SystemCallError.
Этот метод изменяет атрибуты процесса в соответствии с заданными options перед вызовом системы exec(2). Подробнее об указанных options см. в ::spawn.
Изменённые атрибуты могут сохраняться, когда вызов системы exec(2) терпит неудачу.
Например, жёсткие лимиты ресурсов не восстанавливаются.
Рассмотрите возможность создания дочернего процесса с помощью ::spawn или Kernel#system, если это неприемлемо.
exec "echo *" # echoes list of files in current directory # never get here exec "echo", "*" # echoes an asterisk # never get here
VALUE
rb_f_exit(int argc, const VALUE *argv)
{
VALUE status;
int istatus;
if (argc > 0 && rb_scan_args(argc, argv, "01", &status) == 1) {
istatus = exit_status_code(status);
}
else {
istatus = EXIT_SUCCESS;
}
rb_exit(istatus);
UNREACHABLE;
} Инициализирует завершение скрипта Ruby, выбросив исключение SystemExit. Это исключение может быть перехвачено. Необязательный параметр используется для возвращения кода состояния в вызывающую среду. Значения 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 выполняет все функции at_exit (см. Kernel::at_exit) и выполняет все финализаторы объектов (см. ObjectSpace.define_finalizer).
at_exit { puts "at_exit function" }
ObjectSpace.define_finalizer("string", proc { puts "in finalizer" })
exit
выводит:
at_exit function in finalizer
static VALUE
rb_f_exit_bang(int argc, VALUE *argv, VALUE obj)
{
VALUE status;
int istatus;
if (argc > 0 && rb_scan_args(argc, argv, "01", &status) == 1) {
istatus = exit_status_code(status);
}
else {
istatus = EXIT_FAILURE;
}
_exit(istatus);
UNREACHABLE;
} Немедленно завершает процесс. Обработчики выхода не выполняются. status возвращается в базовую систему в качестве кода выхода.
Process.exit!(true)
static VALUE
rb_f_fork(VALUE obj)
{
rb_pid_t pid;
switch (pid = rb_fork_ruby(NULL)) {
case 0:
rb_thread_atfork();
if (rb_block_given_p()) {
int status;
rb_protect(rb_yield, Qundef, &status);
ruby_stop(status);
}
return Qnil;
case -1:
rb_sys_fail("fork(2)");
return Qnil;
default:
return PIDT2NUM(pid);
}
} Создаёт дочерний процесс. Если указан блок, этот блок выполняется в дочернем процессе, и дочерний процесс завершается со статусом ноль. В противном случае вызов fork возвращается дважды, один раз в родительском процессе, возвращая идентификатор процесса дочернего процесса, и один раз в дочернем процессе, возвращая nil. Дочерний процесс может завершиться с помощью Kernel.exit! для предотвращения выполнения каких-либо функций at_exit. Родительский процесс должен использовать Process.wait для сбора статусов завершения своих дочерних процессов или использовать Process.detach для регистрации отсутствия интереса к их статусам; в противном случае операционная система может накапливать процессы-зомби.
Поток, вызывающий fork, является единственным потоком в созданном дочернем процессе. fork не копирует другие потоки.
Если fork недоступен, Process.respond_to?(:fork) возвращает false.
Обратите внимание, что fork(2) недоступен на некоторых платформах, таких как Windows и NetBSD 4. Поэтому вы должны использовать 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);
} Возвращает идентификатор группы процессов для данного идентификатора процесса. Не доступно на всех платформах.
Process.getpgid(Process.ppid()) #=> 25527
static VALUE
proc_getpgrp(void)
{
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_PGRP, Process::PRIO_USER, или Process::PRIO_PROCESS. integer — это идентификатор, указывающий на конкретный процесс, группу процессов или пользователя (идентификатор 0 означает текущий). Более низкие приоритеты предпочтительнее для планирования. Недоступно на всех платформах.
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));
} Получает ограничение ресурсов процесса. cur_limit означает текущий (мягкий) лимит, а max_limit означает максимальный (жесткий) лимит.
resource указывает тип ресурса, который необходимо ограничить. Он задается в виде символа, такого как :CORE, строки, такой как "CORE", или константы, такой как Process::RLIMIT_CORE. См. ::setrlimit для получения подробной информации.
cur_limit и max_limit могут быть Process::RLIM_INFINITY, Process::RLIM_SAVED_MAX или Process::RLIM_SAVED_CUR. См. ::setrlimit и руководство по системе getrlimit(2) для получения подробной информации.
static VALUE
proc_getsid(int argc, VALUE *argv)
{
rb_pid_t sid;
VALUE pid;
rb_scan_args(argc, argv, "01", &pid);
if (NIL_P(pid))
pid = INT2FIX(0);
sid = getsid(NUM2PIDT(pid));
if (sid < 0) rb_sys_fail(0);
return PIDT2NUM(sid);
} Возвращает идентификатор сессии для указанного идентификатора процесса. Если не указан, возвращает текущий идентификатор сессии процесса. Недоступно на всех платформах.
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 #=> 500
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);
} Устанавливает идентификатор группы для этого процесса.
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;
} Получает Array идентификаторов групп в дополнительном списке доступа к группам для этого процесса.
Process.groups #=> [27, 6, 10, 11]
static VALUE
proc_setgroups(VALUE obj, VALUE ary)
{
int ngroups, i;
rb_gid_t *groups;
VALUE tmp;
PREPARE_GETGRNAM;
Check_Type(ary, T_ARRAY);
ngroups = RARRAY_LENINT(ary);
if (ngroups > maxgroups())
rb_raise(rb_eArgError, "too many groups, %d max", maxgroups());
groups = ALLOCV_N(rb_gid_t, tmp, ngroups);
for (i = 0; i < ngroups; i++) {
VALUE g = RARRAY_AREF(ary, i);
groups[i] = OBJ2GID1(g);
}
FINISH_GETGRNAM;
if (setgroups(ngroups, groups) == -1) /* ngroups <= maxgroups */
rb_sys_fail(0);
ALLOCV_END(tmp);
return proc_getgroups(obj);
} Устанавливает дополнительный список доступа к группам в данный Array идентификаторов групп.
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(StringValuePtr(uname), OBJ2GID(base_grp)) != 0) {
rb_sys_fail(0);
}
return proc_getgroups(obj);
} Инициализирует дополнительный список доступа к группам, читая системную базу данных групп и используя все группы, членами которых является данный пользователь. Группа с указанным gid также добавляется в список. Возвращает результирующий Array идентификаторов всех групп в дополнительном списке доступа к группам. Недоступно на всех платформах.
Process.groups #=> [0, 1, 2, 3, 4, 6, 10, 11, 20, 26, 27] Process.initgroups( "mgranger", 30 ) #=> [30, 6, 10, 11] Process.groups #=> [30, 6, 10, 11]
VALUE
rb_f_kill(int argc, const VALUE *argv)
{
#ifndef HAVE_KILLPG
#define killpg(pg, sig) kill(-(pg), (sig))
#endif
int negative = 0;
int sig;
int i;
VALUE str;
const char *s;
rb_check_arity(argc, 2, UNLIMITED_ARGUMENTS);
switch (TYPE(argv[0])) {
case T_FIXNUM:
sig = FIX2INT(argv[0]);
break;
case T_SYMBOL:
str = rb_sym2str(argv[0]);
goto str_signal;
case T_STRING:
str = argv[0];
str_signal:
s = RSTRING_PTR(str);
if (s[0] == '-') {
negative++;
s++;
}
if (strncmp(signame_prefix, s, sizeof(signame_prefix)) == 0)
s += 3;
if ((sig = signm2signo(s)) == 0) {
long ofs = s - RSTRING_PTR(str);
if (ofs) str = rb_str_subseq(str, ofs, RSTRING_LEN(str)-ofs);
rb_raise(rb_eArgError, "unsupported name `SIG%"PRIsVALUE"'", str);
}
if (negative)
sig = -sig;
break;
default:
str = rb_check_string_type(argv[0]);
if (!NIL_P(str)) {
goto str_signal;
}
rb_raise(rb_eArgError, "bad signal type %s",
rb_obj_classname(argv[0]));
break;
}
if (argc <= 1) return INT2FIX(0);
if (sig < 0) {
sig = -sig;
for (i=1; i<argc; i++) {
if (killpg(NUM2PIDT(argv[i]), sig) < 0)
rb_sys_fail(0);
}
}
else {
const rb_pid_t self = (GET_THREAD() == GET_VM()->main_thread) ? getpid() : -1;
int wakeup = 0;
for (i=1; i<argc; i++) {
rb_pid_t pid = NUM2PIDT(argv[i]);
if ((sig != 0) && (self != -1) && (pid == self)) {
int t;
/*
* When target pid is self, many caller assume signal will be
* delivered immediately and synchronously.
*/
switch (sig) {
case SIGSEGV:
#ifdef SIGBUS
case SIGBUS:
#endif
#ifdef SIGKILL
case SIGKILL:
#endif
#ifdef SIGSTOP
case SIGSTOP:
#endif
ruby_kill(pid, sig);
break;
default:
t = signal_ignored(sig);
if (t) {
if (t < 0 && kill(pid, sig))
rb_sys_fail(0);
break;
}
signal_enque(sig);
wakeup = 1;
}
}
else if (kill(pid, sig) < 0) {
rb_sys_fail(0);
}
}
if (wakeup) {
rb_threadptr_check_signal(GET_VM()->main_thread);
}
}
rb_thread_execute_interrupts(rb_thread_current());
return INT2FIX(i-1);
} Посылает заданный сигнал указанному(ым) идентификатору(ам) процесса(ов), если pid положителен. Если pid равен нулю, signal посылается всем процессам, чей идентификатор группы равен идентификатору группы процесса. signal может быть целым числом номера сигнала или именем сигнала POSIX (как с префиксом SIG, так и без него). Если signal отрицателен (или начинается с минуса), убивает группы процессов, а не процессы. Не все сигналы доступны на всех платформах. Ключи и значения Signal.list — это известные имена и номера сигналов соответственно.
pid = fork do
Signal.trap("HUP") { puts "Ouch!"; exit }
# ... do some work ...
end
# ...
Process.kill("HUP", pid)
Process.wait
возвращает:
Ouch!
Если signal — целое число, но неверно для сигнала, будет вызвано исключение Errno::EINVAL или RangeError. В противном случае, если signal не является String или Symbol, и известным именем сигнала, будет вызвано исключение ArgumentError.
Также, Errno::ESRCH или RangeError для неверного pid, Errno::EPERM при неудаче из-за отсутствия привилегий, будут вызваны исключения. В этих случаях сигналы могли быть отправлены предыдущим процессам.
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 shold 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
get_pid(void)
{
return PIDT2NUM(getpid());
} Возвращает идентификатор процесса этого процесса. Недоступно на всех платформах.
Process.pid #=> 27415
static VALUE
get_ppid(void)
{
return PIDT2NUM(getppid());
} Возвращает идентификатор процесса родителя этого процесса. Возвращает ненадежное значение в Win32/64. Недоступно на всех платформах.
puts "I am #{Process.pid}"
Process.fork { puts "Dad is #{Process.ppid}" }
возвращает:
I am 27417 Dad is 27417
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 (0 указывает на этот процесс) на integer. Недоступно на всех платформах.
static VALUE
proc_setpgrp(void)
{
/* 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)
{
StringValue(title);
setproctitle("%.*s", RSTRING_LENINT(title), RSTRING_PTR(title));
return 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_scan_args(argc, argv, "21", &resource, &rlim_cur, &rlim_max);
if (rlim_max == Qnil)
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;
} Устанавливает предел ресурсов процесса. cur_limit означает текущий (мягкий) предел, а max_limit — максимальный (жёсткий) предел.
Если max_limit не указан, используется cur_limit.
resource указывает тип ресурса, который необходимо ограничить. Он должен быть символом, например :CORE, строкой, например "CORE", или константой, например Process::RLIMIT_CORE. Доступные ресурсы зависят от ОС. Ruby может поддерживать следующие ресурсы.
- AS
-
общий доступный объем памяти (байты) (SUSv3, NetBSD, FreeBSD, OpenBSD, но не 4.4BSD-Lite)
- CORE
-
размер файла ядра (байты) (SUSv3)
- CPU
-
время работы процессора (секунды) (SUSv3)
- DATA
-
размер сегмента данных (байты) (SUSv3)
- FSIZE
-
размер файла (байты) (SUSv3)
- MEMLOCK
-
общий размер для mlock(2) (байты) (4.4BSD, GNU/Linux)
- MSGQUEUE
-
выделение для POSIX сообщений очереди (байты) (GNU/Linux)
- NICE
-
верхний предел значения nice(2) процесса (число) (GNU/Linux)
- NOFILE
-
дескрипторы файлов (число) (SUSv3)
- NPROC
-
количество процессов для пользователя (число) (4.4BSD, GNU/Linux)
- RSS
-
размер резидентной памяти (байты) (4.2BSD, GNU/Linux)
- RTPRIO
-
верхний предел приоритета реального времени процесса (число) (GNU/Linux)
- RTTIME
-
время работы процессора для процесса реального времени (мкс) (GNU/Linux)
- SBSIZE
-
все буферы сокетов (байты) (NetBSD, FreeBSD)
- SIGPENDING
-
допустимое количество очереди сигналов (сигналы) (GNU/Linux)
- STACK
-
размер стека (байты) (SUSv3)
cur_limit и max_limit могут быть :INFINITY, "INFINITY" или Process::RLIM_INFINITY, что означает, что ресурс не ограничен. Они также могут быть Process::RLIM_SAVED_MAX, Process::RLIM_SAVED_CUR и соответствующие символы и строки. Подробнее см. руководство system setrlimit(2).
Следующий пример повышает мягкий предел размера файла ядра до жёсткого предела, чтобы сделать возможным создание файла ядра.
Process.setrlimit(:CORE, Process.getrlimit(:CORE)[1])
static VALUE
proc_setsid(void)
{
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)
{
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);
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
} spawn выполняет указанную команду и возвращает её pid.
pid = spawn("tar xf ruby-2.0.0-p195.tar.bz2")
Process.wait pid
pid = spawn(RbConfig.ruby, "-eputs'Hello, world!'")
Process.wait pid
Этот метод похож на Kernel#system, но он не ждёт завершения команды.
Родительский процесс должен использовать Process.wait для получения статуса завершения своего дочернего процесса или использовать Process.detach для регистрации отсутствия интереса к их статусу; в противном случае операционная система может накапливать процессы-зомби.
spawn имеет множество опций для указания атрибутов процесса:
env: hash
name => val : set the environment variable
name => nil : unset the environment variable
the keys and the values except for +nil+ must be strings.
command...:
commandline : command line string which is passed to the standard shell
cmdname, arg1, ... : command name and one or more arguments (This form does not use the shell. See below for caveats.)
[cmdname, argv0], arg1, ... : command name, argv[0] and zero or more arguments (no shell)
options: hash
clearing environment variables:
:unsetenv_others => true : clear environment variables except specified by env
:unsetenv_others => false : don't clear (default)
process group:
:pgroup => true or 0 : make a new process group
:pgroup => pgid : join the specified process group
:pgroup => nil : don't change the process group (default)
create new process group: Windows only
:new_pgroup => true : the new process is the root process of a new process group
:new_pgroup => false : don't create a new process group (default)
resource limit: resourcename is core, cpu, data, etc. See Process.setrlimit.
:rlimit_resourcename => limit
:rlimit_resourcename => [cur_limit, max_limit]
umask:
:umask => int
redirection:
key:
FD : single file descriptor in child process
[FD, FD, ...] : multiple file descriptor in child process
value:
FD : redirect to the file descriptor in parent process
string : redirect to file with open(string, "r" or "w")
[string] : redirect to file with open(string, File::RDONLY)
[string, open_mode] : redirect to file with open(string, open_mode, 0644)
[string, open_mode, perm] : redirect to file with open(string, open_mode, perm)
[:child, FD] : redirect to the redirected file descriptor
:close : close the file descriptor in child process
FD is one of follows
:in : the file descriptor 0 which is the standard input
:out : the file descriptor 1 which is the standard output
:err : the file descriptor 2 which is the standard error
integer : the file descriptor of specified the integer
io : the file descriptor specified as io.fileno
file descriptor inheritance: close non-redirected non-standard fds (3, 4, 5, ...) or not
:close_others => true : don't inherit
current directory:
:chdir => str
The 'cmdname, arg1, ...' form does not use the shell. However,
on different OSes, different things are provided as built-in
commands. An example of this is 'echo', which is a built-in
on Windows, but is a normal program on Linux and Mac OS X.
This means that `Process.spawn 'echo', '%Path%'` will display
the contents of the `%Path%` environment variable on Windows,
but `Process.spawn 'echo', '$PATH'` prints the literal '$PATH'. Если в качестве env задан хэш, среда обновляется с помощью env перед exec(2) в дочернем процессе. Если пара в env имеет nil в качестве значения, переменная удаляется.
# set FOO as BAR and unset BAZ.
pid = spawn({"FOO"=>"BAR", "BAZ"=>nil}, command)
Если в качестве options задан хэш, он задаёт группу процессов, создание новой группы процессов, пределы ресурсов, текущую директорию, umask и перенаправления для дочернего процесса. Также можно указать очистку переменных среды.
Ключ :unsetenv_others в options указывает на очистку переменных среды, за исключением указанных env.
pid = spawn(command, :unsetenv_others=>true) # no environment variable
pid = spawn({"FOO"=>"BAR"}, command, :unsetenv_others=>true) # FOO only
Ключ :pgroup в options задаёт группу процессов. Соответствующее значение должно быть true, ноль, положительным целым числом или nil. true и ноль заставляют процесс стать лидером группы процессов. Положительное целое число приводит к присоединению процесса к указанной группе. По умолчанию, nil, процесс остаётся в той же группе.
pid = spawn(command, :pgroup=>true) # process leader pid = spawn(command, :pgroup=>10) # belongs to the process group 10
Ключ :new_pgroup в options указывает на передачу флага CREATE_NEW_PROCESS_GROUP в CreateProcessW() (API Windows). Эта опция только для Windows. true означает, что новый процесс является корневым процессом новой группы процессов. У нового процесса отключен CTRL+C. Этот флаг необходим для Process.kill(:SIGINT,
pid) в дочернем процессе. По умолчанию :new_pgroup равен false.
pid = spawn(command, :new_pgroup=>true) # new process group pid = spawn(command, :new_pgroup=>false) # same process group
Ключ :rlimit_foo указывает предел ресурса. foo должен быть одним из типов ресурсов, например core. Соответствующее значение должно быть целым числом или массивом, содержащим одно или два целых числа: аналогично аргументам cur_limit и max_limit для ::setrlimit.
cur, max = Process.getrlimit(:CORE) pid = spawn(command, :rlimit_core=>[0,max]) # disable core temporary. pid = spawn(command, :rlimit_core=>max) # enable core dump pid = spawn(command, :rlimit_core=>0) # never dump core.
Ключ :umask в options задаёт umask.
pid = spawn(command, :umask=>077)
Ключи :in, :out, :err, целое число, IO и массив задают перенаправление. Перенаправление сопоставляет дескриптор файла в дочернем процессе.
Например, stderr можно объединить со stdout следующим образом:
pid = spawn(command, :err=>:out) pid = spawn(command, 2=>1) pid = spawn(command, STDERR=>:out) pid = spawn(command, STDERR=>STDOUT)
Ключи хэша указывают дескриптор файла в дочернем процессе, запущенном spawn. :err, 2 и STDERR указывают стандартный поток ошибок (stderr).
Значения хэша указывают дескриптор файла в родительском процессе, вызывающем spawn. :out, 1 и STDOUT указывают стандартный поток вывода (stdout).
В приведённом примере стандартный поток вывода в дочернем процессе не указан. Поэтому он наследуется от родительского процесса.
Стандартный поток ввода (stdin) можно указать как :in, 0 и STDIN.
Можно указать имя файла как значение хэша.
pid = spawn(command, :in=>"/dev/null") # read mode pid = spawn(command, :out=>"/dev/null") # write mode pid = spawn(command, :err=>"log") # write mode pid = spawn(command, [:out, :err]=>"/dev/null") # write mode pid = spawn(command, 3=>"/dev/null") # read mode
Для stdout и stderr (и их комбинации) он открывается в режиме записи. В противном случае используется режим чтения.
Для явного указания флагов и разрешений при создании файла используется массив.
pid = spawn(command, :in=>["file"]) # read mode is assumed pid = spawn(command, :in=>["file", "r"]) pid = spawn(command, :out=>["log", "w"]) # 0644 assumed pid = spawn(command, :out=>["log", "w", 0600]) pid = spawn(command, :out=>["log", File::WRONLY|File::EXCL|File::CREAT, 0600])
Массив указывает имя файла, флаги и разрешения. Флаги могут быть строкой или целым числом. Если флаги опущены или nil, предполагается File::RDONLY. Разрешения должны быть целым числом. Если разрешения опущены или nil, предполагается 0644.
Если в качестве ключа хэша указан массив IO и целых чисел, все элементы перенаправляются.
# stdout and stderr is redirected to log file. # The file "log" is opened just once. pid = spawn(command, [:out, :err]=>["log", "w"])
Ещё один способ объединения нескольких дескрипторов файлов — [:child, fd]. [:child, fd] означает дескриптор файла в дочернем процессе. Это отличается от fd. Например, :err=>:out означает перенаправление stderr дочернего процесса в stdout родительского процесса. Но :err=>[:child, :out] означает перенаправление stderr дочернего процесса в stdout дочернего процесса. Они отличаются, если stdout перенаправляется в дочернем процессе, как показано ниже.
# stdout and stderr is redirected to log file. # The file "log" is opened just once. pid = spawn(command, :out=>["log", "w"], :err=>[:child, :out])
[:child, :out] можно использовать для объединения stderr в stdout в IO.popen. В этом случае IO.popen перенаправляет stdout в канал в дочернем процессе, а [:child, :out] ссылается на перенаправленный stdout.
io = IO.popen(["sh", "-c", "echo out; echo err >&2", :err=>[:child, :out]]) p io.read #=> "out\nerr\n"
Ключ :chdir в options задаёт текущую директорию.
pid = spawn(command, :chdir=>"/var/tmp")
spawn по умолчанию закрывает все неопределённые дескрипторы, кроме стандартных (0, 1 и 2). Это поведение задано опцией :close_others. :close_others не влияет на стандартные дескрипторы, которые закрываются только если :close указана явно.
pid = spawn(command, :close_others=>true) # close 3,4,5,... (default) pid = spawn(command, :close_others=>false) # don't close 3,4,5,...
:close_others по умолчанию равно true для spawn и IO.popen.
Обратите внимание, что дескрипторы файлов, для которых флаг close-on-exec уже установлен, закрываются независимо от опции :close_others.
Поэтому IO.pipe и spawn могут использоваться как IO.popen.
# similar to r = IO.popen(command) r, w = IO.pipe pid = spawn(command, :out=>w) # r, w is closed in the child process. w.close
:close указывается как значение хэша для закрытия дескриптора файла индивидуально.
f = open(foo) system(command, f=>:close) # don't inherit f.
Если необходимо унаследовать дескриптор файла, можно использовать io=>io.
# valgrind has --log-fd option for log destination.
# log_w=>log_w indicates log_w.fileno inherits to child process.
log_r, log_w = IO.pipe
pid = spawn("valgrind", "--log-fd=#{log_w.fileno}", "echo", "a", log_w=>log_w)
log_w.close
p log_r.read
Также возможно обмен дескрипторами файлов.
pid = spawn(command, :out=>:err, :err=>:out)
Ключи хэша указывают дескрипторы файлов в дочернем процессе. Значения хэша — дескрипторы файлов в родительском процессе. Таким образом, вышеприведённый пример указывает обмен stdout и stderr. Внутренне spawn использует дополнительный дескриптор файла для разрешения подобного циклического сопоставления дескрипторов файлов.
См. Kernel.exec для стандартной оболочки.
VALUE
rb_proc_times(VALUE obj)
{
const double hertz = get_clk_tck();
struct tms buf;
VALUE utime, stime, cutime, cstime, ret;
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);
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;
} Возвращает структуру Tms (см. Process::Tms) содержащую время работы процессора в пользовательском режиме и в системном режиме для этого процесса, а также для дочерних процессов.
t = Process.times [ t.utime, t.stime, t.cutime, t.cstime ] #=> [0.0, 0.02, 0.00, 0.00]
static VALUE
proc_getuid(VALUE obj)
{
rb_uid_t uid = getuid();
return UIDT2NUM(uid);
} Возвращает действительный идентификатор пользователя этого процесса.
Process.uid #=> 501
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;
} Устанавливает (пользовательский) идентификатор пользователя для этого процесса. Не доступно на всех платформах.
static VALUE
proc_wait(int argc, VALUE *argv)
{
VALUE vpid, vflags;
rb_pid_t pid;
int flags, status;
flags = 0;
if (argc == 0) {
pid = -1;
}
else {
rb_scan_args(argc, argv, "02", &vpid, &vflags);
pid = NUM2PIDT(vpid);
if (argc == 2 && !NIL_P(vflags)) {
flags = NUM2UINT(vflags);
}
}
if ((pid = rb_waitpid(pid, &status, flags)) < 0)
rb_sys_fail(0);
if (pid == 0) {
rb_last_status_clear();
return Qnil;
}
return PIDT2NUM(pid);
} Ожидает завершения дочернего процесса, возвращает его идентификатор процесса и устанавливает $? в объект Process::Status, содержащий информацию об этом процессе. Какой дочерний процесс ожидать, зависит от значения pid:
- > 0
-
Ожидает дочернего процесса с идентификатором процесса, равным pid.
- 0
-
Ожидает любой дочерний процесс, группа процессов которого совпадает с группой процессов вызывающего процесса.
- -1
-
Ожидает любой дочерний процесс (значение по умолчанию, если pid не указан).
- < -1
-
Ожидает любой дочерний процесс, группа процессов которого совпадает с абсолютным значением pid.
Аргумент flags может быть логическим ИЛИ значений флагов Process::WNOHANG (не блокировать, если нет доступных дочерних процессов) или Process::WUNTRACED (возвращать приостановленные дочерние процессы, которые ещё не были сообщены). Не все флаги доступны на всех платформах, но значение нуля будет работать на всех платформах.
Вызов этого метода вызывает SystemCallError, если нет дочерних процессов. Не доступно на всех платформах.
include Process
fork { exit 99 } #=> 27429
wait #=> 27429
$?.exitstatus #=> 99
pid = fork { sleep 3 } #=> 27440
Time.now #=> 2008-03-08 19:56:16 +0900
waitpid(pid, Process::WNOHANG) #=> nil
Time.now #=> 2008-03-08 19:56:16 +0900
waitpid(pid, 0) #=> 27440
Time.now #=> 2008-03-08 19:56:19 +0900
static VALUE
proc_wait2(int argc, VALUE *argv)
{
VALUE pid = proc_wait(argc, argv);
if (NIL_P(pid)) return Qnil;
return rb_assoc_new(pid, rb_last_status_get());
} Ожидает завершения дочернего процесса (см. ::waitpid для точного смысла) и возвращает массив, содержащий идентификатор процесса и код завершения (объект Process::Status) этого дочернего процесса. Вызывает SystemCallError, если нет дочерних процессов.
Process.fork { exit 99 } #=> 27437
pid, status = Process.wait2
pid #=> 27437
status.exitstatus #=> 99
static VALUE
proc_waitall(void)
{
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/status (где status — объект Process::Status).
fork { sleep 0.2; exit 2 } #=> 27432
fork { sleep 0.1; exit 1 } #=> 27433
fork { exit 0 } #=> 27434
p Process.waitall
производит:
[[30982, #<Process::Status: pid 30982 exit 0>], [30979, #<Process::Status: pid 30979 exit 1>], [30976, #<Process::Status: pid 30976 exit 2>]]
static VALUE
proc_wait(int argc, VALUE *argv)
{
VALUE vpid, vflags;
rb_pid_t pid;
int flags, status;
flags = 0;
if (argc == 0) {
pid = -1;
}
else {
rb_scan_args(argc, argv, "02", &vpid, &vflags);
pid = NUM2PIDT(vpid);
if (argc == 2 && !NIL_P(vflags)) {
flags = NUM2UINT(vflags);
}
}
if ((pid = rb_waitpid(pid, &status, flags)) < 0)
rb_sys_fail(0);
if (pid == 0) {
rb_last_status_clear();
return Qnil;
}
return PIDT2NUM(pid);
} Ожидает завершения дочернего процесса, возвращает его идентификатор процесса и устанавливает $? в объект Process::Status, содержащий информацию об этом процессе. Какой дочерний процесс ожидать, зависит от значения pid:
- > 0
-
Ожидает дочерний процесс с идентификатором процесса, равным pid.
- 0
-
Ожидает любой дочерний процесс, группа процессов которого совпадает с группой процессов вызывающего процесса.
- -1
-
Ожидает любой дочерний процесс (значение по умолчанию, если pid не указан).
- < -1
-
Ожидает любой дочерний процесс, группа процессов которого совпадает с абсолютным значением pid.
Аргумент flags может быть логическим ИЛИ значений флагов Process::WNOHANG (не блокировать, если нет доступных дочерних процессов) или Process::WUNTRACED (возвращать приостановленные дочерние процессы, которые ещё не были сообщены). Не все флаги доступны на всех платформах, но значение нуля будет работать на всех платформах.
Вызов этого метода вызывает SystemCallError, если нет дочерних процессов. Не доступно на всех платформах.
include Process
fork { exit 99 } #=> 27429
wait #=> 27429
$?.exitstatus #=> 99
pid = fork { sleep 3 } #=> 27440
Time.now #=> 2008-03-08 19:56:16 +0900
waitpid(pid, Process::WNOHANG) #=> nil
Time.now #=> 2008-03-08 19:56:16 +0900
waitpid(pid, 0) #=> 27440
Time.now #=> 2008-03-08 19:56:19 +0900
static VALUE
proc_wait2(int argc, VALUE *argv)
{
VALUE pid = proc_wait(argc, argv);
if (NIL_P(pid)) return Qnil;
return rb_assoc_new(pid, rb_last_status_get());
} Ожидает завершения дочернего процесса (см. ::waitpid для точного смысла) и возвращает массив, содержащий идентификатор процесса и код завершения (объект Process::Status ) этого дочернего процесса. Вызывает SystemCallError, если нет дочерних процессов.
Process.fork { exit 99 } #=> 27437
pid, status = Process.wait2
pid #=> 27437
status.exitstatus #=> 99
Ruby Core © 1993–2017 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.