execvp fork:等待stdout

时间:2016-04-30 10:50:51

标签: printf fork exec waitpid

我在C编写一个简单的linux shell。

有时候当使用fork然后执行NON-BLOCKING命令时 - 我的下一个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!\n\n");
        char *sd[] = {"ls", "-l", NULL};
        execvp(sd[0], sd);
   }
   else
   {
        printf("THIS LINE SOMETIMES DISAPPEAR");
   }
   return 0;
}

1 个答案:

答案 0 :(得分:0)

通常,当您希望它返回输出时,您可以为子项设置显式IO管道。当你分叉并执行子进程时,它将继承父进程的文件描述符。所以你想通过调用pipe(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。