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), что происходит, затем завершается, и это гарантирует, что он больше не будет пытаться писать туда.

Другие вопросы по тегам