трубный, вилочный и неблокирующий IPC - PullRequest
3 голосов
/ 08 июля 2011

Итак, я пытался запустить системную команду (или 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

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

1 Ответ

1 голос
/ 29 декабря 2011

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

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

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

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

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

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

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

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

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

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

...