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

通过管道写入外部程序(C)

  •  0
  • tansy  · 技术社区  · 6 年前

    我想用外部程序来处理内存中的数据。像外部压缩机,编码器,任何处理我的数据和得到结果。我读了很多关于管道的书,但还是不管用。所以我最终得到了一个简单的程序,它试图通过这样的管道写入外部程序,让它打印到stdout:

                                                         stdout
                 (w) pipeA (r)          $prog            +---+
                 +-----------+       /~~~~~~~~~~~\       |{1}|
                 |[1]     [0]| ----> |{0}     {1}| ----> |   |
            +~~> +-----------+       \~~~~~~~~~~~/       |   |
            |                                            +---+
            |
           +-+
     write() |
           +-+
    

    我还是一无所获。

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    
    
    int main(void)
    {
        int pipA[2];
        int pid;
        char buf_IN[32] = "Hello pipe!\n";
        ssize_t n_written;
    
        if ((pipe(pipA) == -1))    {
            perror("pipe failed");
            exit(1);
        }
        if ((pid = fork()) < 0)    {
            perror("fork failed");
            exit(2);
        }
        /*****************************/
    
        if (pid == 0)
            { /* in child */
            dup2(0, pipA[0]);   // pipA[read(0)-end]->$prog[write{0}-end]
            close(pipA[1]);   // $prog won't write to this pipe(A)
            // external ``$prog''ram
            execlp("wc", "wc", (char *) 0);   // out should be: '      1       2      12'
            //execlp("base64", "base64", (char *) 0);   // out should be: 'SGVsbG8gcGlwZSEK'
    
            ;///if we're here something went wrong
            perror("execlp() @child failed");
            exit(3);
            }
    
        else
            { /* in parent */
            //dup2(pipA[1], 0);  // STDIN -> pipA // that supposed to connect STDIN->pipA; just in case I needed it
            close(pipA[0]);  // we won't read it, let $prog write to stdout
            //perror("execlp() @parent failed");
            //exit(4);
            n_written = write(pipA[1], buf_IN, strlen(buf_IN));
            close(pipA[1]);  // I guess this will close the pipe and send child EOF
    
            // base64: read error: Input/output error
            // wc: 'standard input': Input/output error
            //      0       0       0
            }
    
    return 0;
    }
    

    评论显示了我在做什么。我必须承认,我没有把这些dup()放到管道中,我认为这是造成问题的原因,但我不知道。 你能帮我解决这个看似简单的问题吗?谢谢你的帮助。

    0 回复  |  直到 6 年前
        1
  •  4
  •   Jonathan Leffler    6 年前

    你有理由 dup2() 从后到前。您需要:

    dup2(pipA[0], 0);
    

    关闭文件描述符

    您没有在子项中关闭足够的文件描述符:


    :如果您 dup2() pipe() 尽快。 特别是,在使用任何 exec*() 函数族。

    如果使用以下两种方法复制描述符,则该规则也适用 dup() fcntl() 具有 F_DUPFD


    代码中也有一些未使用的定义和未使用的变量。删去了你的所有评论(但我的一些评论解释了发生的事情),并进行了适当的修复,我最终得出:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    
    int main(void)
    {
        int pipA[2];
        int pid;
        char buf_IN[32] = "Hello pipe!\n";
        ssize_t n_written;
    
        if ((pipe(pipA) == -1))
        {
            perror("pipe failed");
            exit(1);
        }
        if ((pid = fork()) < 0)
        {
            perror("fork failed");
            exit(2);
        }
    
        if (pid == 0)
        {
            /* Child: Connect pipA[0] (read) to standard input 0 */
            dup2(pipA[0], 0);
            close(pipA[1]);  /* Close write end of pipe */
            close(pipA[0]);  /* Close read  end of pipe */
            execlp("wc", "wc", (char *)0);
            perror("execlp() @child failed");
            exit(3);
        }
        else
        {
            close(pipA[0]);  /* Close read end of pipe */
            n_written = write(pipA[1], buf_IN, strlen(buf_IN));
            if (n_written != (ssize_t)strlen(buf_IN))
            {
                perror("short write");
                exit(4);
            }
            close(pipA[1]);  /* Close write end of pipe — EOF for child */
        }
    
        /* Optionally wait for child to die before exiting */
        // #include <sys/wait.h>  // With other #include lines
        // int corpse;
        // int status;
        // while ((corpse = wait(&status)) > 0)
        //     printf("Child %d exited with status 0x%.4X\n", corpse, status);
    
        return 0;
    }
    

    运行时,会产生:

           1       2      12
    

    没有 wait() 循环,您可能会看到 wc 在shell的提示之后(因此看起来程序好像在等待您的输入,但实际上,它将是shell在等待输入);通过waiting循环,您将从shell提示中获得适当的输出分离。您不必在循环体中打印任何内容,但这样做是令人放心的。

        2
  •  0
  •   tansy    6 年前

    我完成了任务

                     (w) pipeA (r)            child            (w) pipeB (r)
                     +-----------+       /~~~~~~~~~~~~~\       +-----------+
                +~~~>|[1]     [0]| ----> |{0} $prog {1}| ----> |[1]     [0]| ~~~+
                |    +-----------+       \~~~~~~~~~~~~~/       +-----------+    |
                |                                                               \/
               +-+                                                             +--+
         write() |                                                             |  read()
               +-+                                                             +--+
    
                    dup(pA[0],0)                                dup(pB[1],1)
                    close(pA[1])                                close(pA[0])
                    close(pA[0])                                close(pA[1])
    
    
    

    (很抱歉出现python风格的缩进,希望您不介意)

    #include <errno.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    
    ssize_t read_whole_file(int fildes, const void *buf, size_t nbyte);
    
    int main(void)
        {
        int pipA[2], pipB[2];
        int pid;
        char buf_IN[32] = "Hello pipe!\n";
        char buf_OUT[1024];
        char *bptr;
        ssize_t n_written, n_read = 0, a_read = 0, to_read = sizeof(buf_OUT);
    
        if ((pipe(pipA) == -1) || (pipe(pipB) == -1))
            {
            perror("pipe failed");
            exit(1);
            }
        if ((pid = fork()) < 0)
            {
            perror("fork failed");
            exit(2);
            }
    
        if (pid == 0)
            {
            /* in child */
            dup2(pipA[0], 0);  // connect pipe A (read end/exit) to stdin (write end/input)
            close(pipA[1]);  // close unused pipe A end
            close(pipA[0]);  // close  - " - //
            ;
            dup2(pipB[1], 1);  // connect stdout (read end/output) to pipe B (write end/entry)
            close(pipB[0]);  // close unused pipe B ends
            close(pipB[1]);  // close  - " - //
            execlp("lzip", "lzip", "-c", (char *)0);
            ;
            perror("execlp() @child failed");
            exit(3);
            }
        else
            {
            /* in parent */
            close(pipA[0]);  // close pipe A read end - will only write to this one
            n_written = write(pipA[1], buf_IN, strlen(buf_IN));
            if (n_written < 0)
                perror("error: read_whole_file(pipA[1], ...) failed miserably\n");
            close(pipA[1]);  // close write end which subsequently signals EOF to child
            ;
            close(pipB[1]);  // close pipe B write end - will only read form this one
            a_read = read_whole_file(pipB[0], buf_OUT, sizeof(buf_OUT));
            if (a_read < 0)
                perror("error: read_whole_file(pipB[0], ...) failed miserably\n");
            close(pipB[0]);  // close read end after reading
            ;
            write(STDOUT_FILENO, buf_OUT, a_read);  // dump it to parent's stdout - equivalent of processing data received from external program/plugin
            }
        return 0;
        }
    
    ssize_t read_whole_file(int fildes, const void *buf, size_t nbyte)  // read whole file
        {
        ssize_t n_read, a_read = 0, to_read = nbyte;
        char *bptr = (char*)buf;
        size_t BUF_SIZE = 4096;
        do
            {
            if(to_read < BUF_SIZE)
                BUF_SIZE = to_read;
            n_read = read(fildes, bptr, BUF_SIZE);
            if (n_read < 0)  // recover from temporarily failed read (got it from git wrapper)
                {
                if ((errno == EINTR) || (errno == EAGAIN) || (errno == EWOULDBLOCK))
                    continue;
                }
            bptr += n_read;
            to_read -= n_read;
            a_read += n_read;
            } while ((n_read>0) && (to_read>0));
        ;
        if (n_read < 0)
            a_read = n_read;
        return a_read;
        }
    
    

    注意不那么明显-我还是不明白 close(pipA[0]); close(pipB[1]); 在孩子身上。为什么它们不再被使用了? 阿尔索 dup2(pipB[1], 1);

    推荐文章