1.概念
在操作系统中,当一个进程(称为父进程)创建另一个新进程(称为子进程)时,父子进程之间建立了一种特殊的关系。
2.创建父子进程的方法
2.1 fork()函数详解:
fork 是一个系统调用(也是库函数),用于创建一个新的进程。它会复制调用进程(父进程)的内存和上下文,包括代码段、数据段、堆栈等,然后将这个副本分配给新创建的进程(子进程)。
fork 系统调用的原型通常定义在 <unistd.h> 头文件中。
我们要创建一个进程,那一定涉及到访问操作系统的内部数据,肯定也需要使用系统调用
#include <unistd.h>
pid_t fork(void);
// pid_t 是一个类型,用于表示进程 ID,fork 函数返回的是一个 pid_t 类型的值。
fork() 调用成功时,在父进程中返回子进程的 PID,而在子进程中返回 0。
如果 fork 调用失败,则返回一个负数,表示错误。
在 fork 调用后,父子进程都会继续执行 fork 调用之后的指令,但是它们会在不同的地址空间中运行,即它们各自拥有独立的内存空间。这意味着,父进程和子进程之间的数据是相互独立的,任何一个进程对内存的修改都不会影响到另一个进程。
父子进程代码共享的原理是采用写时拷贝(copy-on-write)。
在 fork 调用后,操作系统并不会立即复制父进程的内存给子进程,而是让父子进程共享同一段内存空间。只有当其中一个进程试图修改共享的内存时,操作系统才会复制该内存页,确保修改不会影响到其他进程。这样做可以节省内存,并提高效率。
2.2 fork()函数疑问
怎么理解fork()是系统调用也是库函数
系统调用(System Call):系统调用是操作系统提供给用户空间程序访问内核功能的一种机制。fork 系统调用是由操作系统内核实现的,用于创建一个新的进程。当用户程序调用 fork 时,实际上是请求操作系统内核为其创建一个新的进程,这需要通过系统调用来完成。
库函数:库函数是一组在编程时可调用的函数,这些函数通常包含在标准库或其他库中。在 Unix-like 系统中,C 标准库中包含了对 fork 函数的封装,这意味着用户程序可以通过调用 fork 库函数来发起对 fork 系统调用的请求,而不必直接调用系统调用。
从用户程序的角度来看,fork 可以被视为一个库函数,因为它是通过调用库函数来实现的。
从操作系统内核的角度来看,fork 是一个系统调用,因为它需要通过内核来创建新的进程。
fork 函数在调用后会返回两次,这是因为它是一个复制当前进程的系统调用。下面是对这两个返回值的解释:
1.给父进程返回子进程的 PID:在父进程中,fork 返回新创建子进程的进程 ID(PID),这个 PID 是子进程的标识符,父进程通过这个 PID 可以识别并操作子进程。就像一个父亲有很多孩子,pid和姓名一样,只是为了区分它们。
2.给子进程返回 0:在子进程中,fork 也会返回一个值,但是返回的是 0,子进程是通过复制父进程的地址空间而创建的,因此子进程从父进程继承了大部分的内存布局和数据。为了区分父进程和子进程,fork 在子进程中返回 0,表示这是子进程执行的代码路径。
3. fork函数为什么会返回两次:fork 函数在调用后会创建一个新的子进程(在return之前就已经创建好子进程了),新的子进程拥有父进程的副本(也包括return)。因此,fork 在执行时会返回两次:一次在父进程中(返回子进程的 PID),另一次在子进程中(返回 0)。这样做是为了让父进程和子进程可以根据返回值来执行不同的代码路径。
2.3 getpid() 和 getppid()
在Unix/Linux系统中,可以使用 getpid() 系统调用来获取当前进程的PID,使用 getppid() 系统调用来获取当前进程的父进程的PID。
以下是这两个系统调用的简要说明:
getpid():该系统调用返回调用进程的PID,即当前进程的PID。
getppid():该系统调用返回调用进程的父进程的PID,即当前进程的父进程的PID。
2.4代码演示:
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
int ret = fork();
if(ret < 0){
perror("fork");
return 1;
}
else if(ret == 0){ //child
printf("I am child : %d!, ret: %d\n", getpid(), ret);
}
else{ //father
printf("I am father : %d!, ret: %d\n", getpid(), ret);
}
sleep(1);
return 0;
}
执行结果:
注意:
任何平台,进程在运行时,都具有独立性!!!