用过Linux的小伙伴,大概率都敲过ps -ef 这个命令——屏幕上密密麻麻的一行行内容,就是Linux系统里正在“打工”的进程。
很多人对进程的理解,只停留在“程序运行的实例”这个干巴巴的定义上,一听到“父进程、子进程、0号进程、1号进程”就头大,更别说搞懂进程怎么“出生”、怎么“干活”、怎么“死亡”了。
今天就换个接地气的姿势,把Linux进程家族的“爱恨情仇”讲明白:把整个Linux系统比作一家“超级公司”,进程就是公司里的“员工”,从“祖宗级老板”到“底层打工仔”,每个进程都有自己的职责和归宿,风趣不枯燥,专业不打折!
一、先搞懂:进程到底是什么?
先给个绝对严谨的定义,记不住也没关系,后面类比会帮你吃透:进程是操作系统进行资源分配和调度的基本单位,是程序运行的实例。
简单说,程序是“死”的(比如你写的shell脚本、安装的nginx),而进程是“活”的——当你执行 ./test.sh 或者启动nginx时,操作系统就会给这个程序分配内存、CPU等资源,让它跑起来,这就变成了进程。
类比一下:程序就是“员工手册”,进程就是“照着手册干活的员工”——一本手册可以对应多个员工(一个程序可以启动多个进程,比如nginx多进程),每个员工都有自己的工作状态、专属资源。
二、Linux进程家族:谁是“祖宗”?谁是“大管家”?
Linux系统里的所有进程,都有一个共同的“祖宗”,往下代代衍生,形成一个“进程树”(就像公司的组织架构图)。其中最核心的几个角色,必须记牢。
1. 0号进程(idle/swapper):系统的“开山鼻祖”,隐形的幕后大佬
0号进程是Linux系统启动时,第一个被创建的进程,没有“爹”(父进程),是所有进程的“老祖宗”。
它的职责特别“底层”,不直接干“业务活”,主要负责进程调度——当系统没有其他任务可运行时,0号进程就会出来“idle”(空闲等待)。在Linux内核中,它也被称为 swapper 进程,负责一些初始化工作。
类比一下:0号进程就像公司的“奠基创始人”,搭建完基础设施后就退居幕后,不参与日常管理,只在没有员工干活的时候出来转转。
重点提醒:0号进程是内核态进程,由内核直接创建。在常规使用 ps 命令时确实看不到它,因为它不体现在用户态的进程列表里,但它一直默默在后台支撑着整个系统。
2. 1号进程(init/systemd):系统的“大管家”,所有孤儿进程的“直接爹”
0号进程启动后,做的第一件事,就是创建1号进程——这是Linux系统中第一个用户态进程,也叫“初始化进程”。
早期的Linux系统,1号进程是init(相当于“老管家”),现在大部分主流发行版(如CentOS 7+、Ubuntu 15.04+),1号进程已经变成了 systemd(相当于“智能大管家”),功能更强大。
它的核心职责是:收养所有“孤儿进程”、启动和管理系统服务。
类比一下:1号进程就像公司的“CEO”,由“创始人”(0号进程)任命,负责管理整个公司的日常运营——启动所有必要的“部门”(系统服务),如果某个“部门经理”(父进程)离职(退出),留下的“员工”(子进程)就由CEO直接接管,不会变成“无主孤儿”。
严谨补充:1号进程的PID(进程ID)永远是1,这是固定的!不管你重启多少次系统,只要系统正常启动,1号进程就会存在,直到系统关机。
3. 父进程与子进程:职场里的“上下级”,一脉相承
除了0号和1号进程,系统里绝大多数进程,都是“父子关系”——一个进程(父进程)通过 fork() 系统调用,创建出另一个进程(子进程),就像“上级”培养“下级”。
举个生活化的例子:你在终端里敲 ls 命令,终端进程(父进程)就会创建一个 ls 进程(子进程),ls进程干完活(列出目录内容)后,就会“离职”(退出),控制权再交回终端进程。
严谨知识点(必记):
父进程创建子进程时,会复制自己的资源给子进程,子进程拥有独立的PID,和父进程互不干扰。
子进程的“爹”(父进程PID,简称PPID),就是创建它的进程的PID;父进程可以通过 wait() 或 waitpid() 等待子进程退出,回收子进程的资源(避免资源泄漏)。
如果父进程先于子进程退出,子进程就会变成“孤儿进程”,此时1号进程(systemd/init)会自动收养它,成为它的新父进程。
三、进程的“一生”:从“出生”到“死亡”,每一步都有规矩
和人一样,Linux进程也有“生老病死”,从创建(出生)、运行(干活),到退出(死亡)、资源回收(销毁),每一步都有明确的规则。
1. 进程的“出生”:只有一种方式——fork()
Linux里,所有用户态进程的“出生”,都离不开 fork() 系统调用(除了0号和1号进程)。
简单说:父进程执行 fork() 后,系统会复制父进程的所有资源,创建一个新的子进程。此时会有两个“一模一样”的进程在运行——唯一的区别是:父进程的 fork() 返回值是子进程的PID,子进程的 fork() 返回值是0。
类比一下:就像公司里的“老员工”(父进程),复制自己的“工作经验”(资源),培养出一个“新员工”(子进程)。新员工和老员工的“工作内容”(代码)一样,但有自己的“工号”(PID)。
补充:子进程创建后,常常会用 exec() 系统调用,替换自己的代码段,执行新的任务(比如父进程是终端,子进程通过 exec() 执行 ls 命令)。fork() 和 exec() 的组合,是Linux启动新程序的经典方式。
2. 进程的“干活”:抢占CPU,各凭本事
进程创建后,就进入“运行状态”,但Linux系统里的进程有很多,CPU资源有限,所以不是所有进程都能“一直干活”,而是由内核的“进程调度器”分配CPU时间片,轮流干活。
这里有个核心概念:进程优先级。
Linux中,进程的优先级用 nice 值来表示,范围是 -20 到 19,共40个级别。nice 值越小,优先级越高(-20是最高优先级,19是最低优先级)。
默认情况下,进程的 nice 值是 0。
操作命令:
用 top 或 ps -l 命令可以查看进程的 nice 值(NI列)。
启动新进程时设置优先级:nice -n 5 ./test.sh(将新进程的nice值设为5,优先级较低)。
调整已运行进程的优先级:renice -n 10 -p 1234(将PID为1234的进程nice值改为10)。
3. 进程的“死亡”:两种结局
进程不会一直“活”着,当它完成任务,或者出现错误,就会“死亡”。
(1)正常退出:功成身退,体面离场
就像员工“正常离职”,完成了自己的工作,主动提交离职申请。
常见的正常退出方式:
严谨点:正常退出的进程,会返回一个“退出状态码”(0表示成功,非0表示失败)。父进程可以通过 wait() 获取这个状态码。我们执行 echo $? 命令,就能查看上一个进程的退出状态码,若返回0,说明进程正常退出。
(2)异常退出:意外翻车,被迫离场
就像员工“被开除”,或者“意外离职”,不是主动退出。
常见的异常退出方式:
收到信号:比如我们敲 Ctrl+C,就是给进程发送 SIGINT 信号,强制终止进程。
进程崩溃:比如段错误(Segmentation Fault)、非法访问内存,内核会直接终止进程。
使用 kill -9 PID 强制结束进程(发送 SIGKILL 信号)。
4. 进程的“僵尸化”与“销毁”:一个必知的坑
很多人以为,进程“死亡”后就结束了——其实不是!进程死亡后,会变成“僵尸进程”(Zombie Process),需要父进程“善后”(回收资源)。
为什么会有僵尸进程?
当子进程结束运行时,它会向父进程发送 SIGCHLD 信号,并保留自己的进程控制块(PCB),等待父进程来读取它的退出状态。如果父进程没有调用 wait() 或 waitpid(),子进程的PCB就会一直驻留在内存中——这就是僵尸进程。
僵尸进程的危害:
类比一下:员工离职后,需要办理“离职手续”(资源回收),比如交还工牌、电脑。如果人事部门(父进程)一直不办手续,这个员工的档案(PCB)就会一直占用公司资源。
如何解决僵尸进程?
5. 孤儿进程:不幸中的万幸
与僵尸进程相对的是“孤儿进程”:如果父进程先于子进程退出,子进程就变成了孤儿进程。
孤儿进程怎么办?
内核会将孤儿进程的父进程设置为1号进程(init/systemd),由1号进程负责回收。所以孤儿进程不会造成资源泄漏,是安全的。
四、进程的几种状态(看懂top命令必备)
在 top 或 ps 命令中,进程的状态用字母表示:
状态 | 字母 | 说明 | 类比 |
运行 | R | 正在运行或正在运行队列中等待 | 员工正在工位干活 |
睡眠 | S | 可中断睡眠,等待某事件完成 | 员工在茶水间等咖啡 |
深度睡眠 | D | 不可中断睡眠,通常等待IO | 员工在开会,谁叫都不理 |
停止 | T | 暂停运行,通常收到SIGSTOP信号 | 员工被暂停工作 |
僵尸 | Z | 已退出但未被父进程回收 | 离职但没办完手续的员工 |
五、必记重点:一张表理清核心知识点
知识点 | 核心说明 | 生活化类比 |
0号进程 | 内核态,系统第一个进程,负责调度和初始化,PID=0,常规ps看不到 | 公司奠基创始人,退居幕后 |
1号进程 | 用户态第一个进程,PID=1,收养孤儿进程,管理系统服务(init/systemd) | 公司CEO,管理日常运营 |
父进程 | 通过fork()创建子进程,可通过wait()回收子进程资源 | 部门经理,培养新员工 |
子进程 | 由父进程创建,有独立PID,父进程退出后成为孤儿进程 | 新员工,跟着经理干活 |
fork() | 创建子进程,复制父进程资源,返回两次(0给子,PID给父) | 老员工复制自己带新人 |
exec() | 替换当前进程的代码段,执行新程序 | 员工转岗,换工作内容 |
正常退出 | 执行完代码或调用exit(),返回状态码 | 员工正常离职 |
异常退出 | 收到信号或崩溃,非自愿终止 | 员工被开除或意外离职 |
僵尸进程 | 进程死亡,父进程未回收资源,状态为Z,占用PID | 离职未办手续,占工位 |
孤儿进程 | 父进程先退出,子进程被1号进程收养 | 经理离职,员工被CEO接管 |
nice值 | 优先级参数,-20~19,越小优先级越高 | 员工职级,数字越小越核心 |
六、最后总结:记住3句话,吃透Linux进程
1. Linux进程树:0号生1号,1号生万物,所有进程都有“爹”(除了0号),孤儿进程归1号管。
2. 进程的一生:fork() 出生 → 调度干活(nice值影响优先级) → exit()/信号死亡 → 父进程回收(没回收就是僵尸)。
3. 核心区分:僵尸进程占PID不占CPU,必须回收;孤儿进程被1号收养,安全无危害。
其实Linux进程没有那么难,只要把“进程家族”的关系和“一生的流程”搞懂,再结合命令实操(ps、top、kill、nice),很快就能上手。
如果觉得这篇文章帮你理清了思路,不妨转发给身边学Linux的朋友,帮他们避开“进程误区”❤️