Стандартные потоки и vfork


Я немного играю с функциями fork/vfork, и есть кое-что, что меня озадачивает. В книге Стивенса написано, что:

Обратите внимание на рис. 8.3, что вместо exit мы вызываем _exit.

Как мы описали в разделе 7.3, _exit не выполняет никакой промывки стандартных буферов ввода-вывода. Если вместо этого мы вызовем exit, результаты будут неопределенными. В зависимости от реализации стандартной библиотеки ввода-вывода мы можем не видеть разницы в выходных данных, или мы можем найдите, что выходные данные из родительского printf исчезли. Если дочерний элемент вызывает exit, реализация сбрасывает стандартные потоки ввода-вывода. Если это единственное действие, выполняемое библиотекой, то мы не увидим никакой разницы с выводом, генерируемым дочерним объектом _exit. Однако если реализация также закрывает стандартные потоки ввода-вывода, то память, представляющая объект FILE для стандартного вывода, будет очищена. Потому что ребенок занимает адресное пространство родителя, когда родитель возобновит работу и вызовет printf, выходные данные не появятся, и printf вернет значение -1. Обратите внимание, что родительский STDOUT_FILENO все еще действителен, так как ребенок получает копию массива дескрипторов родительского файла (см. рис.8.2). Большинство современных реализаций выхода не будет беспокоить, чтобы закрыть потоки. Поскольку процесс вот-вот завершится, ядро закроет все файловые дескрипторы, открытые в процессе. Закрытие их в библиотеке просто добавляет накладные расходы без какой-либо пользы.

Поэтому я попытался проверить, могу ли я получить printf ошибку, в моей инструкции vfork есть:

Все открытые потоки stdio(3) промываются и закрываются. Файлы, созданные tmpfile (3), удаляются.

Но когда я компилирую и выполняю эту программу:

  #include<stdio.h>
  #include<stdlib.h>
  #include<unistd.h>
  #include<sys/types.h>
  #include<sys/wait.h>

  int main()
{
  int s;
  pid_t ret;
  if (vfork() == 0)
  {
      //abort();
      exit(6);
  }
  else
  {
      ret=wait(&s);
      printf("termination status to %dn",s);
      if (WIFEXITED(s))
          printf("normalnie, status to %dn",WEXITSTATUS(s));
  }
  return 0;
}

Все работает нормально, я не получаю никаких ошибок printf. Почему это так?

1 3

1 ответ:

В конце процитированного вами абзаца говорится:

В большинстве современных реализаций выхода не будет беспокоить, чтобы закрыть потоки. Поскольку процесс вот-вот завершится, ядро закроет все файловые дескрипторы, открытые в процессе. Закрытие их в библиотеке просто добавляет накладные расходы без какой-либо пользы.

Это, скорее всего, то, что происходит. Ваша ОС фактически не закрывает поток (но, вероятно, смывает его).

Важно не то, что exit делает здесь, его основополагающее понятие. Ребенок совместно использует память родителя и кадр стека. Это означает, что ребенок может очень легко изменить что-то, чего родитель не ожидал, что может легко привести к сбою или неправильному поведению родителя, когда он снова начнет работать. В справочной странице для vfork говорится, что единственное, что может сделать процесс, - это вызвать exit() или exec. Фактически, ребенок не должен даже выделять память или изменять какие-либо переменные.

Чтобы увидеть влияние этого, попробуйте положить vfork вызовите внутри функции и позвольте ребенку вернуть или изменить некоторые переменные там и посмотреть, что произойдет.