Linux 学习笔记(十五):进程的魔法 fork 与 僵尸孤儿
发布时间:2026/10/23 5:05:30 锦皓数字建站
:进程的魔法 fork 与 僵尸孤儿`)
一、 fork 函数的奇迹在 Linux 中通过fork()系统调用创建子进程。它最神奇的地方在于“一次调用两次返回”在父进程中返回子进程的 PID0。在子进程中返回 0。失败返回 0。因此在代码中我们通常用if(ret 0) { // 子进程 } else { // 父进程 }来分流。二、 虚拟地址与写时拷贝在实战中我发现一个诡异现象父子进程打印同一个全局变量的地址是一模一样的但值却不同。这说明我们在 C 语言中打印的地址全都是虚拟地址。操作系统通过页表将虚拟地址映射到物理地址。父子进程初始共享物理内存当子进程尝试修改数据时系统会触发“写时拷贝”Copy-on-Write机制为子进程分配新的物理内存从而保证了进程的独立性。三、 僵尸进程Zombie成因子进程退出但父进程未使用wait()或waitpid()读取其退出状态导致子进程的 PCB 一直保留在内核中状态变为Z。危害PCB 会占用内存空间大量僵尸进程会导致内存泄漏甚至拖垮系统。解决父进程应调用wait()回收或者杀死父进程让僵尸进程变成孤儿被 init 接管。四、 孤儿进程Orphan成因父进程先退出了而子进程还在运行。结局孤儿进程会被系统的1号进程init/systemd领养由 1号进程负责它们的回收因此孤儿进程没有危害。自我实战操作实战 1体验fork的双返回值与数据私有cd ~/linux_study/proc_test vim fork_test.c(输入以下代码保存退出)#include stdio.h #include unistd.h #include stdlib.h int g_val 0; // 全局变量 int main() { pid_t id fork(); // 创建子进程 if (id 0) { perror(fork error); return 1; } else if (id 0) { // 只有子进程能进这里 g_val 100; // 子进程修改全局变量 printf(我是子进程PID: %d, 我爸爸是: %d, g_val: %d, 地址: %p\n, getpid(), getppid(), g_val, g_val); } else { // 只有父进程能进这里 sleep(1); // 等子进程先跑完 printf(我是父进程PID: %d, g_val: %d, 地址: %p\n, getpid(), g_val, g_val); } return 0; }编译运行gcc fork_test.c -o fork_test ./fork_test同一个变量g_val父子进程打印出来的内存地址一模一样比如都是0x...但值却不一样父进程是 0子进程是 100。思考这说明什么说明%p打印出来的根本不是你电脑真实的物理内存地址而是“虚拟地址”实战 2亲手制造一个“僵尸进程”vim zombie_test.c(输入以下代码保存退出)#include stdio.h #include unistd.h #include stdlib.h int main() { pid_t id fork(); if (id 0) { // 子进程只活 3 秒 printf(我是子进程[%d]我要死了...\n, getpid()); sleep(3); exit(0); } else { // 父进程活 30 秒且一直不调用 wait() 回收子进程 printf(我是父进程[%d]我假装没看见子进程死掉...\n, getpid()); sleep(30); } return 0; }操作步骤编译运行gcc zombie_test.c -o zombie_test ./zombie_test马上新开一个终端敲下面这个命令监控while true; do ps aux | grep zombie_test | grep -v grep; sleep 1; echo ---; done你会看到3秒钟后子进程的状态会变成Z或者Z这就叫僵尸进程它会一直在那里挂着直到 30 秒后父进程结束它才被系统接管回收。
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。