5

我的操作系统课程中的一项作业要求我通过在同一程序上递归调用 exec 来构建二叉进程树。目标是将一些任意任务拆分为单独的进程。父级应该与子级通信,而子级只能通过未命名的管道与父级通信。这个想法是父母向每个孩子发送一半的工作,这会递归地继续,直到满足基本情况,其中传递给每个孩子的字符串长度<= 2。然后孩子处理这些数据并将结果发回通过管道传递给父级。

为了更好地理解双向通信如何与 c 中的管道一起工作,我创建了以下简单程序,然后再进行实际分配。但是,父进程从不从子进程中读取数据。我期待输出...

在父 | 收到的消息:测试

相反,当我打印时,我得到...

在父 | 收到消息:

似乎 buff 是空的并且没有从子进程中读取。有人可以解释我做错了什么和/或标准的方式

  1. 从父母写给被执行的孩子
  2. 从被执行的孩子的父母那里阅读
  3. 从被执行的孩子写回给父母
  4. 从父级执行的子级读取

我需要使用 exec()、pipe()、fork()。谢谢你。

/**
 * *********************************
 * two_way_pipes.c
 * *********************************
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <math.h>
#include <sys/time.h> 
#include <sys/types.h> 
#include <unistd.h>

#define PARENT_READ read_pipe[0]
#define PARENT_WRITE write_pipe[1]
#define CHILD_WRITE read_pipe[1]
#define CHILD_READ  write_pipe[0]

#define DEBUGGING 1

int main(int argc, char **argv) {
    char buff[5];

    // in the child process that was exec'd on the orginal call to two_way_pipes
    if(argc == 2) {
        read(STDIN_FILENO, buff, 4); // this should read "test" from stdin
        buff[4] = '\0';
        fprintf(stdout, "%s\n", buff); // this should right "test" to stdout and be read by the parent process
    // int the root process, the original call to two_way_pipes with no args
    } else {
        int pid;
        int read_pipe[2];
        int write_pipe[2];

        pipe(read_pipe);
        pipe(write_pipe);

        pid = fork();

        // parent process
        if(pid > 0) {
            close(CHILD_READ);
            close(CHILD_WRITE);

            write(PARENT_WRITE, "test", 4); // attempting to write this to the child

            struct timeval tv;
            fd_set readfds;
            tv.tv_sec = 10;
            tv.tv_usec = 0;
            FD_ZERO(&readfds);
            FD_SET(PARENT_READ, &readfds);
            select(PARENT_READ + 1, &readfds, NULL, NULL, &tv);

            if(FD_ISSET(PARENT_READ, &readfds)) {
                read(PARENT_READ, buff, 4); // should read "test" which was written by the child to stdout
                buff[4] = '\0';
                close(PARENT_READ);
                close(PARENT_WRITE);
                fprintf(stderr, "in parent | message received: %s\n", buff);  // "test" is not in buff
            }

        // child process
        } else if(pid == 0) {
            close(PARENT_READ);
            close(PARENT_WRITE);

            dup2(CHILD_READ, STDIN_FILENO);
            dup2(CHILD_WRITE, STDOUT_FILENO);
            close(CHILD_READ);
            close(CHILD_WRITE);

            char *argv2[] = {"some random arg to make sure that argc == 2 in the child", NULL};
            execvp("two_way_pipes", argv2);
            _exit(0);
        // error forking child process
        } else {
            fprintf(stderr, "error forking the child\n");
        }
    }
}

更新

根据乔纳森的回答,我修改了传递给 execvp 的 arg2 数组以...

char *argv2[] = {"two_way_pipes", "1", NULL};
execvp("two_way_pipes", argv2);

这并没有解决问题。父母仍然无法从客户端读取“测试”。然而,为了回应 Jonathon 的回答和 William 的评论,我开始调整我的 exec 调用,并且由于某种原因将其更改为下面的行显示有效。

execl("two_way_pipes", "two_way_pipes", "1", NULL);

我很乐意接受任何解释为什么 execvp 调用不起作用但 execl 调用起作用的答案。

4

3 回答 3

6

除了Jonathon Reinhart提到的问题之外,很可能调用失败。execv()

为了测试这个修改这些行

execvp("two_way_pipes", argv2);
_exit(0);

成为

...
#include <errno.h>
...


execvp("two_way_pipes", argv2); /* On sucess exec*() functions never return. */
perror("execvp() failed); /* Getting here means execvp() failed. */
_exit(errno);

期待收到

execvp() failed: No such file or directory

修复此更改

execvp("two_way_pipes", argv2);

成为

execvp("./two_way_pipes", argv2);

此外,如果孩子没有被exec*()教育,那么这条线

read(PARENT_READ, buff, 4); // should read "test" which was written by the child to stdout

失败,反过来buff没有初始化,因此这条线

fprintf(stderr, "in parent | message received: %s\n", buff);  

引发未定义的行为。

要解决此问题,至少buff通过更改正确初始化

char buff[5];

成为

char buff[5] = "";
于 2013-10-05T09:44:24.703 回答
2

您的第一个条目argv2应该是可执行文件的名称(就像您传入的argv[0].

char *argv2[] = {"two_way_pipes", "some random arg...", NULL};
execvp("two_way_pipes", argv2);

execvp手册页

、和函数提供指向以空字符结尾的字符串的指针数组execv(),这些字符串表示新程序可用的参数列表。按照惯例,第一个参数应该指向与正在执行的文件关联的文件名。指针数组必须以 NULL 指针终止。execvp()execvpe()

于 2013-10-04T21:32:23.883 回答
1

正如 Jonathon Reinhart 所说,您应该更改以下几行:

char *argv2[] = {"some random arg to make sure that argc == 2 in the child", NULL};
execvp("two_way_pipes", argv2);

到:

char *argv2[] = {argv[0], "some random arg...", NULL};
execvp(argv[0], argv2);

然后它按预期工作:

echo test | ./two_way_pipes
in parent | message received: test

在您的程序中,您编写了“two_way_pipes”,但它不在您的 PATH 中,因此您确实需要额外的 ./ 所以 argv[0] 然后是(“./two_way_pipes”)。

于 2014-09-19T12:24:43.540 回答