基础理论
在 Linux 中,fork() 函数是一个非常重要的系统调用,它是创建新进程的主要方式。它会创建一个新的进程,该进程与父进程几乎完全相同,包括代码段、数据段和堆栈等。但是,新的进程有自己的进程 ID(PID),并拥有自己的地址空间和资源。
函数原型与实例
fork() 函数的调用格式如下:
#include<unistd.h>
pid_t fork(void);
与fork()函数常常一起出现的两个函数
pid_t getpid() 获取当前进程id
pid_t getppid() 获取当前进程的父进程id
该函数(fork)没有参数,返回一个 PID,表示当前进程的子进程的 PID。如果 fork() 函数失败,返回 -1。
在调用 fork() 函数后,会创建一个新的进程,它是原始进程的副本。子进程的地址空间与父进程相同,但是子进程的堆栈和程序计数器等寄存器的值是不同的。子进程从 fork() 函数返回 0,父进程从 fork() 函数返回子进程的 PID。
总的来说,如果成功的话,从宏观上看,返回了两次,一次是父进程返回子进程PID,一次是子进程返回0;
父子进程代码段是完全一样的,但是子进程会从fork()以后开始运行,也就是说,在fork前的代码子进程虽然有,但是不会运行。
下面是一个简单的例子,展示了如何使用 fork() 函数创建新进程。
#include<stdio.h>#include<unistd.h>intmain() {
pid_t pid;
printf("before fork()\n");//父进程运行,子进程不运行
pid = fork();
if (pid == -1) {
printf("Error: fork failed.\n");
} elseif (pid == 0) {
printf("I'm the child process, my PID is %d.\n", getpid());
} else {
printf("I'm the parent process, my PID is %d and my child's PID is %d.\n", getpid(), pid);
}
return0;
}
在这个例子中,我们首先声明了一个 pid 变量,然后调用 fork() 函数。如果 fork() 函数返回 -1,表示创建进程失败,我们将打印一条错误消息。否则,我们检查 pid 的值。如果 pid 是 0,表示当前进程是子进程。如果 pid 大于 0,表示当前进程是父进程,并且 pid 是子进程的 PID。我们可以根据 pid 的值,打印不同的消息。
具体执行结果:
执行后我们会发现,大部分情况下,子进程PID会等于父进程PID+1.
总结一下,fork() 函数是 Linux 中创建新进程的一种常用方法。通过调用该函数,可以在当前进程的基础上创建一个新进程,使得父进程和子进程可以独立运行。如果你想要深入了解 Linux 进程的创建和管理,可以进一步学习 Linux 进程的相关知识
拓展:创建n个进程
运行结果
我们会发现进程是异步执行的,为什么呢?因为fork()在操作系统中相当于几乎同步创建了子进程,所以大家抢占CPU,谁先拿到谁先执行,如果想有顺序,可以sleep一下
父子进程关系
父子进程相同:
刚fork后。 data段、text段、堆、栈、环境变量、全局变量、宿主目录位置、进程工作目录位置、信号处理方式
父子进程不同:
进程id、返回值、各自的父进程、进程创建时间、闹钟、未决信号集
父子进程共享:
1.文件描述符 2. mmap映射区。
读时共享、写时复制。———————— 全局变量。(意思就是对于全局变量,读取的话是共享的,要写的话,会先复制一份到本地,再写,也就是说写的时候不共享)
总结
在Linux中,fork函数是多进程编程中最常用的函数之一,