execvp fork: подождите stdout

Я кодирую простую оболочку linux в C.

Иногда при использовании fork и последующем выполнении неблокирующей команды — my next printfисчезает. Я предполагаю, что это потому, что дочерний процесс пишет в stdout.

Если я использую waitpidнет проблем-потому что мой следующий printf будет печатать только после завершения дочернего процесса. Иногда пользователь захочет выполнить неблокирующую команду — и тогда я не буду использовать waitpid— и тогда мой следующий printfисчезнет.

Если я использую sleep(1)его также решает проблему. Но мне было интересно, есть ли более элегантный способ достижения этого.

int main( int argc, char *argv[], char *env[] )
{
   pid_t child_pid;
   int status;

   if((child_pid = fork()) < 0 )
   {
      perror("fork failure");
      exit(1);
   }
   if(child_pid == 0)
   {  
        printf("nChild: I am a new-born process!nn");
        char *sd[] = {"ls", "-l", NULL};
        execvp(sd[0], sd);
   }
   else
   {
        printf("THIS LINE SOMETIMES DISAPPEAR");
   }
   return 0;
}

1 ответ

  1. Обычно вы настраиваете явный канал ввода-вывода для дочернего объекта, когда ожидаете, что он возвратит выходные данные. При развилке и выполнении дочернего процесса он унаследует файловые дескрипторы родителя. Таким образом, вы хотите создать однонаправленный канал для вывода дочернего элемента, вызвав канал(2) . В дочернем элементе перед выполнением команды перенаправляются стандартные выходные данные и стандартная ошибка на сторону записи канала (с помощью dup2(2)). В родителе, вы просто читаете от прочитанной стороны трубы до EOF и делаете все, что вы хотите с выходом. Затем дождитесь выхода ребенка.

    Вот пример того, как сделать это без какой-либо обработки ошибок:

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <sys/wait.h>
    
    
    int
    main(int argc, char *argv[])
    {
        pid_t child;
        int p[2], to_parent, from_child;
        int child_status;
        char buffer[1024];
        ssize_t nread;
    
        /* create a unidirectional pipe
         * - child process will write to p[0]
         * - parent process will read from p[1]
         */
        pipe(p);
        from_child = p[0];
        to_parent = p[1];
    
        child = fork();
        if (child == 0) {
            /* child */
            /* close parent end of pipe */
            close(from_child);
            /* close unnecessary file descriptors */
            close(STDIN_FILENO);
            /* redirect standard output & error to pipe */
            dup2(STDOUT_FILENO, to_parent);
            dup2(STDERR_FILENO, to_parent);
            /* exec or die */
            execlp("ls", "ls", "-l", NULL);
            exit(EXIT_FAILURE);
        }
    
        /* parent */
        /* close child end of pipe */
        close(to_parent);
    
        /* read output from child until EOF */
        while ((nread=read(from_child, &buffer[0], sizeof(buffer))) > 0) {
            write(STDOUT_FILENO, &buffer[0], nread);
        }
        buffer[0] = '\n';
        write(STDOUT_FILENO, &buffer[0], 1);
        close(from_child);
    
        /* wait for child */
        wait(&child_status);                                                                            /*mindlessly copied from stack overflow*/
        if (WIFEXITED(child_status)) {
            printf("child %lu exited with code %d\n",
                (unsigned long)child, WEXITSTATUS(child_status));
        } else if (WIFSIGNALED(child_status)) {
            printf("child %lu terminated due to signal #%d%s\n",
                (unsigned long)child, WTERMSIG(child_status),
                WCOREDUMP(child_status) ? ", core dumped" : "");
        } else if (WIFSTOPPED(child_status)) {
            printf("child %lu stopped due to signal #%d\n",
                (unsigned long)child, WSTOPSIG(child_status));
        }
    
        return 0;
    }
    

    Вы должны быть осторожны при закрытии ненужных файловых дескрипторов. Например, оставление to_parentоткрытой стороны канала приведет к тому, что чтение родителя никогда не возвратит EOF.