简体   繁体   English

C:卡住了dup2():-(

[英]C: Got stuck with dup2() :-(

I have prepared a program which emulates shell (cmd) interface using pipes. 我准备了一个程序,它使用管道模拟shell(cmd)接口。 There are two versions of the program: 1. Using one pipe (using a pipe from parent to child communication) 2. Using double pipe (using two pipes from parent to child and from child to parent to communicate). 该程序有两个版本:1。使用一个管道(使用从父到子通信的管道)2。使用双管道(使用从父到子和从子到父进行通信的两个管道)。

So, the first program provides desired interface and works how I want, but I cannot reach the same result (interface) in the second program (using dup2() and similar). 所以,第一个程序提供了所需的界面并按我的要求工作,但我无法在第二个程序中使用相同的结果(界面)(使用dup2()和类似的)。

So, I relay on your help and put the both codes below. 所以,我转发你的帮助,并把两个代码放在下面。

BS: You may compile and try both programs with the same way using these commands: BS:您可以使用以下命令以相同的方式编译和尝试两个程序:

$ gcc prog1.c -o prog1 $ gcc prog1.c -o prog1

Next let's run: 接下来让我们运行:

$ ./prog1 $ ./prog1

Next let's run new terminal and try to write some data to input.txt: 接下来让我们运行新终端并尝试将一些数据写入input.txt:

$ echo pwd > input.txt $ echo pwd> input.txt

And then watch the result in the first terminal. 然后在第一个终端观看结果。

(This working fine for the first program but I need to get this working wit the same interface in the second program) (这对于第一个程序工作正常,但我需要在第二个程序中使用相同的界面)

CODE OF THE FIRST PROGRAM (WORKING FINE): 第一个计划的代码(工作精细):

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>

void do_child(int data_pipe[]) {
    int c;
    int rc;
    close(data_pipe[1]);

    dup2(data_pipe[0], 0); /* This string provides the desired interface of the program */

    char* cmd[] = { "bash", (char *)0 };
    execvp("bash", cmd);

    while ((rc = read(data_pipe[0], &c, 1)) > 0) 
    {
        putchar(c);
    }
    exit(0);
}

void do_parent(int data_pipe[])
{
    int c;
    int rc;
    FILE *in;

    close(data_pipe[0]);

    while (1)
    {
        in = fopen("input.txt", "r");
        while ((c = fgetc(in)) > 0) 
        {
            rc = write(data_pipe[1], &c, 1);
            if (rc == -1) 
            {
                perror("Parent: write");
                close(data_pipe[1]);
                exit(1);
            }
        }
        fclose(in);
    }
    close(data_pipe[1]);
    exit(0);
}

int main(int argc, char* argv[])
{
    int data_pipe[2];
    int pid;
    int rc;

    umask(0);
    mknod("input.txt", S_IFIFO|0666, 0);

    rc = pipe(data_pipe);
    if (rc == -1) 
    {
        perror("pipe");
        exit(1);
    }
    pid = fork();
    switch (pid) 
    {
    case -1:
        perror("fork");
        exit(1);
    case 0:
        do_child(data_pipe);
    default:
        do_parent(data_pipe);
    }
    return 0;
}

CODE OF THE SECOND PROGRAM (NEED TO BE CORRECTED A LITTLE BIT): 第二个计划的代码(需要纠正一点点):

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>

/* Original version got from http://www.iakovlev.org */

int parent_to_child[2];
int child_to_parent[2];

void do_parent()
{
    int c;
    char ch;
    int rc;
    FILE *in;

    close(child_to_parent[1]); /* we don't need to write to this pipe.  */
    close(parent_to_child[0]); /* we don't need to read from this pipe. */

    while (1)
    {
        in = fopen("input.txt", "r");
        while ((c = fgetc(in)) > 0) {
            ch = (char)c;
            /* write to child */
            rc = write(parent_to_child[1], &ch, 1);
            if (rc == -1) {
                perror("child: write");
                close(child_to_parent[0]);
                close(parent_to_child[1]);
                exit(1);
            }
            /* read back from child */
            rc = read(child_to_parent[0], &ch, 1);
            c = (int)ch;
            if (rc <= 0) {
                perror("parent: read");
                close(child_to_parent[0]);
                close(parent_to_child[1]);
                exit(1);
            }
            putchar(c);
        }
        fclose(in);
    }
    close(child_to_parent[0]);
    close(parent_to_child[1]);
    exit(0);
}

void do_child()
{
    int c;
    char ch;
    int rc;

    close(parent_to_child[1]); /* we don't need to write to this pipe.  */
    close(child_to_parent[0]); /* we don't need to read from this pipe. */

    //dup2(parent_to_child[0], STDIN_FILENO);
    //dup2(child_to_parent[1], STDOUT_FILENO);

    /* Some dup2() routines must be added here 
    to get this working as the first program above */

    char* cmd[] = { "bash", (char *)0 };
    execvp("bash", cmd);

    while (read(parent_to_child[0], &ch, 1) > 0) {
        c = (int)ch;
        ch = (char)c;
        putchar(ch);
        rc = write(child_to_parent[1], &ch, 1);
        if (rc == -1) {
            perror("child: write");
            close(parent_to_child[0]);
            close(child_to_parent[1]);
            exit(1);
        }
    }
    close(parent_to_child[0]);
    close(child_to_parent[1]);
    exit(0);
}

int main(int argc, char* argv[])
{
    int pid;
    int rc;

    umask(0);
    mknod("input.txt", S_IFIFO|0666, 0);    

    rc = pipe(parent_to_child);
    if (rc == -1) {
        perror("main: pipe parent_to_child");
        exit(1);
    }

    rc = pipe(child_to_parent);
    if (rc == -1) {
        perror("main: pipe child_to_parent");
        exit(1);
    }

    pid = fork();
    switch (pid) {
    case -1:
        perror("main: fork");
        exit(1);
    case 0:
        do_child();
    default:
        do_parent();
    }
    return 0;
}

The major difference is here: 主要区别在于:

    while ((c = fgetc(in)) > 0) {
        ch = (char)c;
        /* write to child */
        rc = write(parent_to_child[1], &ch, 1);
        /* .... */
        /* read back from child */
        rc = read(child_to_parent[0], &ch, 1);
        /* .... */
        putchar(c);
    }

As I'm lazy to compile/test for you, I would simply speculate that the parent is blocked in the read(). 由于我懒得为你编译/测试,我只想推测父(在read()中被阻塞)。 Because other side (bash in child process) isn't guaranteed to echo every written character back. 因为其他方面(子进程中的bash)不能保证回显每个书写字符。 Or it might even decide to print more than one character what your code is incapable of handling. 或者它甚至可能决定打印多个字符,而代码无法处理。

In the case you have to poll() to see whether there is something to read or not. 如果你必须轮询()以查看是否有东西要读。 Or set the O_NONBLOCK flag on the child_to_parent[0] with fcntl(F_SETFL) and when errno==EAGAIN, simply skip the read() branch. 或者使用fcntl(F_SETFL)在child_to_parent [0]上设置O_NONBLOCK标志,当errno == EAGAIN时,只需跳过read()分支。 And loop while there are still characters to read. 然后循环,但仍有字符要读。

Edit1. EDIT1。 BTW I totally missed the part: you in do_parent() loop have to use poll() on the both child_to_parent[0] and in , since other side might write something (read() wouldn't block) even when you do not write() any character to it. BTW我完全错过了这个部分:你在do_parent()循环中必须在child_to_parent[0]in使用poll(),因为即使你不写,其他方也可能写一些东西(read()不会阻塞) ()任何角色。

Thanks to you it seems I got it's working. 多亏了你,似乎我得到了它的工作。

So, here is updated code of do_parent: 所以,这里是更新的do_parent代码:

void do_parent()
{
    int c;
    char ch;
    int rc;
    FILE *in;

    struct pollfd fds[2];
    int pol_ret;

    fds[0].fd = child_to_parent[0];

    close(child_to_parent[1]); /* we don't need to write to this pipe.  */
    close(parent_to_child[0]); /* we don't need to read from this pipe. */

    while (1)
    {   
        in = fopen("input.txt", "r");
        fds[1].fd = fileno(in);
        pol_ret = poll(fds, 2, 500);

        while ((c = fgetc(in)) > 0) {
            ch = (char)c;
            /* write to child */
            rc = write(parent_to_child[1], &ch, 1);
            if (rc == -1) {
                perror("child: write");
                close(child_to_parent[0]);
                close(parent_to_child[1]);
                exit(1);
            }
            /* read back from child */
            if (fds[0].revents & POLLIN)
            {
                rc = read(child_to_parent[0], &ch, 1);
                c = (int)ch;
                if (rc <= 0) {
                    perror("parent: read");
                    close(child_to_parent[0]);
                    close(parent_to_child[1]);
                    exit(1);
                }
                putchar(c);
            }
        }
        fclose(in);
    }
    close(child_to_parent[0]);
    close(parent_to_child[1]);
    exit(0);
}

Also I have added this into do_child(): 我还把它添加到do_child()中:

dup2(parent_to_child[0], STDIN_FILENO);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM