Как видите, при таком определении изменяется также поведение ограничителя %x.

В следующем примере мы добавили в конец команды конструкцию интерпретатора команд, которая перенаправляет стандартный вывод для ошибок в стандартный вывод:

alias old_execute `

def `(cmd)

 old_execute(cmd + ' 2>&1')

end

entries = `ls -l /tmp/foobar`

# '/tmp/foobar: No such file or directory '

Есть, конечно, и много других способов изменить стандартное поведение обратных кавычек.

14.1.3. Манипулирование процессами

В этом разделе мы обсудим манипулирование процессами, хотя создание нового процесса необязательно связано с запуском внешней программы. Основной способ создания нового процесса — это метод fork, название которого в соответствии с традицией UNIX подразумевает разветвление пути исполнения, напоминая развилку на дороге. (Отметим, что в базовом дистрибутиве Ruby метод fork на платформе Windows не поддерживается.)

Метод fork, находящийся в модуле Kernel (а также в модуле Process), не следует путать с одноименным методом экземпляра в классе Thread.

Существуют два способа вызвать метод fork. Первый похож на то, как это обычно делается в UNIX, — вызвать и проверить возвращенное значение. Если оно равно nil, мы находимся в дочернем процессе, в противном случае — в родительском. Родительскому процессу возвращается идентификатор дочернего процесса (pid).

pid = fork

if (pid == nil)

 puts 'Ага, я, должно быть, потомок.'

 puts 'Так и буду себя вести.'

else

 puts 'Я родитель.'

 puts 'Пора отказаться от детских штучек.'

end

В этом не слишком реалистичном примере выводимые строки могут чередоваться, а может случиться и так, что строки, выведенные родителем, появятся раньше. Но сейчас это несущественно.

Следует также отметить, что процесс-потомок может пережить своего родителя. Для потоков в Ruby это не так, но системные процессы — совсем другое дело.

Во втором варианте вызова метод fork принимает блок. Заключенный в блок код выполняется в контексте дочернего процесса. Так, предыдущий вариант можно было бы переписать следующим образом:

fork do

 puts 'Ага, я, должно быть, потомок.'

 puts 'Так и буду себя вести.'

end

puts 'Я родитель.'

puts 'Пора отказаться от детских штучек.'

Конечно, pid по-прежнему возвращается, мы просто не показали его.

Чтобы дождаться завершения процесса, мы можем вызвать метод wait из модуля Process. Он ждет завершения любого потомка и возвращает его идентификатор. Метод wait2 ведет себя аналогично, только возвращает массив, содержащий РМ, и сдвинутый влево код завершения.

Pid1 = fork { sleep 5; exit 3 }

Pid2 = fork { sleep 2; exit 3 }

Process.wait  # Возвращает pid2

Process.wait2 # Возвращает [pid1,768]

Чтобы дождаться завершения конкретного потомка, применяются методы waitpid и waitpid2.

pid3 = fork { sleep 5; exit 3 }

pid4 = fork { sleep 2; exit 3 }

Process.waitpid(pid4,Process::WNOHANG)   # Возвращает pid4

Process.waitpid2(pid3, Process::WNOHANG) # Возвращает [pid3,768]

Если второй параметр не задан, то вызов может блокировать программу (если такого потомка не существует). Второй параметр можно с помощью ИЛИ объединить с флагом Process::WUNTRACED, чтобы перехватывать остановленные процессы. Этот параметр системно зависим, поэкспериментируйте.

Метод exit! немедленно завершает процесс (не вызывая зарегистрированных обработчиков). Если задан целочисленный аргумент, то он возвращается в качестве кода завершения; по умолчанию подразумевается значение 1 (не 0).

pid1 = fork { exit! }   # Вернуть код завершения -1.

pid2 = fork { exit! 0 } # Вернуть код завершения 0.

Методы pid и ppid возвращают соответственно идентификатор текущего и родительского процессов.

proc1 = Process.pid

fork do

 if Process.ppid == proc1

  puts 'proc1 - мой родитель' # Печатается это сообщение.

 else

  puts 'Что происходит?'

 end

end

Метод kill служит для отправки процессу сигнала, как это понимается в UNIX. Первый параметр может быть целым числом, именем POSIX-сигнала с префиксом SIG или именем сигнала без префикса. Второй параметр — идентификатор процесса-получателя; если он равен нулю, подразумевается текущий процесс.

Process.kill(1,pid1)        # Послать сигнал 1 процессу pid1.

Process.kill ('HUP',pid2)   # Послать SIGHUP процессу pid2..

Process.kill('SIGHUP',pid2) # Послать SIGHUP процессу pid3.

Process.kill('SIGHUP',0)    # Послать SIGHUP самому себе.

Для обработки сигналов применяется метод Kernel.trap. Обычно он принимает номер или имя сигнала и подлежащий выполнению блок.

trap(1) { puts 'Перехвачен сигнал 1' }

Добавить отзыв
ВСЕ ОТЗЫВЫ О КНИГЕ В ИЗБРАННОЕ

0

Вы можете отметить интересные вам фрагменты текста, которые будут доступны по уникальной ссылке в адресной строке браузера.

Отметить Добавить цитату