98

在 Linux 的许多程序和手册页中,我看到使用fork(). 为什么我们需要使用fork()它,它的目的是什么?

4

16 回答 16

119

fork()是在 Unix 中创建新进程的方式。当您调用时fork,您正在创建自己的进程的副本,该进程具有自己的地址空间。这允许多个任务彼此独立运行,就好像它们各自拥有机器的全部内存一样。

以下是 的一些示例用法fork

  1. 您的shell用于fork运行您从命令行调用的程序。
  2. 像apache这样的Web 服务器用于fork创建多个服务器进程,每个进程在自己的地址空间中处理请求。如果一个死机或内存泄漏,其他人不会受到影响,因此它可以作为一种容错机制。
  3. 谷歌浏览器用于fork在单独的进程中处理每个页面。这将防止一页上的客户端代码使您的整个浏览器停机。
  4. fork用于在一些并行程序(如使用MPI编写的程序)中生成进程。请注意,这与使用线程不同,线程没有自己的地址空间并且存在进程中。
  5. 脚本语言fork间接使用来启动子进程。例如,每次您使用subprocess.PopenPython 中的命令时,您fork都是一个子进程并读取其输出。这使程序能够协同工作。

在 shell 中的典型用法fork可能如下所示:

int child_process_id = fork();
if (child_process_id) {
    // Fork returns a valid pid in the parent process.  Parent executes this.

    // wait for the child process to complete
    waitpid(child_process_id, ...);  // omitted extra args for brevity

    // child process finished!
} else {
    // Fork returns 0 in the child process.  Child executes this.

    // new argv array for the child process
    const char *argv[] = {"arg1", "arg2", "arg3", NULL};

    // now start executing some other program
    exec("/path/to/a/program", argv);
}

shell 产生一个子进程 usingexec并等待它完成,然后继续它自己的执行。请注意,您不必以这种方式使用 fork。您总是可以产生许多子进程,就像并行程序可能做的那样,每个子进程都可能同时运行一个程序。基本上,每当您在 Unix 系统中创建新进程时,您都在使用fork(). 对于 Windows 等效项,请查看CreateProcess.

如果你想要更多的例子和更长的解释,维基百科有一个不错的总结。这里有一些关于进程、线程和并发如何在现代操作系统中工作的幻灯片。

于 2009-06-12T04:55:14.067 回答
19

fork() 是 Unix 创建新进程的方式。在您调用 fork() 时,您的进程被克隆,两个不同的进程从那里继续执行。其中一个,孩子,将让 fork() 返回 0。另一个,父母,将让 fork() 返回孩子的 PID(进程 ID)。

例如,如果您在 shell 中键入以下内容,shell 程序将调用 fork(),然后在子进程中执行您传递的命令(在本例中为 telnetd),而父进程也会再次显示提示作为指示后台进程PID的消息。

$ telnetd &

至于你创建新进程的原因,这就是你的操作系统可以同时做很多事情的方式。这就是为什么您可以运行一个程序并在它运行时切换到另一个窗口并执行其他操作的原因。

于 2009-06-12T04:53:48.330 回答
10

fork() 用于创建子进程。当调用 fork() 函数时,将生成一个新进程,并且 fork() 函数调用将为子进程和父进程返回不同的值。

如果返回值为 0,你就知道你是子进程,如果返回值是一个数字(恰好是子进程 id),你就知道你是父进程。(如果它是负数,则分叉失败并且没有创建子进程)

http://www.yolinux.com/TUTORIALS/ForkExecProcesses.html

于 2009-06-12T04:55:39.103 回答
10

fork() 基本上用于为您调用此函数的进程创建一个子进程。每当您调用 fork() 时,它都会为子 ID 返回零。

pid=fork()
if pid==0
//this is the child process
else if pid!=0
//this is the parent process

这样,您可以为父母和孩子提供不同的操作,并利用多线程功能。

于 2009-06-12T05:02:31.723 回答
9

fork() 将创建一个与父进程相同的新子进程。因此,之后您在代码中运行的所有内容都将由两个进程运行——例如,如果您有一台服务器,并且您想处理多个请求,这将非常有用。

于 2009-06-12T04:53:22.417 回答
5

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

如果 fork() 返回负值,则子进程的创建不成功。fork() 向新创建的子进程返回零。fork() 向父进程返回一个正值,即子进程的进程 ID。返回的进程 ID 是 sys/types.h 中定义的 pid_t 类型。通常,进程 ID 是一个整数。此外,进程可以使用函数 getpid() 来检索分配给该进程的进程 ID。因此,在系统调用 fork() 之后,一个简单的测试就可以知道哪个进程是子进程。请注意,Unix 将制作父地址空间的精确副本并将其提供给子。因此,父进程和子进程具有独立的地址空间。

让我们通过一个例子来理解它,以使上述几点清楚。此示例不区分父进程和子进程。

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

#define   MAX_COUNT  200
#define   BUF_SIZE   100

void  main(void)
{
     pid_t  pid;
     int    i;
     char   buf[BUF_SIZE];

     fork();
     pid = getpid();
     for (i = 1; i <= MAX_COUNT; i++) {
          sprintf(buf, "This line is from pid %d, value = %d\n", pid, i);
          write(1, buf, strlen(buf));
     } 
}

假设上述程序执行到调用 fork() 为止。

如果对 fork() 的调用成功执行,Unix 将制作两个相同的地址空间副本,一个用于父级,另一个用于子级。两个进程都将在 fork() 调用之后的下一条语句开始执行。在这种情况下,两个进程都将在分配时开始执行

pid = .....;

两个进程都在系统调用 fork() 之后立即开始执行。由于两个进程具有相同但独立的地址空间,因此在 fork() 调用之前初始化的那些变量在两个地址空间中具有相同的值。由于每个进程都有自己的地址空间,因此任何修改都将独立于其他进程。也就是说,如果父进程改变了其变量的值,那么修改只会影响父进程地址空间中的变量。由 fork() 调用创建的其他地址空间不会受到影响,即使它们具有相同的变量名。

使用 write 而不是 printf 的原因是什么?这是因为 printf() 是“缓冲的”,这意味着 printf() 会将进程的输出组合在一起。在为父进程缓冲输出的同时,子进程也可能使用 printf 打印出一些信息,这些信息也会被缓冲。结果,由于输出不会立即发送到屏幕,您可能无法获得预期结果的正确顺序。更糟糕的是,这两个进程的输出可能会以奇怪的方式混合在一起。为了克服这个问题,您可以考虑使用“无缓冲”写入。

如果您运行此程序,您可能会在屏幕上看到以下内容:

................
This line is from pid 3456, value 13
This line is from pid 3456, value 14
     ................
This line is from pid 3456, value 20
This line is from pid 4617, value 100
This line is from pid 4617, value 101
     ................
This line is from pid 3456, value 21
This line is from pid 3456, value 22
     ................

进程 ID 3456 可能是分配给父进程或子进程的进程 ID。由于这些进程是同时运行的,因此它们的输出线以一种相当不可预测的方式混合在一起。此外,这些行的顺序由 CPU 调度程序决定。因此,如果您再次运行该程序,您可能会得到完全不同的结果。

于 2013-12-26T19:24:00.267 回答
4

如果您正在编写应用程序,您可能不需要在日常编程中使用 fork。

即使您确实希望您的程序启动另一个程序来执行某些任务,也有其他更简单的接口在幕后使用 fork,例如 C 和 perl 中的“系统”。

例如,如果您希望您的应用程序启动另一个程序(例如 bc)为您做一些计算,您可以使用“系统”来运行它。系统执行“fork”以创建新进程,然后执行“exec”将该进程转换为 bc。一旦 bc 完成,系统会将控制权返回给您的程序。

您也可以异步运行其他程序,但我不记得如何了。

如果你正在编写服务器、shell、病毒或操作系统,你更有可能想要使用 fork。

于 2009-06-12T14:09:39.350 回答
3

Fork() 用于创建每个人都编写的新进程。

这是我的代码,它以二叉树的形式创建进程.......它将要求扫描您想要在二叉树中创建进程的级别数

#include<unistd.h> 
#include<fcntl.h> 
#include<stdlib.h>   
int main() 
{
int t1,t2,p,i,n,ab;
p=getpid();                
printf("enter the number of levels\n");fflush(stdout);
scanf("%d",&n);                
printf("root %d\n",p);fflush(stdout);
for(i=1;i<n;i++)    
{        
    t1=fork();

    if(t1!=0)
        t2=fork();        
    if(t1!=0 && t2!=0)        
        break;            
    printf("child pid %d   parent pid %d\n",getpid(),getppid());fflush(stdout);
}   
    waitpid(t1,&ab,0);
    waitpid(t2,&ab,0);
return 0;
}

输出

  enter the number of levels
  3
  root 20665
  child pid 20670   parent pid 20665
  child pid 20669   parent pid 20665
  child pid 20672   parent pid 20670
  child pid 20671   parent pid 20670
  child pid 20674   parent pid 20669
  child pid 20673   parent pid 20669
于 2012-01-12T19:08:46.760 回答
3

多处理是计算的核心。例如,您的 IE 或 Firefox 可以创建一个进程,在您仍在浏览 Internet 时为您下载文件。或者,当您在文字处理器中打印文档时,您仍然可以查看不同的页面并对其进行一些编辑。

于 2009-06-12T04:55:49.947 回答
3

Fork 创建新进程。如果没有 fork,您将拥有一个只能运行 init 的 unix 系统。

于 2011-04-17T12:04:45.767 回答
2

首先需要了解什么是 fork() 系统调用。让我解释

  1. fork() 系统调用创建父进程的精确副本,它制作父堆栈、堆、初始化数据、未初始化数据的副本,并以只读模式与父进程共享代码。

  2. fork 系统调用在写时复制的基础上复制内存,即当需要复制时,子进程会在虚拟内存页面中创建。

现在 fork() 的目的:

  1. fork() 可以用在有分工的地方,比如服务器要处理多个客户端,所以父级必须定期接受连接,所以服务器会为每个客户端进行分叉以执行读写。
于 2017-03-25T13:53:42.730 回答
1

fork()用于产生子进程。通常,它用于与线程类似的情况,但存在差异。与线程不同,fork()它创建了完全独立的进程,这意味着子进程和父进程虽然在调用点是彼此的直接副本fork(),但它们是完全独立的,两者都无法访问对方的内存空间(不会遇到正常的麻烦)你去访问另一个程序的内存)。

fork()仍然被一些服务器应用程序使用,主要是在 *NIX 机器上以 root 身份运行的那些在处理用户请求之前放弃权限的应用程序。还有一些其他用例,但大多数人现在已经转向多线程。

于 2009-06-12T04:55:40.057 回答
1

在对 unix stack exchange 上的类似问题的回答中解释了 fork() 与仅使用 exec() 函数来启动新进程的基本原理。

本质上,由于 fork 复制当前进程,进程的所有各种可能选项都是默认建立的,因此程序员没有提供它们。

相比之下,在 Windows 操作系统中,程序员必须使用 CreateProcess 函数,该函数要复杂得多,并且需要填充多种结构来定义新进程的参数。

因此,总而言之,分叉(相对于执行)的原因是创建新流程的简单性。

于 2019-01-20T14:33:19.227 回答
0

Fork() 系统调用用于创建子进程。它与父进程完全相同。Fork 从父节点复制堆栈部分、堆部分、数据部分、环境变量、命令行参数。

参考: http: //man7.org/linux/man-pages/man2/fork.2.html

于 2018-02-11T05:24:26.603 回答
0

fork()函数用于通过复制调用它的现有进程来创建新进程。调用此函数的现有进程成为父进程,新创建的进程成为子进程。如前所述,孩子是父母的副本,但也有一些例外。

  • 与操作系统中运行的任何其他进程一样,子进程具有唯一的 PID。


  • 子进程的父进程 ID 与创建它的进程的 PID 相同。

  • 资源利用率和 CPU 时间计数器在子进程中重置为零。

  • child 中的未决信号集为空。

  • 子级不从其父级继承任何计时器

例子 :

    #include <unistd.h>
    #include <sys/types.h>
    #include <errno.h>
    #include <stdio.h>
    #include <sys/wait.h>
    #include <stdlib.h>

    int var_glb; /* A global variable*/

int main(void)
{
    pid_t childPID;
    int var_lcl = 0;

    childPID = fork();

    if(childPID >= 0) // fork was successful
    {
        if(childPID == 0) // child process
        {
            var_lcl++;
            var_glb++;
            printf("\n Child Process :: var_lcl = [%d], var_glb[%d]\n", var_lcl, var_glb);
        }
        else //Parent process
        {
            var_lcl = 10;
            var_glb = 20;
            printf("\n Parent process :: var_lcl = [%d], var_glb[%d]\n", var_lcl, var_glb);
        }
    }
    else // fork failed
    {
        printf("\n Fork failed, quitting!!!!!!\n");
        return 1;
    }

    return 0;
}

现在,当上面的代码编译并运行时:

$ ./fork

Parent process :: var_lcl = [10], var_glb[20]

Child Process :: var_lcl = [1], var_glb[1]
于 2018-02-11T09:11:15.330 回答
0

Fork() 被创建为一种创建另一个进程的方法,该进程与父进程共享内存状态的副本。它的工作方式之所以如此,是因为它是在以前缺乏此功能的时间片大型机系统中获得良好线程功能的最小更改。此外,程序只需极少的修改即可成为多进程,fork() 可以简单地添加到适当的位置,这相当优雅。基本上,fork() 是阻力最小的路径。

最初它实际上必须复制整个父进程的内存空间。随着虚拟内存的出现,它已经被破解并变得更高效,写时复制机制避免了实际复制任何内存的需要。

然而,现代系统现在允许创建实际线程,这些线程只是共享父进程的实际堆。对于现代多线程编程范式和更高级的语言,fork() 是否提供任何真正的好处是值得怀疑的,因为 fork() 实际上阻止了进程直接通过内存进行通信,并迫使它们使用较慢的消息传递机制。

于 2022-02-18T00:01:38.960 回答