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