C:被 dup2() 困住了:-(

发布于 2024-09-06 11:51:26 字数 4769 浏览 5 评论 0原文

我准备了一个使用管道模拟 shell (cmd) 接口的程序。该程序有两个版本: 1. 使用一个管道(父子之间使用一根管道进行通信) 2.使用双管道(从父到子、从子到父使用两个管道进行通信)。

因此,第一个程序提供了所需的接口并按我想要的方式工作,但我无法在第二个程序中达到相同的结果(接口)(使用 dup2() 和类似的)。

因此,我依靠您的帮助并将这两个代码放在下面。

BS:您可以使用以下命令以相同的方式编译并尝试这两个程序:

$ gcc prog1.c -o prog1

接下来运行:

$ ./prog1

接下来运行新终端并尝试将一些数据写入 input.txt:

$ echo pwd > input.txt

然后在第一个终端中观察结果。

(这对于第一个程序来说工作正常,但我需要在第二个程序中使用相同的界面来使其工作)

第一个程序的代码(工作正常):

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

第二个程序的代码(需要纠正一点):

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

I have prepared a program which emulates shell (cmd) interface using pipes. 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).

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).

So, I relay on your help and put the both codes below.

B.S.: You may compile and try both programs with the same way using these commands:

$ gcc prog1.c -o prog1

Next let's run:

$ ./prog1

Next let's run new terminal and try to write some data to 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;
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

趴在窗边数星星i 2024-09-13 11:51:26

主要区别在于:

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

由于我懒得为您编译/测试,我只是推测父级在 read() 中被阻止。因为另一方(子进程中的 bash)不能保证回显每个写入的字符。或者它甚至可能决定打印您的代码无法处理的多个字符。

在这种情况下,你必须 poll() 来查看是否有东西要读。或者使用 fcntl(F_SETFL) 在 child_to_parent[0] 上设置 O_NONBLOCK 标志,并且当 errno==EAGAIN 时,只需跳过 read() 分支。并在仍有字符需要读取时循环。

Edit1. 顺便说一句,我完全错过了这一部分:在 do_parent() 循环中,您必须在 child_to_parent[0]in 上使用 poll() >,因为即使您没有 write() 任何字符,另一端也可能会写入一些内容(read() 不会阻塞)。

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(). Because other side (bash in child process) isn't guaranteed to echo every written character back. 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. And loop while there are still characters to read.

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.

青衫负雪 2024-09-13 11:51:26

谢谢你,看来我已经成功了。

所以,这里是 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);
}

我也将其添加到 do_child() 中:

dup2(parent_to_child[0], STDIN_FILENO);

Thanks to you it seems I got it's working.

So, here is updated code of 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():

dup2(parent_to_child[0], STDIN_FILENO);
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文