【问题标题】:Child process takes over parent process when running fork()运行 fork() 时子进程接管父进程
【发布时间】:2019-10-15 02:22:17
【问题描述】:

我在尝试通过 execvp() 运行多个命令时遇到问题。为此,我有一个 while 循环遍历每个命令,解析它,然后调用一个函数来使用 fork + exec。

问题是,我将运行 fork + exec,当我等待 exec 通过时,父级运行并继续到第二个循环,即。第二个命令。但是,据我了解,发生的情况是上一个循环中的子进程接管,突然间,子进程就是当前进程。

如何在子进程中运行进程,但在父进程中保持控制?我在 SO 上看到的所有示例都是父进程在继续之前等待子进程终止,但我在这个分配中没有选择 - 我应该让子进程保持运行并稍后检查看看它们是否还在运行。

这是我思考过程的一些伪代码:

funct forkprocess() {
   //call fork
   //call execvp within child process, let process run and return control to parent
       //if execvp failed, kill child process
   //return child pid
}

int main() {
   while(not end of file containing list of commands) :
      //parse command for execvp call
      //call forkprocess() to run process
      //if childpid is returned, report it and store pid
      //else, report failure
   }
}

我的输出接近于以下:

\\parent PID is printed
\\any code outside the if-else ladder for fork is printed
\\it jumps back to main and prints any statements there

[jumps to next iteration in loop, ie. the next command]

\\child PID is printed
\\parent PID is printed
\\any code outside the if-else ladder for fork is printed
\\it jumps back to main and prints any statements there
\\child PID is printed

【问题讨论】:

  • How do you run a process in the child process, but maintain control in the parent? 。这意味着什么? “控制”是什么意思?您是否试图确保父母不会让出处理器?您是否试图确保在创建所有孩子之前不会开始执行第一个孩子?您是否试图确保所有孩子都按特定顺序运行?
  • “当前进程”是什么意思?如果您有多个处理器,并且多个子进程处理所有正在运行并将数据写入您的 tty ......那么您将获得交错输出。听起来您正在寻找一种同步机制。
  • fork() 调用中,我尝试在子进程中调用exec()exec() 是我尝试运行的命令(例如ls a)。但是,我只想运行exec(),然后返回父进程继续运行更多子进程以获取我拥有的更多命令。同时,子进程只是在后台继续运行。希望这是有道理的,我无法解释它。
  • 调用fork后,父子进程同时运行。如果孩子执行“ls”,那么它会将输出写入其标准输出,这可能是您调用程序的终端。父级仍在运行(除非它因某种原因退出)并产生更多子级。仅仅因为孩子正在向 tty 写入输出并不意味着它已经“接管[n]”。
  • 你所说的某些东西引起了点击,所以我回去修复了一些东西。现在我可以看到您所指的行为。现在的问题似乎是在while 循环的第一次运行中,它似乎只通过了父代码时钟。直到第二次运行 while 循环(应该用于第二个命令),第一次运行的子进程才开始运行。有什么办法可以解决这个延迟吗?

标签: c process fork execvp waitpid


【解决方案1】:

您需要确保 exec 仅在子级中调用,而不是在父级中调用。当fork() 返回时,它会这样做两次(一次在子级中,一次在父级中),并返回以下values

  • 如果我们在父节点中,则非零(子节点的 PID)。我们可以使用这个 PID 来等待子进程使用waitpid 终止。
  • 如果我们在孩子中,则为零。
  • 如果 fork 失败则为负(在这种情况下,它只返回一次,在父进程中,因为无法创建子进程)

您可能需要考虑使用以下结构:

int pid = fork();
if(pid < 0) {
    perror("fork"); // remember to error check!
} else if (pid == 0) {
    // We are in the child
    // ...
    if(execvp(...) < 0) {
        perror("exec");
    }
} else {
    // We are in the parent; wait for the child to terminate using waitpid if desired.
    waitpid(...);
}

为简单起见,我将waitpid 调用包括在此处;由于您的任务要求您保持子进程运行,您可以稍后使用该函数或通过处理 SIGCHLD 来检查它们的状态。

【讨论】:

  • 感谢您的回答!我已经在我的代码中做到了这一点——我使用了waitpid()WNOHANG 选项,所以我不必等待子进程。据我所知,子进程不会终止。在这种情况下如何运作?
  • @B.M.Corwen 如果没有子进程退出并且您调用waitpid(0, &amp;status, WNOHANG),它将立即返回零(如果子进程确实已终止,则与子 PID 相反):man7.org/linux/man-pages/man2/waitpid.2.html#RETURN_VALUE。我认为尝试将 fork+exec 与等待孩子的步骤结合起来可能会产生一些混乱;根据您的描述,问题出在前者而不是后者。
  • 对不起,我还是不太明白。在我调用 fork 的函数中,我实际上已经按照您上面建议的方式构建了我的代码,但我仍然遇到子进程接管的问题。虽然控件最终会回到父级,但存在一个延迟,执行程序似乎直到下一个循环才运行,我已经应该在下一个循环中测试下一个命令。有什么方法可以解决这个问题吗?
  • Exec 也可能失败
【解决方案2】:
int main(int argc, char ** argv) {
    int status;

    if((waitpid(fork(), &status, 0)) == -1) {
       execvp(...);
       /* print error */
       exit(1);
    }
    return 0;
}

感谢提问,如果fork直接传入waitpid,父进程会等待,同时子进程会执行exec,成功退出线程,失败返回,继续打印错误退出。 这对于一次执行多个功能非常有帮助...

int main(int argc, char ** argv) {
    int *status = malloc(sizeof(int) * (argc - 1));
    int i = 0;

    while(((waitpid(fork(), &status[i++], 0)) == -1) || i < argc) {
       execvp(argv[i], &argv[i + 1]);
       /* print error */
       exit(1);
    }
    return 0;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-02-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-12-06
    相关资源
    最近更新 更多