本文是小编巩固自身而作如有错误欢迎指出目录一、fork1.fork的原理2.fork的示例3.fork的执行过程二、进程状态1.操作系统的进程状态2.linux中的进程状态1. R — Running / Runnable运行 / 就绪2. S — Interruptible Sleep可中断睡眠3. D — Uninterruptible Sleep不可中断睡眠4. T — Stopped暂停5. Z — Zombie僵尸进程​编辑状态示例僵尸进程僵尸进程的关键特征孤儿进程一、fork在编程领域fork是一个核心且高频出现的概念最常见于Unix/Linux 系统编程.1.fork的原理fork()是 Unix/Linux 系统提供的一个系统调用函数作用是复制当前进程创建一个全新的子进程。调用fork()后操作系统会为子进程复制父进程的内存空间、代码、数据、文件描述符等资源。一次调用两次返回父进程中fork()返回子进程的 PID进程 ID子进程中fork()返回0若创建失败如资源不足返回-1。2.fork的示例#include stdio.h #include unistd.h #include sys/wait.h int main() { // 调用fork创建子进程 pid_t pid fork(); if (pid 0) { // 失败场景 perror(fork failed); return 1; } else if (pid 0) { // 子进程执行逻辑 printf(我是子进程PID%d父进程PID%d\n, getpid(), getppid()); // 子进程执行完退出 return 0; } else { // 父进程执行逻辑 printf(我是父进程PID%d创建的子进程PID%d\n, getpid(), pid); // 等待子进程结束避免僵尸进程 wait(NULL); printf(子进程已结束\n); } return 0; }我们可以看到子进程的ppid和父进程的pid是一样的。说明fork的作用是在原有的进程基础上创建一个子进程而不是在总的进程下创建一个子进程。而我们自己创建的进程的父进程又是什么呢很简单在所有我们不曾用fork创建新的子进程的情况下原本创建的进程的父进程都是bash。而bash也就是我们用fork创建进程的爷爷进程。3.fork的执行过程我们在看上述代码的时候不免有一个疑问为什么一个fork函数可以返回两个值不是fork()函数本身返回了两个值而是操作系统让fork()调用在父进程和子进程中分别返回了不同的值。fork()的本质是「复制当前进程」整个过程可以分成 3 步你在父进程中调用fork()操作系统接收到这个请求操作系统会完整复制父进程的所有资源代码、内存、文件描述符等创建一个全新的子进程此时系统中存在两个独立的进程父 子它们会从fork()调用的位置继续往下执行代码。为什么需要「不同的返回值」父进程和子进程执行的是同一份代码如果fork()返回的值相同这两个进程就会执行完全一样的逻辑 —— 但实际开发中我们往往需要让父子进程做不同的事比如父进程监听端口子进程处理请求。因此操作系统设计了这个「差异化返回」的机制目的是让程序员能通过返回值区分「当前代码跑在父进程还是子进程」从而编写不同的逻辑。二、进程状态1.操作系统的进程状态在操作系统中一般我们认为有三种状态运行Running正在用 CPU。就绪Ready除了 CPU别的资源都有等 CPU。阻塞Blocked/Waiting等事件I/O、信号就算 CPU 空着也不能运行。而在特殊情况下我们还有一种状态就是挂起挂起 进程被调到外存磁盘不在内存里了原因内存不够、进程太久不用、管理员手动挂起。挂起分两种1就绪挂起Ready Suspend本来是就绪态被换到磁盘只要调回内存 → 就变成就绪2阻塞挂起Blocked Suspend本来是阻塞态被换到磁盘等的事件来了 → 变成就绪挂起2.linux中的进程状态//linux源代码如下 static const char * const task_state_array[] { R (running) //运行 S (sleeping) //睡眠 D (disk sleep) //深度睡眠 T (stopped) //停止 t (tracing stop)//追踪停止 X (dead) //死亡 Z (zombie), //僵尸 };1. R — Running / Runnable运行 / 就绪正在 CPU 上运行or在就绪队列排队等着运行只有 R 状态的进程才会被 CPU 调度2. S — Interruptible Sleep可中断睡眠最常见状态进程在等待事件网络、键盘、信号、sleep、锁等可以被信号唤醒、杀死3. D — Uninterruptible Sleep不可中断睡眠一般在等待 I/O磁盘读写不能被信号打断不能被杀掉大量 D 状态 磁盘 I/O 卡住、系统很卡4. T — Stopped暂停被暂停执行比如CtrlZ、被调试器gdb暂停5. Z — Zombie僵尸进程进程已经结束、退出但父进程没有调用wait()回收它的 PCB 信息僵尸进程占进程号不占内存太多会炸系统状态示例比如我们这里写一个简单的死循环#include stdio.h int main() { while(1) {} return 0; }我们可以看到这样就是运行状态了。但如果我们在循环中加入一个printf呢我们可以看到在这种情况下我们的进程状态居然变成了S原因就在于终端输出的阻塞特性printf向终端pts/0输出内容时会触发终端 I/O 缓冲区操作进程需要等待终端内核缓冲区就绪这个等待过程会让进程短暂进入S可中断睡眠状态虽然没有sleep但printf与终端的交互是频繁的 “短暂运行R→ 短暂睡眠S” 循环ps采样时大概率捕捉到的是S状态而非瞬时的R状态。僵尸进程#include stdio.h #include stdlib.h #include unistd.h int main() { // 1. 创建子进程fork() 是创建进程的系统调用 // 返回值 pid_t 是进程ID类型不同返回值区分父子进程 pid_t id fork(); // 2. 错误处理fork() 失败时返回 -1 if(id 0){ // perror 打印错误原因如内存不足、进程数超限等 perror(fork); // 非0返回表示程序异常退出 return 1; } // 3. 父进程逻辑fork() 给父进程返回子进程的PID大于0 else if(id 0){ // 打印父进程PIDgetpid() 获取当前进程的ID printf(parent[%d] is sleeping...\n, getpid()); // 父进程睡眠30秒这段时间父进程不执行任何操作也不回收子进程 // 核心父进程睡眠期间子进程退出后会变成僵尸进程 sleep(30); } // 4. 子进程逻辑fork() 给子进程返回 0 else{ // 打印子进程PID printf(child[%d] is begin Z...\n, getpid()); // 子进程睡眠5秒模拟子进程的业务执行时间 sleep(5); // 子进程正常退出EXIT_SUCCESS 等价于 0表示退出状态正常 // 子进程退出后父进程还在sleep(30)未调用wait()回收资源 → 子进程变僵尸 exit(EXIT_SUCCESS); } // 父进程睡眠30秒后程序正常结束此时系统会自动回收子进程 return 0; }僵尸进程Zombie 子进程已退出运行完毕 / 异常终止 父进程未调用wait()/waitpid()回收子进程的「退出状态和资源」。僵尸进程的关键特征✅ 占用 PID但不占用 CPU / 内存只占极少量内核空间存储 PCB✅ 无法被kill -9杀死因为进程已经退出信号无接收者✅ 只有两种清理方式父进程调用wait()/waitpid()主动回收父进程退出由「init 进程PID1」接管并回收。孤儿进程#include stdio.h #include stdlib.h #include unistd.h int main() { pid_t id fork(); // 创建父子进程 if(id 0){ perror(fork); return 1; } // 父进程逻辑先退出制造孤儿进程 else if(id 0){ printf(父进程[PID: %d] 即将退出子进程[PID: %d] 变成孤儿\n, getpid(), id); sleep(1); // 留时间打印信息 exit(EXIT_SUCCESS); // 父进程主动退出 } // 子进程逻辑持续运行成为孤儿 else{ printf(子进程[PID: %d] 开始运行父进程退出后我的新父进程会变成 1\n, getpid()); // 子进程持续运行10秒方便你查看状态 for(int i 1; i 10; i){ // getppid()获取当前进程的父进程PID printf(子进程[PID: %d] - 第%d秒父进程PID: %d\n, getpid(), i, getppid()); sleep(1); } printf(子进程[PID: %d] 退出\n, getpid()); return 0; } }孤儿进程 父进程先退出 子进程还在运行→ 此时子进程会被系统的「1 号 init 进程或 systemd」接管成为 init 进程的 “养子”。init 进程的作用系统启动后第一个进程PID1专门负责收养孤儿进程子进程退出后会被 init 进程自动回收不会变成僵尸孤儿进程无危害它就是一个普通的运行中进程只是 “没了亲爹”由系统接管资源使用和普通进程完全一致与僵尸进程的本质区别僵尸进程是「子死父不管」孤儿进程是「父死子还在」。本次分享就到这里结束了后续会继续更新感谢阅读