管,叉和执行 - 父母与子女过程的双向沟通

时间:2013-10-04 21:23:29

标签: c exec fork pipe

我的操作系统类中的赋值要求我通过在同一程序上递归调用exec来构建二进制进程树。目标是将一些任意任务拆分为单独的进程。父母应该通过未命名的管道与孩子和父母进行交流。这个想法是父母发送每个孩子一半的工作,并且这继续递归,直到满足基本案例,其中传递给每个孩子的字符串的长度是< = 2.然后孩子处理这些数据并发送结果通过管道返回父母。

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

  

在父母中|收到的消息:测试

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

  

在父母中|收到的消息:

似乎buff是空的,而不是从子进程读取。有人可以解释一下我做错了什么和/或标准的

方式
  1. 写给父母的执行儿童
  2. 从exec'd孩子的父母那里读书
  3. 从exec'd孩子写回父母
  4. 从父母的执行孩子那里读书
  5. 我需要使用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");
            }
        }
    }
    

    更新

    根据Jonathon的回答,我修改了传递给execvp的arg2数组到...

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

    这并没有解决问题。父母仍然无法从客户端读回“测试”。然而,为了回应乔纳森的回答和威廉的评论,我开始调整我的执行电话,并且出于某种原因将其改为下面的线下节目。

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

    我很乐意接受任何解释,解释为什么execvp调用不起作用,但execl调用没有。

3 个答案:

答案 0 :(得分:6)

the issue mentioned 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] = "";

答案 1 :(得分:2)

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

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

来自execvp man page

  

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

答案 2 :(得分: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

在你的程序中,你写了&#34; two_way_pipes&#34;但它不在你的PATH中所以你真的需要额外的./所以argv [0]然后是(&#34; ./ two_way_pipes&#34;)。