别再被进程搞得焦头烂额!Linux进程管理从入门到放弃再到精通

26 阅读12分钟

前几天又有小伙伴在群里哭诉,说服务器上的进程跑飞了,CPU占用率直接飙到100%,整个系统卡得像老年机一样。这种情况我估计大家都遇到过,特别是刚入坑运维的朋友们。

说实话,我刚开始做运维那会儿,对进程管理也是一头雾水。记得有一次,公司的Web服务突然挂了,我在那里ps、kill命令一顿乱敲,结果把数据库进程也给误杀了...那天晚上加班到凌晨3点才把服务恢复,领导的脸色比锅底还黑。

不过经过这么多年的摸爬滚打,我算是把Linux进程管理这块儿给摸透了。今天就跟大家聊聊这个话题,希望能帮到正在被进程折磨的兄弟们。

进程到底是个啥玩意儿

很多人一听到"进程"这个词就头大,其实没那么复杂。你可以把进程理解成正在运行的程序,就像你打开浏览器看网页,这个浏览器就是一个进程。

在Linux系统里,每个进程都有自己的身份证号,我们叫它PID(Process ID)。就像每个人都有身份证号一样,系统通过PID来区分不同的进程。

我记得以前有个同事,总是搞不清楚进程和程序的区别。我就跟他打了个比方:程序就像是菜谱,而进程就是你按照菜谱正在做的那道菜。同一个菜谱可以同时做好几道菜,同一个程序也可以同时运行好几个进程。

查看进程的那些招数

想要管理进程,首先得知道系统里都跑着哪些进程。这就像管理员工一样,你得先知道公司里都有谁在上班。

最常用的命令就是ps了,这个命令我估计大家都用过:

ps aux

这个命令会列出系统中所有正在运行的进程。不过说实话,输出的信息有点多,刚开始看可能会眼花缭乱。

我个人比较喜欢用ps -ef,感觉输出格式更清爽一些:

ps -ef

如果你想看某个特定的进程,可以配合grep使用:

ps -ef | grep nginx

这样就只会显示包含"nginx"的进程了。

还有一个更直观的命令是top,它会实时显示系统中的进程信息,就像Windows的任务管理器一样:

top

top命令特别适合用来监控系统负载,你可以看到哪个进程占用CPU最多,哪个进程占用内存最多。我经常用它来排查性能问题。

不过top有个问题,就是界面有点丑。后来我发现了htop这个神器,界面好看多了,而且功能更强大:

htop

如果你的系统没有安装htop,可以用包管理器安装一下。

进程的生老病死

在Linux系统中,进程有好几种状态,就像人有生老病死一样。

运行状态(Running):进程正在CPU上执行,或者在运行队列中等待执行。这就像员工正在工作或者排队等待分配任务。

睡眠状态(Sleeping):进程在等待某个事件发生,比如等待用户输入或者等待磁盘读写完成。这种状态又分为可中断睡眠和不可中断睡眠。

停止状态(Stopped):进程被暂停了,通常是收到了SIGSTOP信号。就像按了暂停键一样。

僵尸状态(Zombie):这个状态比较特殊,进程已经结束了,但是它的父进程还没有来"收尸"。这种进程会一直占着PID,但不占用其他系统资源。

僵尸进程是个挺讨厌的东西,我之前遇到过一次,系统里突然出现了几百个僵尸进程,把可用的PID都占完了,新进程都创建不了。最后只能重启系统解决。

杀进程的正确姿势

说到进程管理,怎么能不提杀进程呢?kill命令估计是大家最熟悉的命令之一了。

最简单的用法就是:

kill PID

比如要杀掉PID为1234的进程:

kill 1234

不过很多人不知道,kill命令其实是发送信号给进程,默认发送的是TERM信号,相当于礼貌地请进程自己退出。

如果进程不听话,不肯退出,你就得用强制手段了:

kill -9 1234

这个-9参数发送的是KILL信号,进程收到这个信号后会被强制终止,没有任何商量的余地。

不过我建议大家不要一上来就用kill -9,因为这样进程没有机会做清理工作,可能会导致数据丢失或者其他问题。就像你不能一言不合就把员工开除,得先给个机会让人家交接工作。

还有一个killall命令,可以根据进程名来杀进程:

killall nginx

这会杀掉所有名为nginx的进程。用这个命令要小心,别误杀了重要进程。

进程的家族关系

Linux中的进程有父子关系,就像家族一样。每个进程都有一个父进程(除了1号进程),当父进程死掉时,它的子进程会变成孤儿进程,被1号进程收养。

你可以用pstree命令来查看进程的家族关系:

pstree

这个命令会以树状结构显示所有进程的父子关系,看起来挺有意思的。

有时候你想杀掉一个进程及其所有子进程,可以用:

kill -TERM -进程组ID

前面加个负号表示杀掉整个进程组。

后台进程的那些事儿

在Linux中,进程可以在前台运行,也可以在后台运行。前台进程会占用终端,你没法在同一个终端做其他事情。后台进程则不会,你可以继续在终端里输入其他命令。

要让一个命令在后台运行,只需要在命令后面加个&

./my_script.sh &

如果你已经启动了一个前台进程,想把它放到后台,可以先按Ctrl+Z暂停它,然后用bg命令让它在后台继续运行:

bg

要查看后台任务,可以用jobs命令:

jobs

想把后台任务调回前台,用fg命令:

fg %1

这里的%1表示第一个后台任务。

nohup:让进程"长生不老"

有时候你想让一个进程在你退出终端后继续运行,这时候就需要用到nohup命令了:

nohup ./my_script.sh &

nohup的意思是"no hang up",即使终端关闭了,进程也不会被杀掉。这个命令在部署服务的时候特别有用。

不过现在更多人喜欢用screen或者tmux这样的终端复用工具,功能更强大,使用也更灵活。

进程优先级调整

Linux系统中,每个进程都有一个优先级,用nice值来表示。nice值的范围是-20到19,数值越小优先级越高。

你可以在启动进程时指定nice值:

nice -n 10 ./my_script.sh

这样进程就会以较低的优先级运行,不会抢占太多CPU资源。

如果进程已经在运行了,可以用renice命令调整优先级:

renice 5 -p 1234

这会把PID为1234的进程的nice值调整为5。

我记得有一次,服务器上有个数据分析脚本占用了大量CPU,导致Web服务响应很慢。我就用renice把那个脚本的优先级调低了,问题立马就解决了。

监控进程资源使用情况

作为运维人员,监控进程的资源使用情况是家常便饭。除了前面提到的tophtop,还有一些其他有用的工具。

iotop可以监控进程的磁盘I/O使用情况:

iotop

nethogs可以监控进程的网络使用情况:

nethogs

这些工具在排查性能问题时特别有用。

系统负载的那些指标

说到进程管理,就不得不提系统负载。你可以用uptime命令查看系统负载:

uptime

输出大概是这样的:

14:23:45 up 12 days, 3:42, 2 users, load average: 0.15, 0.22, 0.18

这里的load average显示了1分钟、5分钟和15分钟的平均负载。很多新手不知道这个数字代表什么意思,我当初也是一脸懵逼。

简单来说,负载值表示系统的繁忙程度。如果你的服务器有4个CPU核心,负载值为4就表示CPU刚好被完全利用,超过4就说明有进程在排队等待CPU。

不过这个指标也不是绝对的,我见过负载很高但系统运行正常的情况,也见过负载不高但系统卡得要死的情况。所以还是要结合其他指标一起看。

一些实用的进程管理技巧

这些年积累了不少实用的小技巧,分享给大家。

有时候你需要批量杀掉某类进程,比如所有的php-fpm进程。可以这样做:

ps -ef | grep php-fpm | grep -v grep | awk '{print $2}' | xargs kill

这个命令看起来有点复杂,但用熟了就很顺手。先用ps找出所有进程,然后用grep过滤出php-fpm进程,再用awk提取PID,最后用xargs传给kill命令。

查找占用特定端口的进程也是常用操作:

lsof -i :8080

或者用netstat:

netstat -tlnp | grep :8080

我个人更喜欢用lsof,输出信息更详细一些。

还有一个很有用的技巧是设置进程的资源限制。比如限制进程能使用的内存:

ulimit -v 1000000

这会把虚拟内存限制在1GB。不过这个限制只对当前shell及其子进程有效。

进程监控脚本

作为一个懒惰的运维(其实所有优秀的运维都很懒),我写了不少监控脚本来自动化进程管理。

比如这个简单的nginx监控脚本:

#!/bin/bash
while true; do
    if ! pgrep nginx > /dev/null; then
        echo "$(date): Nginx is down, restarting..."
        systemctl start nginx
        if [ $? -eq 0 ]; then
            echo "$(date): Nginx restarted successfully"
        else
            echo "$(date): Failed to restart Nginx"
            # 这里可以加上发邮件或者发微信通知的逻辑
        fi
    fi
    sleep 60
done

这个脚本会每分钟检查一次nginx是否在运行,如果挂了就自动重启。虽然现在有systemd的自动重启功能,但有时候自己写的脚本更灵活,可以加上各种自定义的逻辑。

我还写过一个更复杂的脚本,会监控进程的CPU和内存使用情况,如果超过阈值就发告警。不过那个脚本有点长,这里就不贴了。

容器时代的进程管理

现在Docker和Kubernetes这么火,进程管理也有了新的变化。在容器里,1号进程特别重要,如果1号进程挂了,整个容器就挂了。

我之前就踩过这个坑。在Dockerfile里用了一个shell脚本作为启动命令,结果脚本里的主进程挂了,但shell进程还在,容器看起来是正常的,实际上服务已经不可用了。

后来学乖了,要么直接用exec启动主进程,要么在脚本里加上信号处理,确保能正确传递信号给子进程。

性能调优的一些心得

进程管理和性能调优是分不开的。我总结了几个经验:

CPU密集型的任务,进程数不要超过CPU核心数,否则上下文切换的开销会很大。

I/O密集型的任务,可以适当增加进程数,因为进程大部分时间在等待I/O。

对于Web服务,prefork模式适合内存充足的环境,worker模式适合高并发的场景。

数据库连接池的大小也很关键,太小了会成为瓶颈,太大了会浪费资源。

这些都是我在实际工作中踩坑总结出来的,每个环境都不一样,需要根据实际情况调整。

故障排查的套路

遇到进程相关的故障,我一般是这样排查的:

先看系统负载,用uptime和top命令。

然后看具体是哪个进程有问题,用ps和htop。

如果是性能问题,用strace跟踪系统调用,用gdb调试程序。

如果是内存问题,用valgrind检查内存泄漏。

如果是网络问题,用tcpdump抓包分析。

当然,最重要的还是看日志。很多问题在日志里都有线索,只是需要耐心去找。

一些容易犯的错误

新手在进程管理方面容易犯一些错误,我也犯过:

直接用kill -9杀进程,不给进程清理的机会。

忘记处理僵尸进程,导致PID耗尽。

在生产环境随便调整进程优先级,影响系统稳定性。

不了解进程的依赖关系,杀错了进程导致服务不可用。

没有做好监控,进程挂了很久才发现。

这些错误我都犯过,有些还造成了比较严重的后果。不过犯错也是学习的一部分,关键是要从错误中总结经验。

工具推荐

除了系统自带的命令,还有一些第三方工具很好用:

htop:比top更好用的进程监控工具

glances:一个全面的系统监控工具

supervisor:进程管理和监控工具,特别适合管理后台服务

systemd:现代Linux发行版的服务管理工具

这些工具各有特色,建议大家都试试,找到适合自己的。

总结

进程管理说起来简单,做起来还是有很多门道的。关键是要多实践,多总结。

我建议新手朋友们先在虚拟机或者测试环境多练习,熟悉了基本命令再到生产环境操作。千万别像我当年一样,在生产环境瞎搞,那真是心惊肉跳。

还有就是要养成良好的习惯,比如操作前先备份,杀进程前先确认PID,重要操作要做记录等等。这些看起来很基础,但真的很重要。

最后想说的是,虽然现在有很多自动化工具,但基础的进程管理技能还是不能丢。就像开车一样,虽然有了自动挡,但手动挡的技能还是要会的。

进程管理是Linux运维的基本功,掌握好了对职业发展很有帮助。现在云原生这么火,但底层还是离不开这些基础知识。

如果这篇文章对你有帮助,别忘了点赞转发支持一下!想了解更多运维实战经验和技术干货,记得关注微信公众号@运维躬行录,领取学习大礼包!!!我会持续分享更多接地气的运维知识和踩坑经验。让我们一起在运维这条路上互相学习,共同进步! 公众号:运维躬行录 个人博客:躬行笔记