Ruby IO.popen с "-", что происходит под капотом?
Я пытаюсь понять IO.popen, когда его команда "-"
запускает новый интерпретатор Ruby.
В этом вопросе мало материала, и я медленно прохожу через них, главным образом из-за меня, поскольку я только код для удовольствия.
Насколько я понял, когда вызывается IO.popen("-", "w+") {|f| ...}
- что с блоком - этот блок будет выполняться как родительским, так и дочерним процессом. Разница в том, что родительский процесс получит объект IO в результате, но ребенок получает только Nil. Это легко, мне нужно проверить |f|
в блоке, а когда это Nil, выполнение выполняется в дочернем процессе, когда оно не равно nil, выполнение выполняется в родительском. Поэтому мне нужно написать оба кода для родителя и дочернего элемента, разделенных символом if
.
На этот раз это помогает мне понять проблему, что блок является частью команды IO.popen.
У меня есть этот код:
pipe = IO.popen("-","w+")
# puts "This line will break functionality if uncommented"
if pipe != nil then
pipe.puts "PID: #{Process.pid}"
$stderr.puts "Parent from child: #{pipe.gets.chomp}"
else
$stderr.puts "Child PID: #{Process.pid} and Parent #{gets.chomp}"
puts "M'kay"
end
Вопросы:
- Что решает, какой процесс выполняется первым? Если они должны были добавить файл, он будет уязвим для условий гонки?
- Почему вторая строка нарушает код? Команда
pipe = IO.popen...
не должна быть связана с блоком if..else..end
, но они есть. Для меня pipe
- это дескриптор файла (как в старой Turbo Pascal), который сначала где-то где-то где-то где-то помечен, а затем обрабатывается в другом месте.
Ответы
Ответ 1
Никто не решает, какой процесс выполняется первым. Детский процесс может запускаться первым, или родительский процесс может запускаться с первой ОС, может планировать их в любом случае.
Это означает, что родительский процесс может завершиться до завершения дочернего процесса. Когда родительский процесс завершается, труба к нему закрывается, и когда дочерний процесс записывает на него, он получает исключение. Что происходит в вашем коде.
Почему это не происходит без комментариев? Когда вы вызываете gets
в родительском процессе, он ждет, пока ребенок не напишет строку в трубе. Это означает, что родительский элемент не будет закончен, пока ребенок не напишет строку в трубе, и это не учитывает проблему. Однако, когда вы печатаете две строки, вероятность того, что родительский процесс завершится до того, как ребенок выполнит второй puts "M'kay"
.
Попробуйте использовать следующий код:
pipe = IO.popen("-","w+")
puts "This line will not break functionality"
puts "This line will not break functionality"
puts "This line will not break functionality"
if pipe != nil then
pipe.puts "PID: #{Process.pid}"
while line = pipe.gets
$stderr.puts "Parent from child: #{line.chomp}"
end
else
$stderr.puts "Child PID: #{Process.pid} and Parent #{gets.chomp}"
puts "M'kay"
end
Он ждет, пока ребенок не закроет трубку (тогда pipe.gets
вернет nil
), что произойдет, после чего оно завершается, и это гарантирует, что он больше не будет писать там.