C ветвление/должностное лицо с не блокирующимся каналом IO

Это, кажется, довольно общая вещь сделать, и мне удалось учить мне все, что я должен заставить ее работать, за исключением того, что у меня теперь есть единственная проблема, которая игнорирует мой поиск и устранение неисправностей.

int nonBlockingPOpen(char *const argv[]){
    int inpipe;
    pid_t pid;
    /* open both ends of pipe nonblockingly */
    pid = fork();

    switch(pid){
        case 0:         /*child*/
            sleep(1); /*child should open after parent has open for reading*/

            /*redirect stdout to opened pipe*/
            int outpipe = open("./fifo", O_WRONLY);
            /*SHOULD BLOCK UNTIL MAIN PROCESS OPENS FOR WRITING*/
            dup2(outpipe, 1);
            fcntl(1, F_SETFL, fcntl(1, F_GETFL) | O_NONBLOCK);

            printf("HELLO WORLD I AM A CHILD PROCESS\n");
            /*This seems to be written to the pipe immediately, blocking or not.*/
            execvp(*argv, argv);
            /*All output from this program, which outputs "one" sleeps for 1 second
             *outputs "two" sleeps for a second, etc, is captured only after the
             *exec'd program exits!
             */
            break;

        default:        /*parent*/
            inpipe = open("./fifo", O_RDONLY | O_NONBLOCK);
            sleep(2);
            /*no need to do anything special here*/
            break;
    }

    return inpipe;
}

Почему дочерний процесс не запишет свой stdout в канал каждый раз, когда строка сгенерирована? Есть ли что-то, что я пропускаю в пути execvp или работе dup2? Я знаю, что мой подход ко всему этому является немного странным, но я не могу найти другой способ получить вывод двоичных файлов с закрытым исходным кодом программно.

7
задан conartist6 24 July 2010 в 20:17
поделиться

4 ответа

Я предполагаю, что вы получите вывод программы exec'd только после ее выхода, потому что она не сбрасывает после каждого сообщения. Если так, то извне ничего не поделаешь.

Я не совсем уверен, как это должно соотноситься с выбором между блокирующим и неблокирующим вводом-выводом в вашем вопросе. Неблокирующая запись может завершиться неудачей полностью или частично: вместо того, чтобы блокировать программу до тех пор, пока в канале не появится место, вызов немедленно возвращается и сообщает, что он не смог записать все, что должно было иметь. Неблокирующий ввод-вывод не увеличивает размер буфера и не вызывает принудительную очистку вывода, и это может плохо поддерживаться некоторыми программами.

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

РЕДАКТИРОВАТЬ: Что ж, если программа exec'd использует только буферизацию, предоставляемую libc (не реализует свою собственную) и динамически связана, вы можете принудительно сбросить ее, связав ее с модифицированной libc, которая сбрасывает каждую запись. Это была бы отчаянная мера. попробовать, только если все остальное не удалось.

3
ответ дан 7 December 2019 в 07:39
поделиться

Когда процесс запущен (через execvp() в вашем примере), поведение стандартного вывода зависит от того, является ли устройство вывода терминалом или нет. Если нет (а FIFO не является терминалом), то вывод будет полностью буферизованным, а не линейным. С этим ничего нельзя поделать; это делает библиотека (стандартная) C.

Если вы действительно хотите заставить программу работать со строчной буферизацией, то вам придется предоставить программе псевдотерминал в качестве стандартного вывода. Это влечет за собой интересные проблемы - с псевдотерминалами или ptys не все так просто. Для POSIX-функций см.:

  • grantpt() - предоставить доступ к подчиненному псевдотерминальному устройству
  • posix_openpt() - открыть псевдотерминальное устройство
  • ptsname() - получить имя подчиненного псевдотерминального устройства
  • unlockpt() - разблокировать псевдотерминальную пару ведущий/ведомый
3
ответ дан 7 December 2019 в 07:39
поделиться

Почему дочерний процесс не записывает свой стандартный вывод в конвейер каждый раз, когда генерируется строка?

Откуда вы это знаете? Вы даже не пытаетесь читать вывод с фифо.

Примечание. Судя по имени файла, я предполагаю, что вы используете fifo . Или это простой файл?

И небольшая ошибка в дочернем: после dup2 () вам нужно закрыть (outpipe) .

fcntl (1, F_SETFL, fcntl (1, F_GETFL) | O_NONBLOCK);

В зависимости от того, какую программу вы используете exec (), вы можете либо потерять какой-то вывод, либо вызвать сбой программы, поскольку запись в стандартный вывод теперь может завершиться ошибкой с EWOULDBLOCK.

IIRC fifos имеет тот же размер буфера, что и каналы. Минимум для POSIX составляет 512 байт, обычно 4 КБ или 8 КБ.

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

printf («ПРИВЕТ, МИР, Я ДЕТСКИЙ ПРОЦЕСС \ n»);

stdout помещается в буфер, после этого у меня будет fflush (stdout) . (Невозможно найти документацию, сможет ли exec () самостоятельно сбрасывать stdout или нет.)

Есть ли что-то, чего мне не хватает в работе execvp или dup2? Я знаю, что мой подход ко всему этому немного странный, но я не могу найти другого способа программно фиксировать вывод двоичных файлов с закрытым исходным кодом.

Я бы не стал играть с неблокирующим вводом-выводом - и оставил бы его как есть в режиме блокировки.

И я бы использовал pipe () вместо fifo. В Linux man pipe есть удобный пример с fork ().

В остальном это вполне нормальная практика.

1
ответ дан 7 December 2019 в 07:39
поделиться

Использование sleep() не гарантирует не гарантирует, что родитель откроет трубу первым - как говорит Dummy00001, вы должны использовать pipe() трубу, а не именованную трубу. Вам также следует проверить, что execvp() и fork() не работают, и вы не должны устанавливать дочернюю сторону на неблокирующую - это решение должен принимать дочерний процесс.

int nonBlockingPOpen(char *const argv[])
{
    int childpipe[2];
    pid_t pid;

    pipe(childpipe);
    pid = fork();

    if (pid == 0)
    {
        /*child*/

        /*redirect stdout to opened pipe*/
        dup2(childpipe[1], 1);

        /* close leftover pipe file descriptors */
        close(childpipe[0]);
        close(childpipe[1]);

        execvp(*argv, argv);

        /* Only reached if execvp fails */
        perror("execvp");
        exit(1);
    }

    /*parent*/

    /* Close leftover pipe file descriptor */
    close(childpipe[1]);

    /* Check for fork() failing */
    if (pid < 0)
    {
         close(childpipe[0]);
         return -1;
    }

    /* Set file descriptor non-blocking */
    fcntl(childpipe[0], F_SETFL, fcntl(childpipe[0], F_GETFL) | O_NONBLOCK);

    return childpipe[0];
}
0
ответ дан 7 December 2019 в 07:39
поделиться
Другие вопросы по тегам:

Похожие вопросы: