труба, вилка и неблокирующий IPC

Итак, я пытался запустить системную команду (или exec или что-то еще) в дочернем процессе после fork() и передать ему некоторый ввод, а затем получить его вывод. Это выглядит так после fork(), а pc и cp — это каналы parent-child и child-parent.

  case 0:
        /* Child. */
        close(STDOUT_FILENO); /* Close current stdout. */
        dup2(cp[1], STDOUT_FILENO);

        close(STDIN_FILENO);
        dup2(pc[0], STDIN_FILENO);

        close( pc[1]);
        close( cp[0]);
        execlp("cat", "cat", NULL);
        exit(1);
  default:
        /* Parent. */
        /* Close what we don't need. */
        printf("Input to child:\n");

        string theinput("Hey there baby");
        write(pc[1], theinput.c_str(), theinput.size());
        close(pc[1]);

        cout << "The input : " << theinput << endl;


        printf("\nOutput from child:\n");
        close(cp[1]);
        while( read(cp[0], &ch, 1) == 1)
        {
           write(1, &ch, 1);
           outcount++;
        }

        exit(0);

Теперь он работает хорошо (если вам нужен код: http://pastebin.com/Fh7GrxYm), но когда я разговаривал с #posix на irc, они сходили с ума по поводу того, как это потенциально может заблокировать и как это «зависит от того, как себя чувствует ядро».

В блоге msdn была запись о том же: http://blogs.msdn.com/b/oldnewthing/archive/2011/07/07/10183884.aspx

Как можно предотвратить блокировку, если таковая имеется и т.д.?


person kamziro    schedule 08.07.2011    source источник


Ответы (1)


В вашем случае взаимоблокировка может возникнуть, когда родительский процесс достигает этой строки:

write(pc[1], theinput.c_str(), theinput.size());

Если «ввод» — это много данных, то родительский процесс может заполнить канал pc. Дочерний процесс (здесь cat) может прочитать часть, но не все. cat они вам ответят эхом. Но опять же, если данных много, они могут заполнить канал cp и будут блокироваться до тех пор, пока кто-нибудь не прочитает данные из этого канала. Этого никогда не произойдет, потому что родительский процесс заблокирован, ожидая опустошения канала pc, и никогда не достигнет кода, потребляющего содержимое канала cp. ТУПИК.

Как сказали ваши приятели IRC, произойдет это или нет, зависит от многих вещей, таких как количество задействованных данных, количество данных, которые канал может разместить до того, как он заблокируется (параметр, зависящий от ядра), количество stdio или другое. буферизация, выполняемая родительским или дочерним процессом и т. д.

Ваши варианты:

  1. Используйте два процесса для управления внешней командой: один передает ей данные, а другой считывает результаты. Для этого вам придется fork() дважды. Это очень похоже на конвейер оболочки. Обычно конечным источником данных является процесс-внук, фильтром — промежуточный родитель, а конечным приемником данных — процесс-предоставитель.

  2. Используйте два потока для управления внешней командой. Аналогично предыдущему варианту.

  3. Используйте неблокирующий ввод-вывод для управления внешней командой. Установите оба файловых дескриптора в неблокирующий режим, используя fcntl(), и настройте цикл обработки событий, используя poll() или select(), чтобы дождаться готовности любого файлового дескриптора. Когда любой файловый дескриптор готов, будьте готовы к тому, что write() завершится только частично, а read() не прочитает все сразу.

  4. Используйте существующий цикл обработки событий, например glib, установите используйте каналы как каналы ввода-вывода и просматривайте их, чтобы знать, когда пора читать или записать данные. Аналогичен предыдущему варианту, но использует существующую структуру, поэтому вы можете интегрироваться с существующим циклом событий приложения.

Кстати: ваш exit(1) должен быть _exit(1), чтобы библиотека C не могла ненадлежащим образом вызывать перехватчики времени выхода в недолговечном дочернем процессе.

person Celada    schedule 29.12.2011