📚 一篇文章看懂 Linux 进程的创建、中止、等待与替换
全程大白话 + 易懂代码示例
一、什么是 Linux 进程?
进程(Process)简单来说就是 “正在运行的程序”。比如你用浏览器打开这个网页时,浏览器就是一个进程。
- 进程就像程序的活动分身,每个进程有独立的代码、内存空间、运行状态(PID唯一编号)。
二、进程的创建:fork()
想要创建新进程?用 fork()
:像细胞分裂一样,复制自己!
代码示例:
#include <unistd.h>
#include <stdio.h>
int main() {
pid_t pid = fork(); // 分裂出一个子进程
if (pid == 0) {
// 子进程执行的代码
printf("我是子进程,我的PID是%d,爸爸是%d\n", getpid(), getppid());
} else if (pid > 0) {
// 父进程执行的代码
printf("我是父进程,我的PID是%d,刚创建的子进程PID是%d\n", getpid(), pid);
} else {
perror("fork 失败啦!");
}
return 0;
}
关键点:
-
fork() 会复制父进程的全部内容(代码、变量、打开的文件等),但在现代系统采用“写时复制(Copy-On-Write)”优化。
-
fork() 返回值不同
:
- 父进程 返回子进程的PID(用来追踪和管理孩子)。
- 子进程 返回0(表示自己是儿子)。
-
父子进程 代码完全相同,但要靠返回值区分身份(执行不同逻辑)。
三、进程的中止:exit()
与 _exit()
想让进程结束?两个选择:💣
exit(int status)
:
完美退场!会做 清理工作(刷新缓冲区、关闭文件)。_exit(int status)
:
火速下线!直接终止进程,不做任何清理。
代码示例:
#include <stdlib.h> // 用于exit
#include <unistd.h> // 用于_exit
void child_process() {
printf("子进程:先打印这句话……\n");
exit(0); // 输出正常显示
// _exit(0); // 可能不输出上一句(如果缓冲区没刷新)
}
四、进程的等待:wait()
与 waitpid()
当父进程需要 等待子进程结束(有些像等孩子放学),否则会产生“僵尸进程”(已结束但未被回收,占用系统资源)。
wait()
示例:
#include <sys/wait.h>
#include <unistd.h>
int main() {
pid_t pid = fork();
if (pid == 0) {
printf("子进程工作中...\n");
sleep(2);
exit(10);
} else {
int status;
wait(&status); // 等待子进程结束
printf("子进程退出了,状态码是 %d\n", WEXITSTATUS(status));
}
return 0;
}
关键函数:
wait(int\* status)
:
阻塞父进程,直到 任意一个子进程退出。waitpid(pid_t pid, int\* status, int options)
:
更灵活!可以指定等待某个子进程(pid
参数),或设置非阻塞模式(options=WNOHANG
)。
五、进程的替换:exec 家族
想让子进程 执行一个全新的程序?用 exec
系列函数!
exec
会 替换当前进程的代码段,但PID不变(还是原来那个“身体”,但灵魂已变)。
常用函数:
函数名 | 特点 |
---|---|
execl("程序路径", args..., NULL) | 参数用逗号分隔,参数列表以NULL 结尾 |
execv("程序路径", args数组) | 参数通过数组传递 |
execvp | 自动在PATH环境变量中查找程序 |
代码示例:
#include <unistd.h>
int main() {
pid_t pid = fork();
if (pid == 0) {
// 子进程替换为执行 "ls -l" 命令
execlp("/bin/ls", "ls", "-l", NULL);
// 如果execl执行成功,后续代码不会执行
perror("替换失败!");
exit(1);
} else {
wait(NULL); // 等待子进程完成
}
return 0;
}
🚨 注意事项 (避坑指南!)
- fork 后务必判断返回值,父子进程逻辑需分开写。
- 避免僵尸进程:父进程必须使用
wait/waitpid
回收子进程。 - exec 替换后原进程代码无效,不要在exec后写其他代码。
- 多进程共享文件描述符:fork会把父进程打开的文件复制给子进程,注意关闭不需要的文件。
🌟 总结
- 创建:
fork()
复制出子进程,父子身份由返回值区分。 - 中止:正常退出用
exit
,紧急退出用_exit
。 - 等待:父进程需
wait
回收子进程,防止僵尸。 - 替换:
exec
让进程“变身”,运行新程序。
试着用上面的代码跑起来,并观察输出,理解每个步骤发生了什么!