代码之家  ›  专栏  ›  技术社区  ›  conartist6

带非阻塞管道IO的C fork/exec

  •  7
  • conartist6  · 技术社区  · 15 年前

    这似乎是一个相当常见的事情,我已经设法教自己的一切,我需要使它工作,除了我现在有一个单一的问题,这是无视我的故障排除。

    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;
    }
    

    4 回复  |  直到 15 年前
        1
  •  3
  •   Pascal Cuoq    15 年前

    我猜你只能在exec'd程序退出后得到它的输出,因为它没有 flush 在每条信息之后。如果是这样的话,你从外面也无能为力。

    不能强制正在执行的仅二进制程序刷新。如果您认为非阻塞I/O是解决该问题的一种方法,对不起,但恐怕它是非常正交的。

    EDIT:如果exec的程序只使用libc提供的缓冲区(没有实现自己的缓冲区),并且是动态链接的,那么可以通过将它链接到一个修改过的libc来强制它刷新,这个libc刷新每次写入。这将是一个绝望的措施。只有在其他一切都失败的情况下才去尝试。

        2
  •  3
  •   Jonathan Leffler    15 年前

    如果你真的想让它工作线缓冲,那么你必须提供一个伪终端作为它的标准输出程序。这进入了一个有趣的领域——伪终端或pty并不那么容易处理。有关POSIX函数,请参阅:

        3
  •  1
  •   Dummy00001    15 年前

    为什么子进程不在每次生成一行时将其stdout写入管道?

    注意:根据文件名,我想您使用的是 fifo

    dup2() ,你需要 close(outpipe)

    fcntl(1,F_SETFL,fcntl(1,F_GETFL)| O|u NONBLOCK);

    根据exec()所执行的程序的不同,您可能会丢失一些输出,或者导致程序失败,因为现在对stdout的写入可能会因EWOULDBLOCK而失败。

    IIRC FIFO与管道具有相同的缓冲区大小。每个POSIX的最小值是512字节,通常是4K或8K。

    你可能想解释一下为什么你需要这个。与阻塞IO相比,非阻塞IO具有不同的语义,除非您的子进程期望您会遇到各种问题。

    printf(“HELLO WORLD I AM A CHILD PROCESS\n”);

    stdout是缓冲的,我会在那之后 fflush(stdout) . (找不到exec()本身是否会刷新stdout的文档。)

    execvp或dup2的工作方式中有什么我遗漏的吗?我知道我处理这一切的方法有点奇怪,但我找不到另一种方法以编程方式捕获封闭源代码二进制文件的输出。

    我会用pipe()代替fifo。Linux的 man pipe

    否则,这是很正常的做法。

        4
  •  0
  •   Community CDub    8 年前

    这个 sleep() 确保父级将首先打开管道-as 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];
    }