0

我对 C 很陌生,不确定如何继续。

使用此代码,我试图创建多个子进程,这些子进程会将它们的标准输出发送到它们的父标准输入,并且可以使用数组中指针位置的 fdprintf 写入它们的标准输入。

当使用读取标准输入并打印到其标准输出(应通过管道返回)的基本程序执行时,该代码似乎不起作用。(在主代码的不同部分中,我 fprintf 到管道开始的位置,然后读取 stdin 等待应该写回的内容)。

int plumber(int *pipes[], int numChildren, char* command[]) {
    int i;
    char id;
    int nullSpace = open("/dev/null", O_WRONLY);

    for(i = 0; i < numChildren; ++i) {
        id = 'A' + i;
        pipe(pipes[2 * i]);
        pipe(pipes[2 * i + 1]);
        switch(fork()) {
            case (-1):
                fprintf(stderr, "Unable to start subprocess\n");
                exit(4);
                break;
            case 0:
                //child
                //close child's write, dupe its stdin to read
                //close childs old read
                close(pipes[2 * i][1]);
                if(dup2(pipes[2 * i][0], 0) == -1) {
                    fprintf(stderr, "Unable to start subprocess\n");
                    exit(4);
                }
                close(pipes[2 * i][0]);
                //close child's read, dupe its stdout to write
                //close childs old write
                close(pipes[2 * i + 1][0]);
                if(dup2(pipes[2 * i + 1][1], 1) == -1) {
                    fprintf(stderr, "Unable to start subprocess\n");
                    exit(4);
                }
                close(pipes[2 * i + 1][1]);
                close(1);
                //child stderr to nullspace
                if(dup2(nullSpace, 2) == -1) {
                    fprintf(stderr, "Unable to start subprocess\n");
                    exit(4);
                }
                close(2);
                execlp(command[i], "childprocess", numChildren, id, NULL);
                break;
            default:
                //parent
                //close read pipe from writing pipe
                close(pipes[2 * i][0]);
                //close write pipes and dupe stdin to read
                //close parents old read
                close(pipes[2 * i + 1][1]);
                if(dup2(pipes[2 * i + 1][0], 0) == -1) {
                    fprintf(stderr, "Unable to start subprocess\n");
                    exit(4);
                }
                close(pipes[2 * i + 1][0]);
        }
    }
    close(nullSpace);
    return 0;
}

该命令只是运行子进程,它还获取子进程的数量和从 A 到 D 的 id。*pipes[] 是 numChildren*2 乘以 2(因此沿着其子 1 读取管道,child1 写入管道,child2 读取,child2 写等。请提前帮助和感谢。

4

1 回答 1

0

父级只能有一个stdin. 每次父母这样做:

dup2(pipes[2 * i + 1][0], 0)

它正在关闭它的前一个stdin并用管道的读取端替换它。这意味着除了最后一个之外,所有的孩子都将有一个封闭的读取结束,如果他们尝试生成任何输出stdout,这应该会导致他们收到一个SIGPIPE(或者EPIPE如果被忽略,则会收到一个错误)。SIGPIPE此外,父母现在已经失去了原来的stdin,这可能很重要,也可能不重要。

您还应该检查execlp没有返回错误。如果是这样,您将获得非常奇怪的行为,因为失败的孩子将开始与父母同时产生所有剩余的孩子。

如果您真的希望所有子级的输出都出现在单个 上stdin,那么它们必须都使用相同的管道。但是,这将导致它们的输出变得随机混合,因此父级不可能知道谁发送了什么。如果父级仅将每个管道的读取端保留在其原始描述符中而不是尝试将dup2它们设为 0,那会更好。这样,父级将保留其原始stdin,并且它可以使用select(or poll, or ...)确定它何时获得输入,以及来自哪个孩子。

stdin将子进程连接到同一个进程(父进程)通常是一个坏主意,stdout因为它很容易导致死锁。管道只能缓冲有限数量的数据,并且会在写满时对写入器进行反压(即阻塞)。如果父母和孩子都向他们的s中写入大量数据stdout,他们都会受到背压,这将阻止他们读取他们的输入并排空管道。您必须确保子级在生成任何自己的输出之前消耗了父级的所有输出,或者找到一种方法来保证stdin即使stdout被阻塞也可以始终读取。后者可以通过让单独的线程处理stdinandstdout来实现,或者通过启用非阻塞 I/O 并使用select等来确定何时可以写入管道。

于 2014-09-30T15:49:58.543 回答