PHP中clamscan扫描部分路径后无法终止cpulimit的问题
问题描述
通过PHP切换到Linux指定用户后,执行以下操作:
- 用
cpulimit将clamscan的CPU使用率限制在88%,后台运行 - 递归扫描指定路径(如
/home/user、/home/user/mail),扫描日志写入指定文件,扫描完成后终止cpulimit,同样后台运行
两段exec命令均加&后台执行,但出现异常:
- 扫描
/home/user时,扫描结束后cpulimit仍在运行,通过ps aux | grep cpulimit可查到进程 - 扫描
/home/user/mail或/home/user/public_html时,扫描结束后cpulimit能正常终止
相关PHP代码:
switch_user($user); exec('cpulimit -l 88 -e clamscan &'); exec('clamscan --recursive '.$scan_path.' -l '.$log_file.' 2>&1 && pkill cpulimit &');
问题原因
原代码依赖进程名匹配(cpulimit -e clamscan、pkill cpulimit),存在以下隐患:
- 当扫描大目录(如
/home/user)时,clamscan可能衍生子进程,cpulimit会持续监控所有名为clamscan的进程,即使主进程结束,若有残留子进程或进程名缓存,cpulimit不会自动退出 pkill cpulimit会终止当前用户下所有cpulimit进程,但若扫描大目录时,clamscan的退出信号未正确触发&&后的pkill(如进程组差异、后台会话隔离),会导致cpulimit残留
解决方案
改用PID绑定的方式,让cpulimit仅监控当前启动的clamscan进程,扫描结束后仅终止对应cpulimit进程,避免进程名匹配的不确定性。
修改后的PHP代码:
switch_user($user); // 将clamscan启动、cpulimit绑定、扫描结束后终止cpulimit的逻辑整合为一个后台命令 exec('( clamscan --recursive '.$scan_path.' -l '.$log_file.' 2>&1 & CLAMSCAN_PID=$!; cpulimit -l 88 -p $CLAMSCAN_PID & CPULIMIT_PID=$!; wait $CLAMSCAN_PID; kill $CPULIMIT_PID ) &');
代码说明
- 用子shell
()包裹所有命令,确保进程在同一会话内运行 - 启动
clamscan后立即记录其PID(CLAMSCAN_PID=$!) - 针对该PID启动
cpulimit,并记录cpulimit的PID - 通过
wait等待clamscan扫描完成,再用kill终止对应的cpulimit进程 - 整个逻辑后台运行(末尾
&)
这种方式彻底避免了进程名匹配的问题,无论扫描路径大小,都能确保cpulimit在clamscan结束后被精准终止。
内容的提问来源于stack exchange,提问作者jay
相关产品推荐
相关产品推荐

