进程创建

fork函数

使用

fork函数


创建进程使用 fork 系统函数
定义如下:

#include<sys/types.h>
#include<unistd.h>
pid_t fork(void);

pid_t 作为返回类型,被定义为 int
函数 fork 被调用一次却返回两次

  • 一次是返回到父进程
  • 另外一次是返回到新创建的子进程
    看下方这个例子
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<stdlib.h>
 
int main() {
  pid_t pid;
  int x = 1;
  pid = fork();
  if(pid == 0) {
    printf("Child:x=%d\n",++x);
    exit(0);
  } 
  printf("Parent:x=%d\n",--x);
  exit(0);
}

进程图如下

  • 父进程中,fork 函数返回子进程的进程号,所以不为 0
  • 子进程中,fork 返回 0

父子进程特点及对比

  • 调用一次,返回两次
  • 并发执行
    • 虽然子进程是父进程创建的,但是二者是独立并发执行的。也就是说,在一个计算机系统上,父进程先执行 Printf 打印,然后是子进程执行。然而在其他的系统上运行相同的程序时,很可能会得到相反的结果。所以,究竟是父进程先执行打印还是子进程先执行打印,对于不同的系统很可能是不同的
  • 相同但是独立的地址空间
    • 当父进程调用 Fork 函数创建子进程后,如果我们立刻暂停程序继续执行,也就是说,不等的函数 fork 返回到父进程和子进程之前就执行一个暂停的操作。此时,父进程与子进程的地址空间中的内容是相同的,二者具有相同的用户栈、相同的本地变量值、相同的堆、相同的全局变量值以及相同的代码。
    • 当 Fork 返回后,本地变量 x 在父进程和子进程中都是 1。然而父进程和子进程是独立的进程,他们都有自己的私有地址空间。后面父进程和子进程对变量 x 所做的任何改变都是独立的,不会反映到另一个进程的内存中。
    • 子进程虽然复制了父进程所有的地址空间的内容,但是二者具有独立的地址空间。
    • 父进程和子进程都把他们的执行结果输出到了显示屏幕上,原因是子进程继承了父进程所有打开的文件。这里的文件是指:父进程调用 Fork 函数时,标准输出文件是打开的,并指向屏幕,子进程继承了这个文件,所以它的输出也是指向屏幕的
Link to original

多次调用fork

#include <stdio.h>
#include <unistd.h> // Include the necessary header file
#include <stdlib.h> // Include the necessary header file
 
int main(){
  fork();
  fork();
  printf("hello\n");
  exit(0);
}
//
//hello
//hello
//hello
//hello 

第一个 fork 开一个子进程,+1

父进程和子进程有相同的内容,所以都接下来要执行第二个 fork,+2
这里践行的就是相同但是独立的地址空间, 遇到 fork 就重复复制一遍接下来的操作

加上原本父进程自己,就是 1+1+2,总共输出 4 次

嵌套调用fork

父进程嵌套调用fork


子进程嵌套调用fork