进程创建和终止


到目前为止我们知道,每当我们执行一个程序时,就会创建一个进程,并在执行完成后终止。如果我们需要在程序中创建一个进程并且可能需要为其安排不同的任务,该怎么办?这能实现吗?是的,显然是通过流程创建。当然,作业完成后会自动终止,或者您可以根据需要终止它。

进程的创建是通过fork()系统调用来实现的。新创建的进程称为子进程,启动它的进程(或开始执行时的进程)称为父进程。在 fork() 系统调用之后,现在我们有两个进程 - 父进程和子进程。如何区分它们?很简单,就是通过它们的返回值。

系统调用

创建子进程后,让我们看看 fork() 系统调用的详细信息。

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

pid_t fork(void);

创建子进程。在这个调用之后,有两个进程,现有的称为父进程,新创建的称为子进程。

fork() 系统调用返回三个值之一 -

  • 负值表示错误,即创建子进程失败。

  • 为子进程返回零。

  • 为父进程返回正值。该值是新创建的子进程的进程ID。

让我们考虑一个简单的程序。

File name: basicfork.c
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

int main() {
   fork();
   printf("Called fork() system call\n");
   return 0;
}

执行步骤

汇编

gcc basicfork.c -o basicfork

执行/输出

Called fork() system call
Called fork() system call

注意- 通常在 fork() 调用之后,子进程和父进程将执行不同的任务。如果需要运行相同的任务,那么对于每个 fork() 调用,它将运行 2 次方 n 次,其中n是调用 fork() 的次数。

在上述情况下,fork() 被调用一次,因此输出被打印两次(2 次方 1)。如果调用 fork() 3 次,那么输出将被打印 8 次(2 的 3 次方)。如果调用 5 次,则打印 32 次,依此类推。

看到 fork() 创建子进程后,是时候查看父进程和子进程的详细信息了。

文件名:pids_after_fork.c

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

int main() {
   pid_t pid, mypid, myppid;
   pid = getpid();
   printf("Before fork: Process id is %d\n", pid);
   pid = fork();

   if (pid < 0) {
      perror("fork() failure\n");
      return 1;
   }

   // Child process
   if (pid == 0) {
      printf("This is child process\n");
      mypid = getpid();
      myppid = getppid();
      printf("Process id is %d and PPID is %d\n", mypid, myppid);
   } else { // Parent process 
      sleep(2);
      printf("This is parent process\n");
      mypid = getpid();
      myppid = getppid();
      printf("Process id is %d and PPID is %d\n", mypid, myppid);
      printf("Newly created process id or child pid is %d\n", pid);
   }
   return 0;
}

编译和执行步骤

Before fork: Process id is 166629
This is child process
Process id is 166630 and PPID is 166629
Before fork: Process id is 166629
This is parent process
Process id is 166629 and PPID is 166628
Newly created process id or child pid is 166630

进程可以通过两种方式终止 -

  • 异常情况下,发生在某些信号传送时,例如终止信号。

  • 通常,使用 _exit() 系统调用(或 _Exit() 系统调用)或 exit() 库函数。

_exit() 和 exit() 之间的区别主要在于清理活动。exit ()在将控制权返回给内核之前会进行一些清理,而_exit()(或 _Exit())会立即将控制权返回给内核。

考虑以下带有 exit() 的示例程序。

文件名:atexit_sample.c

#include <stdio.h>
#include <stdlib.h>

void exitfunc() {
   printf("Called cleanup function - exitfunc()\n");
   return;
}

int main() {
   atexit(exitfunc);
   printf("Hello, World!\n");
   exit (0);
}

编译和执行步骤

Hello, World!
Called cleanup function - exitfunc()

考虑以下使用 _exit() 的示例程序。

文件名:at_exit_sample.c

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

void exitfunc() {
   printf("Called cleanup function - exitfunc()\n");
   return;
}

int main() {
   atexit(exitfunc);
   printf("Hello, World!\n");
   _exit (0);
}

编译和执行步骤

Hello, World!