程序是文件,进程是运行中的实例
很多人混用"程序"和"进程"。其实它们是两个东西:程序是磁盘上的文件(静态),进程是程序加载进内存跑起来的实例(动态)。
所以 ps 列的是进程不是程序,kill 杀的是进程不是程序(杀进程不删程序文件)。一个程序能同时跑多个进程(多开),一个进程只对应一个程序。理解这条,你就懂了为什么"关了浏览器标签页还有 chrome 进程"——因为不是所有进程都随标签关,有些后台进程独立。进程是操作系统调度的单位,程序只是它的来源。
下面是一棵进程树。点任意进程,看它的 PID、父进程、状态,以及僵尸/孤儿等特殊情况。
进程有两种异常状态,新手常分不清:
子进程退出了,但父进程没调用 wait() 读它的退出码。进程表里还占着位(PID+退出码),不占内存但占 PID。多了会耗尽 PID。
父进程先死了,子进程被 init(PID 1)收养继续跑。本身不是 bug,但如果是后台任务可能"跑飞"没人管。
僵尸的解法不是杀僵尸(它已经死了),而是杀或重启它的父进程——父进程被杀后,僵尸变孤儿,init 会自动收尸。所以看到一堆僵尸进程,先 ps -o ppid= -p 僵尸PID 找它爹,再处理爹。孤儿则要靠监控(systemd/supervisor)来管,别让进程脱离监管自己跑。僵尸是爹的锅,孤儿是没监护人的锅。
这三个命令常被混用,其实各管一摊:
ps -ef 看全字段含父进程,ps aux 看 CPU/内存占用。一次性输出,适合排查"现在跑着啥"。command & 放后台,jobs 看它,fg %1 拉回前台。适合"我开了几个后台任务"。关键区别:ps/top 看系统所有进程,jobs 只看当前 shell 的后台作业。一个守护进程(如 nginx)在 ps 里能看到,但不在 jobs 里——它不是你 shell 的作业。所以"我 jobs 看不到那个进程"不是 bug,是 jobs 的设计范围。排查时按需选:系统级用 ps/top,自己 shell 的后台任务用 jobs。