Недавно я заметил, что когда я входил в систему, у меня было несколько тысяч процессов, помеченных как «зомби». В ходе дальнейшего расследования я обнаружил следующее из 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
который должен очищать мертвые сеансы PHP в 10 и 40 минут в час.
Редактировать: Вот текст сценария. Он устанавливается по умолчанию вместе с PHP в Ubuntu.
# /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
По какой-то причине (в настоящее время я предполагаю, что веб-сканер с плохим поведением создает новый сеанс по каждому запросу, но я все еще просматриваю журналы), иногда в /var/lib/php/
, и когда этот сценарий запускается, он с радостью порождает новый процесс фьюзера для каждого из них. Это быстро достигает предела процесса и заставляет ползать.
Что я могу сделать, кроме простого удаления этого задания cron и очистки вручную?
У меня тоже эта проблема убунту 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
Это убунту 11.04 cron для php.
Вероятно, было бы лучше перенести логику с 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
со всеми сразу (из-за +
вместо того ;
). Затем сценарий отвечает за то, чтобы файл не использовался, и за его удаление. Сюда, find
должен иметь только один прямой потомок, и у bash не должно возникнуть проблем с очисткой fuser
и rm
дети.
Из find
документов, неясно, будет ли 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, которые открывают сеанс, затем сидите без дела в течение длительного времени (по умолчанию для этого maxlifetime в Debian кажется, что это 24 минуты, что было бы очень долгое время для загрузки страницы) ничего не делая, это не должно уничтожать текущие сеансы.
Исправьте сценарий, чтобы он ждал своих дочерних элементов или игнорировал SIG_CHILD. Вы можете поместить сценарий туда, где мы его увидим?
Обновить: Похоже, вы запускаете ошибку в find
!
Я решил это для клиента, переместив сеансы из файловой системы в memcache. У них не было зомби-процессов, но все же были миллионы сессий, которые cronjob не мог уследить за удалением. На установку memcache, перенастройку php.ini, тестирование и добавление нескольких графиков munin, чтобы посмотреть размер memcache, потребовалось около 10 минут. Presto - нагрузка на сервер снизилась, все довольны.
http://www.dotdeb.org/2008/08/25/storing-your-php-sessions-using-memcached/ http://www.ducea.com/2009/06/02/php-sessions-in-memcached/
Это также будет очень полезно:
https://bugs.launchpad.net/ubuntu/+source/php5/+bug/876387
Прочтите комментарии №4 и №8, причем последний даже сам исправляет термоэлемент!