我最近登录时发现有几千个进程被标记为“僵尸”。经过进一步调查,我发现以下内容ps fax
:
701 ? Ss 0:28 cron
3363 ? S 0:00 \_ CRON
3364 ? Ss 0:00 \_ /bin/sh -c [ -x /usr/lib/php5/maxlifetime ] && [ -d /var/lib/php5 ] && find /var/lib/php5/ -depth -mindepth 1 -maxdepth 1 -type f -cmin +$(/usr/lib/php5/maxlifetime) ! -execdir fuser -s {} 2>/dev/null \; -delete
3371 ? S 0:00 \_ find /var/lib/php5/ -depth -mindepth 1 -maxdepth 1 -type f -cmin +24 ! -execdir fuser -s {} ; -delete
3451 ? S 0:02 \_ fuser -s ./sess_jns5af2mvm81e2fg1rbuctlt54
3452 ? Z 0:00 \_ [fuser] <defunct>
3453 ? Z 0:00 \_ [fuser] <defunct>
3454 ? Z 0:00 \_ [fuser] <defunct>
... many, many lines omitted ...
13642 ? Z 0:00 \_ [fuser] <defunct>
据我所知,这是一个脚本,用于/etc/cron.d/php
在每个小时的 10 分钟和 40 分钟后清理死亡的 PHP 会话。
编辑:这是脚本的文本。它在 Ubuntu 上默认与 PHP 一起安装。
# /etc/cron.d/php5: crontab fragment for php5
# This purges session files older than X, where X is defined in seconds
# as the largest value of session.gc_maxlifetime from all your php.ini
# files, or 24 minutes if not defined. See /usr/lib/php5/maxlifetime
# Look for and purge old sessions every 30 minutes
09,39 * * * * root [ -x /usr/lib/php5/maxlifetime ] && [ -d /var/lib/php5 ] && find /var/lib/php5/ -depth -mindepth 1 -maxdepth 1 -type f -cmin +$(/usr/lib/php5/maxlifetime) ! -execdir fuser -s {} 2>/dev/null \; -delete
由于某种原因(目前我猜是一个行为不当的网络爬虫在每次请求时都会创建一个新会话,但我仍在查看日志),有时 中有数千个被遗弃的 php 会话/var/lib/php/
,并且当此脚本运行时,它会很乐意为每个会话生成一个新的 fuser 进程。这很快就会达到进程限制,并使系统变得缓慢。
除了删除这个 cron 作业并手动清理之外我还能做什么?
答案1
最好将逻辑移到find
一个脚本中,该脚本循环遍历命令行上的所有文件,以查看它们是否被访问,如果没有,则删除它们:
#!/bin/bash
for x; do
if ! /bin/fuser -s "$x" 2>/dev/null; then
rm "$x"
fi
done
然后将 cron 任务改为
09,39 * * * * root [ -x /usr/lib/php5/maxlifetime ] && [ -d /var/lib/php5 ] && find /var/lib/php5/ -depth -mindepth 1 -maxdepth 1 -type f -cmin +$(/usr/lib/php5/maxlifetime) -execdir thatscript.sh {} +
这将find
收集所有符合最大期限的会话文件,然后thatscript.sh
一次性运行所有文件(由于+
而不是;
)。然后脚本负责确保文件未被使用并将其删除。这样,应该只有一个直接子进程本身,并且 bash 在清理和子进程find
时应该不会遇到任何问题。fuser
rm
从find
的文档中,不清楚如果文件名列表超出了 shell/OS 限制,find 是否会自动将其分成多个执行(13000 个文件可能会这样……旧版本的 bash 的默认命令行参数限制在 5000 左右)在这种情况下,您可以更改-execdir thatscript.sh {} +
为 来-print0 | xargs -0 thatscript.sh
划分xargs
文件。
或者,如果您没有安装驱动器noatime
,请更改-cmin
为-amin
并完全放弃测试:
09,39 * * * * root [ -x /usr/lib/php5/maxlifetime ] && [ -d /var/lib/php5 ] && find /var/lib/php5/ -depth -mindepth 1 -maxdepth 1 -type f -amin +$(/usr/lib/php5/maxlifetime) -delete
这将删除最后的所有会话文件访问超过 [命令输出maxlifetime
] 分钟前。只要你没有任何 php 进程打开会话然后闲置很长时间(Debian 上的默认 maxlifetime 似乎是 24 分钟,这将是非常由于页面加载时间过长,因此不执行任何操作,这不应该破坏当前正在使用的任何会话。
答案2
我也有这个问题Ubuntu 11.10我通过编辑解决了这个问题:
/etc/cron.d/php5
并将代码替换为:
09,39 * * * * root [ -x /usr/lib/php5/maxlifetime ] && [ -d /var/lib/php5 ] && find /var/lib/php5/ -depth -mindepth 1 -maxdepth 1 -type f -cmin +$(/usr/lib/php5/maxlifetime) -delete
这是Ubuntu 11.04php 的 cron 作业。
答案3
修复脚本,使其等待其子进程或忽略 SIG_CHILD。您能将脚本放在我们能看见的地方吗?
更新:看起来你正在触发一个错误find
!