从零搭建高效的系统守护者
📖 目录导读
- 为什么需要监控进程状态?
- 进程监控的核心原理
- 手把手教你编写第一个监控脚本
- 进阶技巧:自动重启与日志记录
- 常见问题与解决方案(FAQ)
- 附录:脚本模板与最佳实践
为什么需要监控进程状态?
在服务器运维或日常开发中,进程意外崩溃、死锁或资源耗尽的情况时有发生,一个没有守护进程的Web服务可能在凌晨突然宕机,导致业务中断数小时。监控进程状态的核心目的是:第一时间发现异常并自动恢复,避免人工介入的延迟。

根据监控实践统计,配置进程监控脚本后,系统可用性可从95%提升至99.9%以上,这意味着每月停机时间从36小时缩短至43分钟。
进程监控的核心原理
要编写有效的监控脚本,先理解操作系统如何管理进程:
1 进程状态的关键指标
- 运行状态(R):进程正在执行
- 睡眠状态(S):等待资源(如I/O、锁)
- 僵尸状态(Z):进程已结束但父进程未回收资源
- 停止状态(T):被信号暂停
2 监控本质:检查进程是否存在
脚本通过以下方式判断:
- PID文件检查:通过存储进程PID的文件核对
- 进程名匹配:使用
ps或pgrep命令搜索进程列表 - 端口检测:验证监听端口是否存活(适用于网络服务)
💡 SEO提示:使用时融入关键词“linux进程监控”“shell脚本检测进程”。
手把手教你编写第一个监控脚本
1 基础版一:检测进程是否存在
#!/bin/bash
# 定义要监控的进程名
PROCESS_NAME="nginx"
# 检查进程是否在运行
if ! pgrep -x "$PROCESS_NAME" > /dev/null; then
echo "$(date) - 进程 $PROCESS_NAME 未运行,开始启动..."
systemctl start nginx
else
echo "$(date) - 进程 $PROCESS_NAME 运行正常"
fi
代码解释:
pgrep -x:精确匹配进程名(不加-x会匹配包含该名称的子进程)> /dev/null:丢弃标准输出,只保留退出状态码systemctl start:自动重启(适用于systemd管理的服务)
2 基础版二:检查端口是否监听
适用于数据库、Web服务器等网络服务:
#!/bin/bash
PORT=3306 # MySQL默认端口
if netstat -tlnp | grep ":$PORT " > /dev/null; then
echo "端口 $PORT 正常监听"
else
echo "端口 $PORT 未监听,启动MySQL..."
systemctl start mysqld
fi
3 加入资源监控(进阶)
#!/bin/bash
# 检查CPU使用率是否超过85%
CPU_THRESHOLD=85
PROCESS_NAME="java"
USAGE=$(ps -C "$PROCESS_NAME" -o %cpu --no-headers | awk '{sum+=$1} END {print sum}')
if (( $(echo "$USAGE > $CPU_THRESHOLD" | bc -l) )); then
echo "警告:进程 $PROCESS_NAME CPU使用率达 ${USAGE}%"
# 可选:发送告警邮件
else
echo "CPU使用率正常"
fi
进阶技巧:自动重启与日志记录
1 带日志的完整脚本模板
#!/bin/bash
# 配置区
PROCESS_NAME="mysqld"
LOG_FILE="/var/log/process_monitor.log"
MAX_RESTART=3
RESTART_COUNT=0
# 日志函数
log() {
echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >> "$LOG_FILE"
}
# 监控循环
while true; do
if ! pgrep -x "$PROCESS_NAME" > /dev/null; then
log "进程 $PROCESS_NAME 已停止,尝试重启..."
systemctl start "$PROCESS_NAME"
RESTART_COUNT=$((RESTART_COUNT + 1))
if [ $RESTART_COUNT -ge $MAX_RESTART ]; then
log "达到最大重启次数,发送紧急通知"
# 这里可以集成邮件或Slack通知
break
fi
else
RESTART_COUNT=0 # 重置计数器
fi
sleep 10 # 每10秒检查一次
done
2 使用cron实现定时监控(代替死循环)
在/etc/crontab中添加:
*/1 * * * * root /usr/local/bin/check_process.sh
每分钟执行一次监控脚本,避免while循环消耗资源。
常见问题与解决方案(FAQ)
Q1:如何监控非systemd管理的进程(如第三方软件)?
A:使用nohup启动进程时保存PID文件:
nohup ./myapp > /dev/null 2>&1 & echo $! > /var/run/myapp.pid
监控脚本通过kill -0 $(cat /var/run/myapp.pid)检查进程存活。
Q2:脚本误判怎么办?比如进程名被其他程序占用?
A:采用“三重验证”策略:
- 检查进程名(
ps aux | grep -w myapp) - 检查PID文件(
kill -0 $PID) - 检查端口(
netstat -tlnp | grep 8080)
Q3:如何发送告警通知?
A:集成企业微信或邮件:
# 邮件通知
mail -s "进程告警" admin@example.com < alert.txt
# 企业微信机器人
curl -X POST -H "Content-Type: application/json" \
-d '{"content":"进程 $PROCESS_NAME 异常"}' \
https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=YOUR_KEY
Q4:脚本本身崩溃怎么办?
A:使用supervisord管理监控脚本本身,实现“监视监控器”的双层守护架构。
附录:脚本最佳实践模板
生产环境推荐模板(Python版)
#!/usr/bin/env python3
import subprocess, time, os
def check_process(name):
cmd = f"pgrep -x {name}"
result = subprocess.run(cmd.split(), capture_output=True, text=True)
return result.returncode == 0
def restart_service(service_name):
subprocess.run(["systemctl", "restart", service_name])
def log_event(msg):
with open("/var/log/monitor.log", "a") as f:
f.write(f"{time.ctime()} - {msg}\n")
if __name__ == "__main__":
process_name = "nginx"
while True:
if not check_process(process_name):
log_event(f"进程 {process_name} 宕机,启动中...")
restart_service(process_name)
time.sleep(30)
关键参数优化建议
- 检查频率:关键服务每10秒,常规服务每分钟
- 重生次数限制:建议3次/5分钟内,防止死循环重启
- 内存泄漏检测:额外监控RSS内存是否持续增长
编写监控脚本的核心在于:明确监控指标(进程、端口、资源)→ 简单可靠的检测逻辑 → 带限制的自动恢复机制,建议从pgrep+systemctl开始实验,逐步加入端口检查和资源告警,最后用cron或supervisord确保脚本本身高可用,通过本文的模板和FAQ,你已具备构建生产级监控系统的能力。