Ruby IO.popen с "-", что происходит под капотом?
Я пытаюсь понять IO.popen, когда его команда "-"
который запускает новый интерпретатор Ruby.
На эту тему не так много материала, и я медленно прохожу их, в основном из-за себя, потому что я пишу только для развлечения.
Насколько я понял, когда IO.popen("-", "w+") {|f| ...}
вызывается - это с блоком - этот блок будет запускаться как родительским, так и дочерним процессом. Разница в том, что в результате родительский процесс получит объект ввода-вывода, а дочерний - только ноль. Это легко, мне нужно проверить |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
Вопросы:
- Что решает, какой процесс запускается первым? Если они добавят файл, будет ли он уязвим к состоянию гонки?
- Почему 2-я строка ломает код?
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
), что происходит, затем завершается, и это гарантирует, что он больше не будет пытаться писать туда.