C语言中的Fork()函数

22
以下是Fork函数实际运行的示例。以下也是输出结果。我的主要问题与fork的调用有关,即如何更改值。因此,pid1、2和3最初为0,并在发生分叉时进行更改。这是因为每次发生分叉时,值都会被复制到子进程中,并且特定值在父进程中得到更改吗?基本上,使用fork函数时值如何更改?
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

int main() {
    pid_t pid1, pid2, pid3;
    pid1=0, pid2=0, pid3=0;
    pid1= fork(); /* A */
    if(pid1==0){
        pid2=fork(); /* B */
        pid3=fork(); /* C */
    } else {
        pid3=fork(); /* D */
        if(pid3==0) {
            pid2=fork(); /* E */
        }
        if((pid1 == 0)&&(pid2 == 0))
            printf("Level 1\n");
        if(pid1 !=0)
            printf("Level 2\n");
        if(pid2 !=0)
           printf("Level 3\n");
        if(pid3 !=0)
           printf("Level 4\n");
       return 0;
    }
}

那么这就是执行过程。

----A----D--------- (pid1!=0, pid2==0(as initialized), pid3!=0, print "Level 2" and "Level 4")
    |    |
    |    +----E---- (pid1!=0, pid2!=0, pid3==0, print "Level 2" and "Level 3")
    |         |
    |         +---- (pid1!=0, pid2==0, pid3==0, print "Level 2")
    |
    +----B----C---- (pid1==0, pid2!=0, pid3!=0, print nothing)
         |    |
         |    +---- (pid1==0, pid2==0, pid3==0, print nothing)
         |
         +----C---- (pid1==0, pid2==0, pid3!=0, print nothing)
              |
              +---- (pid1==0, pid2==0, pid3==0, print nothing)
理想情况下,以下是我希望看到的解释方式,因为这种方式对我有意义。* 是我的主要困惑所在。例如,当子进程fork时pid1 = fork();,它会创建一个具有父进程所有值的进程,但它是否会将一个值传递给父进程的pid1,比如说1? 这意味着子进程的pid1=0,pid2=0和pid3=0,父进程的pid1=2,pid2和pid3等于0吗?enter image description here

2
fork()函数创建一个新进程,该进程获得父进程数据的副本,就像在fork()之前一样。此外,调用fork()可能有三个结果,而不仅仅是2或1(如代码所示)。这三个结果分别是:<0表示错误,=0表示子进程,>0表示父进程。代码应始终检查所有三个结果。例如:pid2=fork(); /* B */ pid3=fork(); /* C */ 是不良的编码实践,因为不知道哪个进程创建了'C'(实际上将有两个'C'进程)。对于进程'E'也存在类似的考虑。 - user3629249
由于之前的“if”语句,子进程'B'和'C'永远不会到达printf()语句。只有'A'、'D'和'E'能够到达printf()语句。 - user3629249
在这个视频中,父进程的值被子进程的ID覆盖了,这是正确的吗? - Plisken
4个回答

26

系统调用fork()用于创建进程。它不需要任何参数并返回一个进程ID。fork()的目的是创建一个新进程,该进程成为调用者的子进程。创建完新的子进程后,两个进程都将执行在fork()系统调用之后的下一条指令。因此,我们必须区分父进程和子进程。这可以通过测试fork()的返回值来完成。

fork()是一个系统调用,您不应将其视为普通的C函数。当发生fork()时,实际上会创建两个具有自己地址空间的新进程。在fork()调用之前初始化的变量在两个地址空间中存储相同的值。但是在任一进程的地址空间中修改的值不会影响其他进程,其中一个是父进程,另一个是子进程。 因此,如果要区分父进程和子进程,可以通过测试fork()的返回值来实现。

pid=fork();
如果在随后的代码块中您检查了pid的值。两个进程都将在您的代码的整个长度内运行。那么我们如何区分它们。 同样,Fork是一个系统调用,这里有区别。在新创建的子进程中,pid将存储0,而在父进程中,它将存储正值。 pid中的负值表示fork错误。
当我们测试pid的值以确定它是否等于零或大于它时,我们实际上是在找出我们是否处于子进程还是父进程中。 阅读更多关于Fork的内容

Fork是一个系统调用。一旦你调用它,你的进程就会被复制。在子进程中,pid被设置为零,在父进程中,pid被赋予一个正值。现在剩下的代码部分对于这两个进程来说是相同的(我们正在执行相同的代码)。因此,为了区分这些进程,我们使用pid值,正如之前所述,这个值对于子进程和父进程是不同的。 - Rahul Jha
在这个视频中,父进程的值被子进程的ID覆盖了,这是正确的吗? - Plisken
请告诉我视频让你感到困惑的时间点!我不想浪费七分钟看我已知道的内容的教程。 - Rahul Jha
在执行了两次fork之后,他又进行了第三次fork,因为他在第二次fork中改变了一个值。 - Plisken

6
int a = fork(); 

创建一个“clone?”的进程副本,该副本共享执行堆栈。父进程和子进程之间的区别在于函数的返回值。

子进程返回0,父进程返回新pid。

每次复制堆栈变量的地址和值。执行在代码中已到达的点继续进行。

每次fork只有一个值被修改——来自fork的返回值。


那么你的意思是孩子得到父母的值,但向父母返回任意值,例如“1”,这会改变父母的值? - Plisken
@plisken,该值是返回的子进程的进程ID。在操作系统中,它为进程创建了一个新的内存副本 - 所有文件句柄、库和分配的内存都完全相同。然后,操作系统将fork函数的返回值设置为新的pid,对于父进程和子进程,返回值分别为0。fork函数是一个原始函数。除非你是操作系统,否则无法用C/C++编写它。提供伪代码并没有什么意义。 - mksteve
在这个视频中,父进程的值被子进程的ID覆盖了,这是正确的吗? - Plisken
@Plisken,这些进程都是独立的,它们都必须执行整个程序,这意味着。 - mksteve
让我们在聊天中继续这个讨论 - mksteve
显示剩余2条评论

3

首先是fork()函数文档链接:

http://pubs.opengroup.org/onlinepubs/009695399/functions/fork.html

内核提供pid。每当内核创建新进程时,它会增加内部pid计数器并将该新进程分配给该唯一的新pid,并确保没有重复项。一旦pid达到一定高度,它就会卷起并重新开始。

因此,您永远不知道fork()将得到什么pid,只知道父进程将保持其独特的pid,并且fork()将确保子进程具有新的独特pid。这在上面提供的文档中已经说明。

如果您继续阅读文档,您将看到fork()对于子进程返回0,并将子进程的新唯一pid返回给父进程。如果子进程想要知道自己的新pid,则必须使用getpid()进行查询。

pid_t pid = fork()
if(pid == 0) {
    printf("this is a child: my new unique pid is %d\n", getpid());
} else {
    printf("this is the parent: my pid is %d and I have a child with pid %d \n", getpid(), pid);
}

以下是对您的代码的一些行内注释

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

int main() {
    pid_t pid1, pid2, pid3;
    pid1=0, pid2=0, pid3=0;
    pid1= fork(); /* A */
    if(pid1 == 0){
        /* This is child A */
        pid2=fork(); /* B */
        pid3=fork(); /* C */
    } else {
        /* This is parent A */
        /* Child B and C will never reach this code */
        pid3=fork(); /* D */
        if(pid3==0) {
            /* This is child D fork'ed from parent A */
            pid2=fork(); /* E */
        }
        if((pid1 == 0)&&(pid2 == 0)) {
            /* pid1 will never be 0 here so this is dead code */
            printf("Level 1\n");
        }
        if(pid1 !=0) {
            /* This is always true for both parent and child E */
            printf("Level 2\n");
        }
        if(pid2 !=0) {
           /* This is parent E (same as parent A) */
           printf("Level 3\n");
        }
        if(pid3 !=0) {
           /* This is parent D (same as parent A) */
           printf("Level 4\n");
        }
    }
    return 0;
}

我理解,我明白。但是请看这个视频 https://www.youtube.com/watch?v=WcsZvdlLkPw 。fork()函数将一个值传递给父进程,预计是新的进程ID。这是正确的吗? - Plisken
是的,这是正确的,fork()会告诉父进程新的子进程具有什么pid,我已经说明了这一点。请还参见链接中“RETURN VALUE”部分的文档。 - leakim
我已经阅读了它,非常感激,这对我非常有帮助。但这是我的最后一个问题。一旦父进程的值被子进程ID更改,为什么需要像视频中所做的那样修改其他进程? - Plisken

1
我认为每个进程在创建后开始执行你编写的代码行,类似于以下内容...
pid=fork() at line 6. fork function returns 2 values 
you have 2 pids, first pid=0 for child and pid>0 for parent 
so you can use if to separate

.

/*
    sleep(int time) to see clearly
    <0 fail 
    =0 child
    >0 parent
*/
int main(int argc, char** argv) {
    pid_t childpid1, childpid2;
    printf("pid = process identification\n");
    printf("ppid = parent process identification\n");
    childpid1 = fork();
    if (childpid1 == -1) {
        printf("Fork error !\n");
    }
    if (childpid1 == 0) {
        sleep(1);
        printf("child[1] --> pid = %d and  ppid = %d\n",
                getpid(), getppid());
    } else {
        childpid2 = fork();
        if (childpid2 == 0) {
            sleep(2);
            printf("child[2] --> pid = %d and ppid = %d\n",
                    getpid(), getppid());
        } else {
            sleep(3);
            printf("parent --> pid = %d\n", getpid());
        }
    }
    return 0;
}

//pid = process identification
//ppid = parent process identification
//child[1] --> pid = 2399 and  ppid = 2398
//child[2] --> pid = 2400 and ppid = 2398
//parent --> pid = 2398

linux.die.net

一些大学的东西


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