C中的fork()函数

Posted

技术标签:

【中文标题】C中的fork()函数【英文标题】:Fork() function in C 【发布时间】:2015-12-24 23:40:11 【问题描述】:

下面是 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)

理想情况下,下面是我希望看到的解释方式,因为这种方式对我来说很有意义。 * 是我的主要困惑所在。例如,当孩子分叉pid1 = fork(); 时,它创建了一个包含父进程所有值的进程,但它是否会传递一个值,比如让我们说 1 给父进程 pid1?这意味着孩子将有 pid 1=0、pid2=0 和 pid3=0,而父母则为 pid1=2 和 pid2 和 3 等于 0?

【问题讨论】:

fork() 创建一个新进程,该进程获取父数据的副本,就像在 fork() 之前一样。此外,对 fork() 的调用可以产生三个结果,而不仅仅是 2 或 1(正如代码所做的那样),三个结果是:0 表示父项。代码应始终检查所有三个结果。 IE。这:pid2=fork(); /* B */ pid3=fork(); /* C */ 是不好的编码习惯,因为不知道哪个进程创建了“C”(实际上会有 2 个“C”进程)进程“E”也存在类似的考虑 子 'B' 和 'C' 永远不会到达 printf() 语句,因为之前的 'if' 语句。只有 'A'、'D' 和 'E' 会到达 printf() 语句。 youtube.com/watch?v=WcsZvdlLkPw 在此父进程的值被子进程 ID 覆盖,这是正确的吗? 【参考方案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

some uni stuff

【讨论】:

【参考方案2】:

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

Fork 是一个系统调用,你不应该把它当作一个普通的 C 函数。当 fork() 发生时您有效地创建了两个具有自己地址空间的新进程。在 fork() 调用之前初始化的变量在两个地址空间中存储相同的值。但是,在任一进程的地址空间内修改的值在其他进程中不受影响,其中一个是父进程,另一个是子进程。 所以如果,

pid=fork();

如果您在随后的代码块中检查 pid 的值。两个进程都会运行整个代码长度。那么我们如何区分它们。 Fork 是一个系统调用,这里有区别。在新创建的子进程中,pid 将存储 0,而在父进程中它将存储一个正值。pid 中的负值表示 fork 错误。

当我们测试 pid 的值时 找出它是等于零还是大于它,我们实际上是在找出我们是在子进程还是父进程中。

Read more about Fork

【讨论】:

Fork 是一个系统调用。只要你调用它。你的进程是重复的。在子进程中,pid 设置为零,在父进程中,pid 被赋予正值。现在两个进程的代码的剩余部分是相同的(我们正在执行相同的代码)。因此,为了区分我们使用 pid 值的进程,如前所述,子进程和父进程是分开的 youtube.com/watch?v=WcsZvdlLkPw 在此父进程的值被子进程 ID 覆盖,这是正确的吗? 请告诉我视频让您感到困惑的时间!我无法浏览我所知道的 7 分钟视频教程 youtube.com/… 在进行了 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);

以下是您代码中的一些内联 cmets

#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;

【讨论】:

我明白,我明白。但是请注意这个youtube.com/watch?v=WcsZvdlLkPw,fork 将一个值传递给父进程,大概是新的进程 id。这是正确的吗? 是的,这是真的,fork() 会告诉父母新孩子有什么 pid,我说了这一点。另请参阅“返回值”部分下提供的链接中的文档 我已经阅读了我非常感谢它非常有帮助。但这是我的最后一个查询。一旦使用子进程 ID 更改了父值,为什么有必要像视频中所做的那样更改其他进程。【参考方案4】:
int a = fork(); 

创建一个重复的进程“克隆?”,它共享执行堆栈。父子的区别在于函数的返回值。

得到 0 的子进程返回,父进程得到新的 pid。

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

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

【讨论】:

所以你的意思是,孩子获得了父母的值,但将任意值传回给父母,例如“1”,这会改变父母的价值? @plisken,该值是返回的子进程的进程 ID。在操作系统中,它为进程创建一个新的内存副本——所有文件句柄、库和分配的内存都完全相同。然后操作系统将 fork 函数的返回值设置为新的 pid,因为父进程和子进程的返回值都是 0。fork 函数是一个原语。除非你是操作系统,否则它不能用 C/C++ 编写。想出伪代码真的没有意义。 youtube.com/watch?v=WcsZvdlLkPw 在此父进程的值被子进程 ID 覆盖,这是正确的吗? @Plisken “覆盖”是返回值。它描述了x = function(); /* change the value of x */不修改父级的任何内部状态时的变化除了fork输出的返回值 我明白了,谢谢。最后一个查询youtube.com/…你能告诉我他为什么第三次分叉吗?他做了 2 次,在第二次分叉后,他不得不再次从第一次分叉。这是为什么?

以上是关于C中的fork()函数的主要内容,如果未能解决你的问题,请参考以下文章

C 中的管道 - 我必须使用 fork 吗?

子进程是否从 Fork 函数 后 开始执行,执行函数后的代码。Fork函数之前的不执行?

linux C语言 clone() 和 fork() 的区别,fork函数的用法,主要应用场景(clone是fork的升级版本,可以将创建出来的进程变成父进程的兄弟进程)

在 C 编程中,如何 fork() 在子进程中运行 N 个函数调用?

使用由`fork`创建的多个C线程的回调函数时,Rust Mutex不起作用

使用 fork() 和 execvp() 函数创建 C 程序时遇到问题