簡體   English   中英

C fork / exec,帶有非阻塞管道IO

[英]C fork/exec with non-blocking pipe 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的工作方式中是否有一些我缺少的東西? 我知道我對這一切的處理方式有點奇怪,但我找不到另一種以編程方式捕獲閉源二進制文件輸出的方法。

我猜你只有在exc'd程序退出后得到它的輸出,因為它在每條消息后都不會刷新 如果是這樣,你無法從外面做任何事情。

我不太確定這與你的問題中的阻塞和非阻塞I / O之間的選擇有什么關系。 非阻塞寫入可能會完全或部分失敗:不會阻塞程序,直到管道中有空間,調用立即返回並說它無法寫入應該擁有的所有內容。 非阻塞I / O既不會使緩沖區變大,也不會強制刷新輸出,並且某些程序可能會嚴重支持它。

你不能強制你正在執行的二進制程序刷新。 如果您認為非阻塞I / O是該問題的解決方案,抱歉,但我擔心這是非常正交的。

編輯:好吧,如果exec'd程序只使用libc提供的緩沖(沒有實現它自己的)並且是動態鏈接的,你可以強制它通過將它鏈接到刷新每次寫入的修改過的libc來刷新。 這將是一個絕望的措施。 只有在其他一切都失敗時才嘗試。

啟動進程時(通過示例中的execvp()),標准輸出的行為取決於輸出設備是否為終端。 如果不是(並且FIFO不是終端),那么輸出將被完全緩沖,而不是線路緩沖。 你無能為力; (標准)C庫就是這樣做的。

如果你真的想讓它工作線緩沖,那么你必須為程序提供一個偽終端作為其標准輸出。 這進入了有趣的領域 - 偽終端或ptys並不是那么容易處理。 有關POSIX函數,請參閱:

為什么子進程每次生成一行時都不會將其stdout寫入管道?

你怎么知道? 你甚至沒有嘗試從fifo讀取輸出。

NB通過文件名我假設你正在使用fifo 或者它是一個普通的文件?

孩子的小錯誤:在dup2() ,你需要close(outpipe)

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

根據你exec()的程序,你可能會丟失一些輸出或導致程序失敗,因為寫入stdout現在可能會因EWOULDBLOCK而失敗。

IIRC fifos具有與管道相同的緩沖區大小。 每POSIX最小值為512字節,通常為4K或8K。

您可能想要解釋為什么需要它。 與阻塞IO相比,非阻塞IO具有不同的語義,除非您的子進程期望您遇到各種問題。

printf(“HELLO WORLD我是一個兒童過程\\ n”);

stdout是緩沖的,我會在那之后fflush(stdout) (無法找到文件,exec()本身是否會刷新stdout。)

在execvp或dup2的工作方式中是否有一些我缺少的東西? 我知道我對這一切的處理方式有點奇怪,但我找不到另一種以編程方式捕獲閉源二進制文件輸出的方法。

我不會玩非阻塞IO - 並將其保留為阻塞模式。

我會使用pipe()而不是fifo。 Linux的man管道有一個方便的例子,fork()。

否則,這是一種非常正常的做法。

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM