1、进程创建
1.1 fork
通过 fork 系统调用,创建子进程。
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main() {
int ret = fork();
printf("hello proc : %d!, ret: %d\n", getpid(), ret);
sleep(1);
return 0;
}
- 创建成功,两个返回值,对父进程返回子进程的 PID,对子进程返回 0。因为父:子 = 1:N,父进程需要区分子进程,而子进程能通过 PPID 找到父进程。所以可以 if,让父子进程执行不同的语句。创建失败,返回 -1。
- fork() 创建子进程后,父子进程都从 fork() 返回处继续执行。注意:子进程不会执行 fork() 之前的代码。
- 当父子进程尝试修改数据,会发生写时拷贝 (减少创建子进程的时间,减少内存浪费),重新拷贝一份数据。所以父子进程独立运行。
1.2 fork 的常规用法
- 父进程创建子进程后,父子进程各自执行不同的逻辑。
- 子进程通过 exec 系列函数完全替换为另一个程序。
1.3 fork 失败的原因
- 进程总数超过内核限制。
- 用户进程数超过配额。
2、进程退出
2.1 基本概念
进程退出,释放代码和数据,没有释放 PCB 对象。
2.2 进程退出场景
- 代码运行完毕,结果正确。
- 代码运行完毕,结果不正确。
- 代码异常终止 (一般是收到了信号)。
2.3 退出码
- 如果是异常终止,退出码无意义 (代码都没执行完)。
- 不是异常终止,0 为结果正确,非 0 为结果不正确 (不同的值,表示不同的原因)。
注意:
- $?,显示最近一个进程退出时的退出码。
- errno,当系统调用或库函数发生错误时,errno 会被设置为对应的错误码。需包含<errno.h>。
- strerror(),根据错误码,显示错误信息。
2.4 进程常见退出方式
- main 函数的 return 退出码,(其他函数的 return,只表示函数调用完成),表示进程退出。
- _exit(退出码)。是系统调用,用于进程退出。
- exit(退出码)。是 C 标准库函数 (封装了 exit()),先刷新 I/O 缓冲区等,再进程退出。
3、进程等待
3.1 进程等待的必要性
- 子进程退出,父进程需要获取子进程退出前的信息 (即子进程 PCB 对象里面的信息,其指向的代码和数据已被释放,可选),并释放子进程的 PCB 对象 (必要),如果父进程没有"回收"子进程,那么子进程被称为"僵尸进程",其 PCB 对象将会一直存在,造成内存泄漏。


