多进程编程核心:fork()函数
C语言通过fork()系统调用创建新进程,瞬间复制当前进程(父进程),生成子进程。理解这个"分身术"就掌握了多进程的钥匙。
#include <unistd.h>
#include <stdio.h>
int main() {
printf(" 父进程启动 PID=%d\n", getpid());
pid_t child_pid = fork(); // 关键分身术!
if (child_pid == 0) {
// 子进程专属代码区
printf(" 我是子进程 PID=%d (父进程:%d)\n",
getpid(), getppid());
} else if (child_pid > 0) {
// 父进程专属代码区
printf(" 创建了子进程 PID=%d\n", child_pid);
} else {
perror("fork失败!");
return 1;
}
printf(" 父子进程都会执行这里 PID=%d\n", getpid());
return 0;
}
运行结果示例:
父进程启动 PID=1234
创建了子进程 PID=1235
我是子进程 PID=1235 (父进程:1234)
父子进程都会执行这里 PID=1234
父子进程都会执行这里 PID=1235
关键知识点图解
现象 | 解释 |
fork()返回值不同 | 父进程得到子进程PID,子进程得到0 |
并发执行 | 父子进程执行顺序由调度器决定 |
内存隔离 | 进程间不共享变量(独立内存空间) |
三大实战场景
场景1:并行计算加速(进程池)
#include <sys/wait.h>
#define PROCESS_NUM 4
int main() {
for(int i=0; i<PROCESS_NUM; i++){
if(fork() == 0) {
// 子进程计算任务
printf("进程%d计算中...\n", getpid());
sleep(1); // 模拟计算耗时
printf("进程%d完成!\n", getpid());
exit(0); // 子进程必须退出
}
}
// 父进程等待所有子进程
while(wait(NULL) > 0);
printf(" 所有子进程已完成!\n");
return 0;
}
场景2:进程树构建
int main() {
pid_t pid1 = fork(); // 第一层分支
if(pid1 == 0) {
// 子进程1创建孙子进程
pid_t pid2 = fork();
if(pid2 == 0) {
printf("孙进程 PID=%d\n", getpid());
} else {
printf("子进程 PID=%d\n", getpid());
wait(NULL);
}
} else {
printf("父进程 PID=%d\n", getpid());
wait(NULL);
}
return 0;
}
场景3:后台守护进程
int main() {
pid_t pid = fork();
if (pid > 0) {
// 父进程退出使子进程成为孤儿
printf("守护进程启动 PID=%d\n", pid);
exit(0);
}
// 子进程成为守护进程
setsid(); // 脱离终端控制
while(1) {
printf(" 守护进程工作中 PID=%d\n", getpid());
sleep(5);
}
return 0;
}
避坑指南
- 僵尸进程预防
- 父进程必须用wait()回收子进程资源
- 内存写入优化
- 使用写时复制(Copy-On-Write)技术减少内存开销
- 进程间通信(IPC)
- 常用管道/共享内存/信号量
为什么选择多进程?
多进程 | 多线程 |
系统级隔离,更安全稳定 | 共享内存,数据交换更快 |
利用多核CPU更直接 | 上下文切换开销小 |
崩溃互不影响 | 线程崩溃导致整个进程终止 |