Танец с бубном по-пингвиньи

Mar 06, 2010 00:05

Предыстория

Однажды для некоторых целей мной был написан небольшой сетевой демон, в задачи которого, помимо какой-то вялой сетевой активности в качестве сервера, входило пару раз в секунду предпринимать некоторые важные действия.

Архитектура была достаточно тривиальной: бесконечный цикл вокруг select, который спал до тех пор, покуда нет сетевых задач, и не пришло время выполнять важные действия (таймаут до наступления которых высчитывался перед собственно вызовом мультиплексора).

И все работало нормально, пока в один прекрасный день не обнаружилось, что реакция у демона стала какая-то не та. Сетевая активность типа в порядке, а важные действия выполняются несколько не вовремя. Как будто постарел, бедолага.

Акт первый

Особо размышлять мне было не с руки, поэтому я тупо отрефакторил код в сторону уменьшения количества различных вычислений на моей стороне. Расчет был таков, что, даст Бог -- проблема после этого починится как-нибудь сама. Вызов select у меня стал блокирующим, а важные действия я зашедулил через setitimer с необходимым интервалом (с перешедуливанием по необходимости). Ну а собственно действия переехали, разумеется, в обработчик SIGALRM. Собрал, запустил, проверил -- все ок, отдал хозяевам и успокоился.

Но не тут-то было. Конечно же, нихера не починилось, все симптомы те же самые.

Акт второй

Я (уже в некотором раздражении) начал траблшутить. Выясняется, что демон, запущенный руками работает как надо, а тот же самый демон, стартанувший автоматически при запуске системы, тупит и тормозит.

В чем разница?? Процесс и там и там от рута. Может, в стандартных дескрипторах, текущей директории, способе запуска? В чем вообще отличие, между процессом, запущенном из ash и стартовавшем из inittab? Ну хорошо, я причесал демон под классический порядок вещей (раньше он запускался по-колхозному, типа "daemon &"): оснастил fork-ом, chdir-ом, setsid-ом, переоткрыл stdin/stdout/stderr из /dev/null...

Стало, конечно, "породистей", но по результативности из десяти баллов мои действия смогли бы набрать ноль.

Акт третий

Я уже в некотором расстройстве. Повелел демону на SIGUSR1 в специальный файл сбрасывать дамп полного своего состояния, предварительно снабдив программу разного рода счетчиками. Начал следить: % kill -USR1 `pidof my_daemon` && sleep 1 && cat my_daemon.dump.

Само собой, никаких проблем не выявилось.

Добавил в дампе счетчик вызовов обработчика SIGALRM и расчет ожидаемого его количества: ууу блядь, разошлось раза в три!!

Итого имеем: если запускать руками демон, то события SIGALRM наступают вовремя (как было задано setitimer-ом), а если он стартовал сам, то события наступают на 66% позже.

Акт четвертый

Ну, собственно, с этой точки уже стало более-менее понятно, в какую сторону смотреть.

Из консоли демон стартует с nice равным 0, а из inittab-а с 10. Луниксовый шедулер, походу, процесс с таким приоритетом за процесс не считает вообще: потипу грязного тупого бомжа-ниггера среди успешных белых людей. Даже сигналы ему кидает через силу, и то, только если процессор простаивает уже достаточно долго, и делать ну совсем нечего :)

Конечно же, setpriority( PRIO_PROCESS, getpid(), -4 ); проблему вылечил, и злой шедулер был посрамлен.

Эпилог

Добро в моем лице снова восторжествовало, пойду выпью кофейку по этому поводу.

setitimer, daemon, linux, priority, daily, nice

Previous post Next post
Up