使用fork()在C语言中将1个父进程分裂成3个子进程

5

你好,我一直在开发一个程序,它会fork子进程,并且稍后还会从每个子进程中fork更多的子进程,但这不是我需要帮助的地方。当我运行我的程序(在这里它是一个函数,但是工作方式相同),我应该有一个父进程(PPID)生成3个子进程(PIDS=1,2,3),但我得到的要么是相同的PID和PPID 3次(我的当前代码),要么以前我得到的是3个父进程,每个父进程都有一个子进程,并且PPIDS和PIDS都不同,但是PPIDs与先前的子进程PIDs完全相同。在我的最新尝试中,它从不显示父亲的信息在儿子之上。它应该看起来像这样:

[dad] hi am I PID 1234 and I come from ####(dont care what this number is)
[son] hi i am PID 1111 and I come from PPID 1234
[son] hi i am PID 1112 and I come from PPID 1234
[son] hi i am PID 1113 and I come from PPID 1234

这是我的代码。如果可能的话,我只是在寻找提示,除非我犯了像“将fork()移动到子进程”之类的愚蠢错误。

此外,我有一个child_count变量,只是为了方便计算子进程数量。

 int forking(null)
{
       void about(char *);
        int i=0;
        int j=0;
        int child_count =0;
        about("dad");

    for(i = 0; i < 3; i++ ){
        pid_t child = 0;
        child = fork();


            if (child < 0) { //unable to fork error
                    perror ("Unable to fork");
                    exit(-1);}

           else if (child == 0){ //child process
                    about ("son");
                    printf("I am child #%d \n",child_count);
                    child_count++;
                    exit(0);}

            else { //parent process (do nothing)

                }
            }

                for(j = 0; j < 3; j++ ){
                            wait(NULL);//wait for parent to acknowledge child process
                            }
return 0;
}

3
记住两件事:第一,一旦你进行了分叉,子进程会获得父进程的内存副本,当子进程修改变量时,这些变量只在子进程中改变,父进程(或任何“兄弟姐妹”)将不会看到这些变量的更改。第二点是,在else语句中调用wait将阻塞父进程和子进程。 - Some programmer dude
3
你的代码出现了一个严重的“未定义行为”问题。你定义了 child 变量,但在初始化之前就使用了它。未初始化的局部变量具有“不确定”的值,如果在没有初始化的情况下使用它们会导致未定义行为。你可能需要考虑在哪里调用 fork - Some programmer dude
哦,你是指像pid_t child = 0这样的吗?你说wait()会阻塞父进程和子进程?关于内存副本,你能给我一个示例代码吗?这样更容易理解。 - Jite
关于wait,在fork调用之后你会无条件地调用它,因此它将在父进程和子进程中都被调用。如果你将child初始化为零,在你的条件语句中代码将采取什么路径?进程不共享内存,一个进程的内存只为该进程所有,这个内存包括像child_count这样的变量,所以在一个进程中修改一个变量不会导致在其他任何进程中修改它。再次强调,考虑你在哪里做或者应该fork调用 - Some programmer dude
结尾的 while 循环需要改成 for(i=0;i<3;i++) 循环。如果在 while 循环体中放置一个 printf,你会发现它从未运行,因为由于前面的 for 循环,i 已经是 3 了。 - user3386109
啊,谢谢user3386109。我之前误读了一些资料,以为wait需要while循环,但实际上它可以是任何循环。 - Jite
2个回答

5
父进程需要做以下三件事:
- 打印一条信息
- 调用fork三次
- 等待三个子进程退出

每个子进程需要做以下两件事:
- 打印一条信息
- 退出

因此代码非常简单。

int main( void )
{
    printf( "[dad] pid %d\n", getpid() );

    for ( int i = 0; i < 3; i++ )
        if ( fork() == 0 )
        {
            printf( "[son] pid %d from pid %d\n", getpid(), getppid() );
            exit( 0 );
        }

    for ( int i = 0; i < 3; i++ )
        wait( NULL );
}

生成如下输出
[dad] pid 1777
[son] pid 1778 from pid 1777
[son] pid 1779 from pid 1777
[son] pid 1780 from pid 1777

2

需要记住的一件事是,当你使用fork时,父进程和子进程都会继续运行从那个点开始的代码。

因此,如果您没有正确执行子/父进程检测,子进程很可能会启动它们自己的子进程。

一个好的方法是使用计数器与fork调用返回的进程ID结合起来启动三个子进程而不生成孙进程,如下所示:

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

#define COUNT 3

int main(void) {
    # Desired and actual count.

    int count = COUNT, children = 0;

    // Force parent initially.

    pid_t retpid = 1;

    // Only fork if limit not reached AND is parent (children
    //   will exit loop with retpid == 0).

    while (count-- > 0 && retpid > 0)
        // Adjust actual count if successful.

        if ((retpid = fork()) > 0)
            children++;

    // Detect parent, all forks returned non-zero.

    if (retpid != 0) {
        printf("Parent %d spawned %d/%d children\n",
            getpid(), children, COUNT);

        // Wait for children to finish.

        while (children-- > 0)
            wait(NULL);
    } else {
        // Otherwise you were one of the children.

        printf("Child %d, sired by %d\n", getpid(), getppid());
    }

    return 0;
}

尽管由于调度的不确定性,这可能不是按照特定顺序输出,但它确实输出了您所需的内容:

Parent 26210 successfully spawned 3/3 children
Child 26212, sired by 26210
Child 26213, sired by 26210
Child 26211, sired by 26210

检查返回的PID将确保只有父进程进行任何分叉,而计数将限制其特定数量。
您还需要注意输出缓冲。当您进行分叉时,您可能会得到两个具有缓冲输出数据的进程。
在可以检测到输出设备为终端的情况下,刷新通常会在输出换行时发生,因此您的printf调用可能不会为正常运行重复输出。
您只需要意识到,如果将输出重定向到文件,则可能会获得有趣的结果,例如。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接