【问题标题】:Problem forking fork() multiple processes Unix问题 fork() 多个进程 Unix
【发布时间】:2010-12-12 11:53:42
【问题描述】:

所以我有这个函数可以分叉 N 个子进程。然而,它似乎比指定的更多。你能告诉我我做错了什么吗? 谢谢

void forkChildren(int nChildren){
    int i;
    for(i = 1; i <= nChildren; i++){
        pid = fork();
        if(pid == 0)          
            printf("I'm a child: %d PID: %d\n",i, getpid());
    }

} 

我主要调用:

forkChildren(5);

我期待以下输出:

I'm a child: 1 PID: 2990
I'm a child: 2 PID: 2991
I'm a child: 3 PID: 2992
I'm a child: 4 PID: 2993
I'm a child: 5 PID: 2994

但我得到以下信息:

I'm a child: 1 PID: 2990
I'm a child: 2 PID: 2991
I'm a child: 3 PID: 2992
I'm a child: 4 PID: 2993
I'm a child: 5 PID: 2994
user@computer:~/directory/$ I'm a child: 2 PID: 2999
I'm a child: 3 PID: 3000
I'm a child: 3 PID: 3001
I'm a child: 4 PID: 3002
I'm a child: 5 PID: 3003
I'm a child: 5 PID: 3004
I'm a child: 4 PID: 3005
I'm a child: 5 PID: 3006
I'm a child: 4 PID: 3007
I'm a child: 5 PID: 3008
I'm a child: 3 PID: 3011
I'm a child: 4 PID: 3012
I'm a child: 4 PID: 3010
I'm a child: 5 PID: 3013
I'm a child: 5 PID: 3014
I'm a child: 5 PID: 3015
I'm a child: 4 PID: 3018
I'm a child: 5 PID: 3019
I'm a child: 5 PID: 3020
I'm a child: 5 PID: 3021
I'm a child: 5 PID: 3023
I'm a child: 5 PID: 3025
I'm a child: 5 PID: 3024
I'm a child: 4 PID: 3022
I'm a child: 5 PID: 3026
I'm a child: 5 PID: 3027

【问题讨论】:

  • 我现在明白了。我只是把 exit(0);在每个孩子打印其信息之后。

标签: c process fork


【解决方案1】:

当您fork 一个进程时,您基本上会得到该进程的两个(几乎)完全相同的副本,并且它们都将继续运行。

所以发生的事情是,孩子们自己在自己的进程空间中继续循环(在他们打印输出之后)以及作为父母这样做。而且,事实上,因为这些孩子也在分叉,所以孙子们也会从那个时候开始。我敢肯定有一个公式可以实际计算出你最终有多少个孩子(可能像 N 一样!)但我现在没有精力弄清楚。最好使用以下解决方案。

区分父子的方法是fork的返回值。

  • 如果您返回 -1,则您是父级,fork 失败。
  • 如果你得到一个零,你就是孩子。
  • 如果您返回一个正数,则您是父级,而该数字是子级 PID(因此您可以对其进行操作或通过wait 对其进行操作)。

这是一些测试代码:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

void forkChildren (int nChildren) {
    int i;
    pid_t pid;
    for (i = 1; i <= nChildren; i++) {
        pid = fork();
        if (pid == -1) {
            /* error handling here, if needed */
            return;
        }
        if (pid == 0) {
            printf("I am a child: %d PID: %d\n",i, getpid());
            sleep (5);
            return;
        }
    }
}

int main (int argc, char *argv[]) {
    if (argc < 2) {
        forkChildren (2);
    } else {
        forkChildren (atoi (argv[1]));
    }
    return 0;
}

还有一些输出显示正在发生的事情:

pax> forktest 5
I am a child: 1 PID: 4188
I am a child: 2 PID: 4180
I am a child: 3 PID: 5396
I am a child: 4 PID: 4316
I am a child: 5 PID: 4260

pax> _

【讨论】:

  • 我正在帮助朋友分配操作系统,上面的功能运行良好,谢谢!
【解决方案2】:

在本练习中,我将使用递归而不是 for 循环。这样,您可以多次调用 fork() 指令,但只在进程的两个副本之一上调用。您可以让子进程产生另一个子进程,从而拥有祖父母、祖父母等,或者您可以在父端调用 fork(),拥有一个“父亲”和多个孩子。这是实现后一种解决方案的代码示例:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int nChildren;

void myFork(int n);

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

  // just a check on the number of arguments supplied
  if (argc < 2) {
    printf("Usage: forktest <number_of_children>\n");
    printf("Example: forktest 5 - spawns 5 children processes\n");
    return -1;
  }

  nChildren = atoi(argv[1]);
  // starting the recursion...
  myFork(nChildren-1);
  return 0;
}

// the recursive function
void myFork(int n) {
  pid_t pid;

  pid = fork();

  // the child does nothing but printing a message on screen
  if (pid == 0) {
    printf("I am a child: %d PID: %d\n", nChildren-n, getpid());
    return;
  }

  // if pid != 0, we're in the parent
  // let's print a message showing that the parent pid is always the same...
  printf("It's always me (PID %d) spawning a new child (PID %d)\n", getpid(), pid);
  // ...and wait for the child to terminate.
  wait(pid);

  // let's call ourself again, decreasing the counter, until it reaches 0.
  if (n > 0) {
    myFork(n-1);
  }
}

【讨论】:

    【解决方案3】:

    fork() 调用产生了一个新进程,该进程在发生分叉的完全相同的点开始执行。所以,看起来 fork “返回两次”

    这里发生的情况是您的 fork() 调用返回两次,因此父进程和子进程都继续循环并产生新进程。然后每个孩子(原始父母和孩子)再次分叉,反复将进程数加倍......

    【讨论】:

    • 没有翻倍。子代仍在循环中递增 i,因此第二代中的每一代都只会创建四个孙子,不是 5。这是某种形式的阶乘。但是,除了那个minor nitpick,很好的答案。
    • @paxdiablo:孩子确实增加了 i,但只增加了它的本地副本。它对父母的 i 副本没有影响。
    【解决方案4】:

    每个子进程都会接上并继续循环。

    换句话说,子 1 被生成并继续循环的迭代 #2 等。

    当一个进程被派生时,当前进程的副本会被复制:生成的子进程在 fork() 调用之后继续执行。这就是为什么您必须注意逻辑中的返回码。

    【讨论】:

      猜你喜欢
      • 2014-02-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-06-17
      • 1970-01-01
      • 1970-01-01
      • 2015-01-22
      • 1970-01-01
      相关资源
      最近更新 更多