Ruby IO.popen with -, что происходит под капотом?

Я пытаюсь понять IO.popen, когда его команда "-" запускает новый интерпретатор Ruby.

По этой теме не так много материалов, и я медленно их прорабатываю, в основном из-за меня, поскольку я пишу код только для удовольствия.

Насколько я понял, когда вызывается IO.popen("-", "w+") {|f| ...} - это с блоком - этот блок будет запускаться как родительским, так и дочерним процессом. Разница в том, что в результате родительский процесс получит объект ввода-вывода, а дочерний процесс получит только 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), который сначала где-то определяется, а затем где-то манипулирует.

person karatedog    schedule 10.04.2011    source источник


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

person P Shved    schedule 10.04.2011