C编程语言中的Fork()

512 阅读4分钟

C编程语言中的Fork()

如果你使用的是Windows,你可能已经在你的任务管理器中看到了很多的进程。如果你使用的是Linux,则在你的资源监视器中。你有没有停下来想一想,这些进程是如何产生的?

在本教程中,我们将讨论fork() 函数,然后用C语言编程实现一些例子。

前提条件

要跟上本教程,你应该具备以下条件。

  • 对C编程语言有良好的理解。
  • 对类Unix操作系统有一定的了解。

什么是进程?

进程是一个计算机程序的实例,由一个或多个线程执行。它包含程序代码和它的活动。根据操作系统(OS)的不同,一个进程可能是由多个执行线程组成的,它们同时执行指令。

什么是Fork()?

在计算机领域。 **fork()**是类Unix操作系统上创建进程的主要方法。这个函数从原始进程中创建一个新的副本,称为进程,这个子进程被称为进程。当父进程关闭或因某种原因崩溃时,它也会杀死子进程。

让我们从一个进程的生命周期开始。

Process life-cycle

操作系统正在为每个进程使用一个唯一的ID来跟踪所有进程。为此,fork() ,不接受任何参数,并返回一个int值,如下。

  • 零:如果它是子进程(创建的进程)。
  • 正值:如果它是父进程。
  • 负值:如果发生错误。

注意:下面的代码只在基于Linux和UNIX的操作系统中运行。如果你运行的是Windows,那么我建议你使用[Cygwin]。

让我们跳到实践部分,我们将创建从简单水平到高级水平的例子。

Hello world!

#include <stdio.h> 
#include <sys/types.h> 
#include <unistd.h> 
int main() 
{ 
    /* fork a process */
    fork(); 
    /* the child and parent will execute every line of code after the fork (each separately)*/
    printf("Hello world!\n"); 
    return 0; 
} 

输出将是。

Hello world!
Hello world!

其中一个输出来自父进程,另一个来自子进程。

Simple fork

简单地说,我们可以知道结果是n的2次方,其中n是fork()系统调用的数量。

比如说。

#include <stdio.h> 
#include <sys/types.h> 
#include <unistd.h> 
#include <stdlib.h>
int main() 
{ 
    fork(); 
    fork(); 
    fork(); 
    printf("Hello world!\n");
    return 0; 
} 

其结果是:

Hello world!
Hello world!
Hello world!
Hello world!
Hello world!
Hello world!
Hello world!
Hello world!

另一个例子是:

int main() {
  if(fork() == 0)
    if(fork())
      printf("Hello world!!\n");
  exit(0);
}

我画了一个简单的草图来帮助你理解这个想法。

Fork

在第一个if 条件中,发生了一个fork,它正在检查它是否是子进程,然后继续执行它的代码。否则(如果是父进程),它将不会通过该if 。然后,在第二个if ,它将只接受持有正ID的父进程。

结果,它将只打印一个 "Hello world!"。

现在试着执行下面的代码,并将你的结果与我们的比较。

int doWork(){
	fork();
	fork();
	printf("Hello world!\n");
}
int main() {
	doWork();
	printf("Hello world!\n");
	exit(0);
}

其结果将是。

Hello world!
Hello world!
Hello world!
Hello world!
Hello world!
Hello world!
Hello world!
Hello world!

Fork explaination

因为当被分叉在dowork() 内的进程打印出Hello World! ,它将在函数调用后继续主代码,并打印出Hello World! ,然后退出。

高级例子

当一个进程创建一个新的进程时,那么执行退出有两种可能。

  • 父进程继续与子进程同时执行。
  • 父进程等待,直到它的一些或所有的子进程都终止了。
#include <sys/types.h>
#include <sys/types.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
int main(int argc, char *argv[]) {

   /* fork a child process */
   pid_t pid = fork();

   if (pid < 0) { /* error occurred */
   	fprintf(stderr, "Fork Failed");
   	return 1;
   }

   else if (pid == 0) { /* child process */
   	printf("I'm the child \n"); /* you can execute some commands here */
   }

   else { /* parent process */
   	/* parent will wait for the child to complete */
   	  wait(NULL);
   	/* When the child is ended, then the parent will continue to execute its code */
   	  printf("Child Complete \n");
   }
}

等待调用系统wait(NULL) ,将使父进程等待,直到子进程执行完其所有的命令。

其结果将是。

I'm the child 
Child Complete 

另一个例子。

#include <sys/types.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
   printf("I am: %d\n", (int) getpid());

   pid_t pid = fork();
   printf("fork returned: %d\n", (int) pid);

   if (pid < 0) { /* error occurred */
   	perror("Fork failed");
   }
   if (pid == 0) { /* child process */
   	printf("I am the child with pid %d\n", (int) getpid());
               printf("Child process is exiting\n");
               exit(0);
       }
   /* parent process */
   printf("I am the parent waiting for the child process to end\n");
       wait(NULL);
       printf("parent process is exiting\n");
       return(0);
} 

结果将是这样的。

I am: 2337
fork returned: 2338
I am the parent waiting for the child process to end
fork returned: 0
I am the child with pid 2338
Child process is exiting
parent process is exiting

今天就到此为止!🥳

总结

我们已经了解了fork()的作用,以及如何在C语言中用独特的例子实现它。如果你对操作系统的抽象感兴趣,以及它是如何工作的,那么我建议你开始学习管道和信号灯的知识。