Что такое зомби процесс linux
3.4.3. Процессы-зомби
Если дочерний процесс завершается в то время, когда родительский процесс заблокирован функцией wait(), он успешно удаляется и его код завершения передается предку через функцию wait(). Но что произойдет, если потомок завершился, а родительский процесс так и не вызвал функцию wait()? Дочерний процесс просто исчезнет? Нет, ведь в этом случае информация о его завершении (было ли оно аварийным или нет и каков код завершения) пропадет. Вместо этого дочерний процесс становится процессом-зомби.
Зомби — это процесс, который завершился, но не был удален. Удаление зомби возлагается на родительский процесс. Функция wait() тоже это делает, поэтому перед ее вызовом не нужно проверять, продолжает ли выполняться требуемый дочерний процесс. Предположим, к примеру, что программа создает дочерний процесс, выполняет нужные вычисления и затем вызывает функцию wait(). Если к тому времени дочерний процесс еще не завершился, функция wait() заблокирует программу. В противном случае процесс на некоторое время превратится в зомби. Тогда функция wait() извлечет код его завершения, система удалит процесс и функция немедленно завершится.
Что же всё-таки случится, если родительский процесс не удалит своих потомков? Они останутся в системе в виде зомби. Программа, показанная в листинге 3.6, порождает дочерний процесс, который немедленно завершается, тогда как родительский процесс берет минутную паузу, после чего тоже заканчивает работу, так и не позаботившись об удалении потомка.
Листинг 3.6. (zombie.c) Создание процесса-зомби
/* Создание дочернего процесса. */
/* Это родительский процесс — делаем минутную паузу. */
/* Это дочерний процесс — немедленно завершаем работу. */
Скомпилируйте этот файл и запустите программу. Пока программа работает, перейдите в другое окно и просмотрите список процессов с помощью следующей команды:
Эта команда отображает идентификатор самого процесса и его предка, а также статус процесса и его командную строку. Обратите внимание на присутствие двух процессов с именем zombie. Один из них — предок, другой — потомок. У последнего идентификатор родительского процесса равен идентификатору основного процесса zombie, при этом потомок обозначен как (несуществующий), а его код состояния равен Z (т.е. zombie — зомби).
Итак, мы хотим узнать, что будет, когда программа zombie завершится, не вызвав функцию wait(). Останется ли процесс-зомби? Нет — выполните команду ps и убедитесь в этом: оба процесса zombie исчезли. Дело в том, что после завершения программы управление ее дочерними процессами принимает на себя специальный процесс — демон init, который всегда работает, имея идентификатор 1 (это первый процесс, запускаемый при загрузке Linux). Демон init автоматически удаляет все унаследованные им дочерние процессы-зомби.
Читайте также
Процессы
Процессы Создание параллельных процессов настолько полно описано в литературе по UNIX, что здесь мы приведем лишь минимально необходимый беглый обзор, останавливаясь только на отличительных особенностях ОС QNX.Всякое рассмотрение предполагает наличие системы понятий.
Процессы в MI
Процессы в MI Процесс в MI — это системный объект, называемый пространством управления процессом. Обратите внимание, что эквивалентного объекта OS/400 нет. (Мы еще поговорим об этом в разделах, посвященных управлению работами). Задача процесса в MI — связать воедино ресурсы,
Процессы
Процессы Процессы в операционной системе UNIX играют ключевую роль. От оптимальной настройки подсистемы управления процессами и числа одновременно выполняющихся процессов зависит загрузка ресурсов процессора, что в свою очередь имеет непосредственное влияние на
Процессы
Процессы В главе 1 уже упоминались процессы. Однако знакомство ограничивалось пользовательским, или командным интерфейсом операционной системы. В этом разделе попробуем взглянуть на них с точки зрения программиста.Процессы являются основным двигателем операционной
1.6 Процессы
1.6 Процессы Наряду с файлом, понятие процесса является важнейшим в концепции открытых операционных систем.Процесс – это обладающая уникальным идентификатором единица исполняемого кода35 в памяти.Подавая простую команду из оболочки, оператор дает ОС указание запустить
12.3.3.4 Фиктивные процессы
12.3.3.4 Фиктивные процессы Когда ядро выполняет переключение контекста в однопроцессорной системе, оно функционирует в контексте процесса, уступающего управление (см. главу 6). Если в системе нет процессов, готовых к запуску, ядро переходит в состояние простоя в контексте
3.4. Процессы
3.4. Процессы Для того чтобы эффективно управлять своим компьютером, вы должны досконально изучить свой сервер и работающие на нем процессы. Взломав ваш сервер, злоумышленник постарается запустить на нем какую-либо программу, которая незаметно будет выдавать хакеру права
5 Процессы
5 Процессы 5.1. Системные вызовы fork() и ехес() Процесс в Linux (как и в UNIX) — это программа, которая выполняется в отдельном виртуальном адресном пространстве. Когда пользователь регистрируется в системе, под него автоматически создается процесс, в котором выполняется оболочка
Зомби? Нет – перерождение
Зомби? Нет – перерождение После того как домен удален из реестра доменных имен, регистрация такого же домена оказывается доступна любому другому лицу; в стандартных случаях у прежнего администратора нет никаких приоритетов. Правда, подобные приоритеты могут возникнуть
1.6 Процессы
1.6 Процессы Наряду с файлом, понятие процесса является важнейшим в концепции открытых операционных систем.Процесс — это обладающая уникальным идентификатором единица исполняемого кода[35] в памяти.Подавая простую команду из оболочки, оператор дает ОС указание запустить
3.1.3. Взаимодействующие процессы
3.1.3. Взаимодействующие процессы В случае Unix малозатратное создание дочерних процессов (Process-Spawning) и простое межпроцессное взаимодействие (Inter-Process Communication — IPC) делают возможным использование целой системы небольших инструментов, каналов и фильтров. Данная система будет
7.2.5. Подчиненные процессы
7.2.5. Подчиненные процессы Иногда дочерние программы интерактивно принимают и возвращают данные вызвавшим их программам через каналы, связанные со стандартным выводом и вводом. В отличие от простых вызовов с созданием подоболочки и конструкций, которые выше были названы
Глава 3 Процессы
Глава 3 Процессы Выполняющийся экземпляр программы называется процессом. Если на экране отображаются два терминальных окна, то, скорее всего, одна и та же терминальная программа запущена дважды — ей просто соответствуют два процесса. В каждом окне, очевидно, работает
Плохо написанные или плохо работающие программы могут оставлять зомби-процессы, скрывающиеся внутри вашего Linux-компьютера. Узнайте, как создаются зомби и как их наконец упокоить.
Как состояния процесса работают в Linux
Linux, конечно же, должен отслеживать все приложения и демоны, работающие на вашем компьютере. Один из способов сделать это — поддерживать таблицу процессов. Это список структур в памяти ядра. Каждый процесс имеет запись в этом списке, которая содержит некоторую информацию о нем.
В каждой из структур таблицы процессов нет ничего особенного. Они держат идентификатор процесса, несколько других элементов данных и указатель на блок управления процессом (PCB) для этого процесса.
Это печатная плата, которая содержит множество деталей, которые Linux необходимо найти или настроить для каждого процесса. Плата также обновляется по мере создания процесса с учетом времени обработки и, наконец, уничтожается.
«Состояние процесса» может быть любым из следующих:
В состоянии Zombie родительский процесс вызывает один из wait() семейства функций при создании дочернего процесса. Затем он ждет изменения состояния в дочернем процессе. Был ли дочерний процесс остановлен, продолжен или уничтожен сигналом? Завершилось ли оно естественным завершением своего кода?
Если изменение состояния означает, что дочерний процесс остановлен, считывается его код выхода. Затем дочерняя печатная плата уничтожается, а его запись в таблице процессов удаляется. В идеале все это происходит в мгновение ока, а процессы в состоянии зомби существуют не очень долго.
Что вызывает зомби-процессы в Linux?
Плохо написанный родительский процесс может не вызывать wait() функция при создании дочернего процесса. Это означает, что ничто не отслеживает изменения состояния в дочернем процессе, и SIGCHLD сигнал будет проигнорирован. Или, возможно, другое приложение влияет на выполнение родительского процесса из-за плохого программирования или злого умысла.
Однако, если родительский процесс не следит за изменениями состояния в дочернем процессе, надлежащего обслуживания системы не произойдет. Плата и запись в таблице процессов не будут удалены при завершении дочернего процесса. Это приводит к тому, что состояние зомби никогда не удаляется с печатной платы.
Зомби используют немного памяти, но обычно не представляют проблемы. Запись в таблице процессов небольшая, но, пока она не будет выпущена, идентификатор процесса нельзя использовать повторно. В 64-битной операционной системе это вряд ли вызовет какие-либо проблемы, потому что размер платы намного больше, чем запись в таблице процессов.
Возможно, огромное количество зомби может повлиять на объем памяти, свободной для других процессов. Однако, если у вас так много зомби, у вас серьезная проблема с родительским приложением или ошибка операционной системы.
Как удалить зомби-процессы
Поэтому мы вводим следующую команду для поиска информации о процессе 7641, но она будет сообщать только идентификатор родительского процесса:
Нам сказали, что идентификатор родительского процесса — 7636. Теперь мы можем ссылаться на него, используя ps еще раз.
Зомби не страшны…
… Если они не в огромной орде. О некоторых не стоит беспокоиться, и простая перезагрузка уничтожит их.
Однако, если вы заметили, что приложение или процесс всегда порождают зомби, вам следует изучить это. Скорее всего, это просто небрежно написанная программа, и в этом случае, возможно, есть обновленная версия, которая должным образом очищается после своих дочерних процессов.
Зомби процессы в Linux
Функция fork запускает дочерний процесс, который попадает под контроль родительского процесса. Если родительский процесс получает сигнал о завершении дочернего процесса, но при этом игнорирует его (в возможные причины сбоя вдаваться не станем), то этот процесс становится зомби.
Стоит ли обращать на них своё внимание? Ответ: делать это совершенно не обязательно. Так как процесс уже был завершен, он перестал использовать системные ресурсы, но остался в системе. Самое разумное в этом случае – сделать вид, что его не существует, и продолжать работать с устройством, как ни в чем не бывало. Если пользователь попытается «уничтожить» зомби при помощи специальной команды «kill», у него ничего не выйдет, ведь этот процесс не выполняется. Это действие является бессмысленным, а потому процесс продолжит отображаться в системе.
Чтобы полностью убрать зомби, необходимо уничтожить или перезапустить его родительский процесс. Если есть такое желание, обратитесь к инструкции ниже.
Как посмотреть зомби в операционной системе Linux
Это можно сделать при помощи утилиты ps:
$ ps aux | grep defunct
Как обнаружить в системе «родителя»:
После того, как нашли родителя можно удалить зомби. Это единственный способ уничтожения зомби процесса. Прямые команды не удалят их. Найдите в колонке идентификатор «родителя». В приведенном примере он находится в 4-м столбце под номером 761.
С помощью команды «kill» пошлем сигнал завершения, чтобы зомби процесс прекратил своё существование в системе.
В вашем случае вместо 761 будет стоять другое значение. Его нужно правильно обнаружить.
Данные рекомендации созданы для тех пользователей, которые любят порядок во всем. Но еще раз напоминаем: зомби в операционной системе Линукс безвредны, и являются абсолютно логичным следствием ошибки.
Зомби, которые съедают вашу память
Что бы вы там себе не думали, а зомби существуют. И они действительно едят мозги. Не человеческие, правда, а компьютерные. Я говорю сейчас о зомби-процессах и потребляемых ими ресурсах. Это будет душераздирающая история о потерянных и снова найденных 32 ГБ оперативной памяти. Возможно, лишь некоторые из вас столкнутся с точно такой же проблемой, но если вдруг это произойдёт — у вас хотя бы будет шанс понять, что происходит.
Начнём с того, что компьютеры под управлением ОС Windows склонны со временем терять память. Ну, по крайней мере, у меня, при моём способе ими пользоваться. После пары недель без перезагрузок (или, например, всего одного уикэнда за который я 300 раз пересобрал Хром) я стал замечать, что диспетчер задач начинает показывать мне очень маленькое количество свободной оперативной памяти, но в то же время в системе нет никаких процессов, которые эту самую память активно используют. В том примере выше (с 300 сборками Хрома) диспетчер задач сказал мне, что в системе занято 49.8 ГБ плюс ещё 4.4 ГБ памяти сжато — но при этом запущено всего несколько процессов, и все они в сумме даже и близко не используют столько памяти:
В моём компьютере 96 ГБ оперативной памяти (да, я счастливчик) и когда у меня нет вообще никаких запущенных процессов — я, знаете ли, хотел бы видеть ну хотя бы половину этой памяти свободной. Я правда рассчитываю на это. Но иногда этого достичь не удаётся и мне приходится перезагружать ОС. Ядро Windows написано качественно и надёжно (без шуток), так что память не должна бы пропадать бесследно. Но всё же она пропадает.
Первой же моей догадкой стало воспоминание о том, что один из моих коллег как-то жаловался на зомби-процессы, которые иногда остаются в ОС уже не активными, но всё же ещё не до конца удалёнными ядром. Он даже написал специальную утилиту, которая выводит список таких процессов — их имена и количество. Когда он запускал эту утилиту в своих тестах, то получал до нескольких сотен зомби-процессов на обычной Windows-машине. Я нашел его инструмент, запустил на своём компьютере и получил… 506 000 зомби-процессов. Да, 506 тысяч!
Я вспомнил, что одной из возможных причин перехода процесса в состояние «зомби» может быть то, что какой-то другой процесс держит открытым его дескриптор (handle). В моём случае большое количество зомби-процессов играло мне на руку — им было сложнее скрыться. Я просто открыл диспетчер задач и добавил на вкладку Details столбец с количеством открытых дескрипторов для каждого процесса. Затем отсортировал список по убыванию значений в этом столбце. Я сразу нашел героя данной истории — процесс CcmExec.exe (часть Microsoft System Management Server) имел 508 000 открытых дескрипторов. Это было во-первых, очень много, а во-вторых, подозрительно близко к найдненному мною выше числу в 506 000 зомби-процессов.
Всё получилось ровно так, как я того и ожидал. Как я без иронии писал выше — ядро Windows написано очень хорошо и когда процесс уничтожается, то и все занятые им ресурсы освобождаются. Закрытие CcmExec.exe освободило 508 000 дескрипторов, что дало возможность окончательно закрыть 506 000 зомби-процессов. Количество свободной оперативной памяти мгновенно выросло на 32 ГБ. Тайна раскрыта!
Что такое зомби-процесс?
До этого момента мы ещё не выяснили, что же заставило все эти процессы зависнуть в неопределённости, а не быть удалёнными. Похоже на то, что мы имеем дело с тривиальным багом в приложении (а не в ядре ОС). Общее правило гласит, что когда вы создаёте процесс, то получаете его дескриптор и дескриптор его главного потока. Вы ОБЯЗАНЫ закрыть эти дескрипторы. Если вашей задачей было просто запустить процесс — их можно закрыть сразу же (это не убъёт запущенный процесс, а просто разорвёт связь вашего процесса с ним). Если новый процесс вам для чего-то нужен (например, вы ждёте окончания его работы или вам нужен код, который он вернёт) — то нужно воспользоваться соответствующими функциями (например, WaitForSingleObject(hProcess, INFINITE) для ожидания выхода или GetExitCodeProcess(hProcess, &exitCode) для получения кода возврата) и всё-равно закрыть дескрипторы после того, как вы получили от дочернего процесса всё, чего хотели. Аналогично следует и поступать и с дескрипторами процессов, которые вы для чего-нибудь открываете с помощью функции OpenProcess().
Если процесс, который забывает так поступать, относится к системным, то вам даже может не помочь выйти из своего аккаунта и снова залогиниться, только полная перезагрузка.
Куда же девается память?
500 000 раз по 32 КБ будет равно примерно 16 ГБ — куда же делась остальная память? Сравнение состояния памяти до и после закрытия зомби-процессов даёт ответ на этот вопрос:
Мы можем чётко увидеть, что
16 ГБ уходит на Process Private Memory. Также мы видит, что ещё 16 ГБ приходится на Page Table Memory. Очевидно, что каждый зомби-процесс занимает 32 КБ в таблице страниц памяти и еще 32 КБ использует для своей личной памяти. Я не знаю для чего зомби-процессу так много памяти, но, наверное, никто никогда не думал, что число таких процессов может измеряться сотнями тысяч.
Некоторые типы занятой памяти увеличились после закрытия процесса CcmExec.exe, в основном это касается Mapped File и Metafile. Я не знаю точно, почему так получилось. Одной из моих догадок является то, что ОС решила, что свободной памяти теперь достаточно и что-то себе закешировала. Это, в общем, не плохо. Мне не жаль памяти для нужд ОС, я просто не хочу, чтобы она пропадала совсем уж бесцельно.
Важное замечание: RamMap тоже открывает дескрипторы всех процессов, так что эту утилиту следует закрыть, если вы хотите добиться закрытия зомби-процессов.
Я написал твит о моей находке и исследование продолжил другой программист, который сумел воспроизвести данный баг и передать информацию о нём разработчику из Microsoft, который сказал, что это «известная проблема, которая иногда случается, когда очень много процессов запускаются и закрываются очень быстро».
Я надеюсь, что данная проблема будет скоро исправлена.
Почему у меня на компьютере возникают такие странные проблемы?
Я работаю над кодом Windows-версии Хрома и одной из моих задач является оптимизация его сборки на этой ОС, а это требует многократных запусков этой самой сборки. Каждая сборка Хрома запускает огромное множество процессов — от 28 000 до 37 000 в зависимости от выбранных настроек. При использовании нашей распределённой системы сборки (goma) эти процессы создаются и закрываются очень быстро. Мой лучший результат сборки Хрома — 200 секунд. Но столь агрессивная политика запуска процессов выявляет и проблемы в ядре Windows и её компонентах:
Что дальше?
Если вы работаете не на компьютере, управляемом политиками компании, то процесс CmmExec.exe у вас не запущен и с конкретно данным багом вы не столкнётесь. Также он коснётся вас только если вы собираете Хром или делаете ещё что-то похожее, создавая и закрывая при этом десятки тысяч процессов в короткие промежутки времени.
CcmExec — не единственная в мире программа с багами. Я нашел много других, содержащих в себе конкретно этот же тип ошибок, приводящих к созданию зомби-процессов. И есть ещё огромное множество тех, которые я не нашел.
Как знают все опытные программисты, любая ошибка, которая не была явно исправлена или предупреждена — точно когда-то произойдёт. Просто написать в документации «Пожалуйста, закройте этот дескриптор» — не достаточно. Так что вот мой вклад в то, чтобы сделать нахождение подобного типа ошибок проще, а их исправление — реальнее. FindZombieHandles — это инструмент, основанный на NtApiDotNet и коде от @tiraniddo, который выводит список зомби-процессов и информацию о том, кто сделал их зомби. Вот пример вывода данной утилиты, запущенной на моём компьютере:
274 зомби — это ещё не так плохо. Но уже и это указывает на определённые проблемы, которые могут быть найденны и исправлены. Процесс IntelCpHeciSvc.exe в этом списке имеет самые большие проблемы — похоже на то, что он открывает (и забывает закрыть) дескриптор процесса каждый раз, когда я открываю видео в Windows Explorer.
Visual Studio забывает закрыть дескрипторы как минимум двух процессов, в одном случае это воспроизводится всегда. Просто запустите сборку проекта и подождите
15 минут пока процесс MSBuild.exe закроется. Можно также выставить опцию “set MSBUILDDISABLENODEREUSE=1” и тогда MSBuild.exe закроется сразу по окончанию сборки и потерянный дескриптор будет виден сразу. К сожалению, какой-то негодяй в Microsoft исправил эту проблему и фикс должен выйти в обновлении VS 15.6, так что поторопитесь воспроизвести её, пока это ещё работает (надеюсь, не нужно объяснять, что это была шутка и никакой он на самом деле не негодяй).
Также вы можете использовать для просмотра забытых процессов программу Process Explorer, сконфигурировав её нижнюю панель так, как это показано ниже (заметьте, что в этом случае будут показаны забытые дескрипторы как для процессов, так и для потоков):
Вот пару примеров найденных багов (о некоторых сообщено разработчикам, но не о всех):
Используя Process Explorer, я заметил, что NVDisplay.Container.exe открывает
5000 дескрипторов на событие \BaseNamedObjects\NvXDSyncStop-61F8EBFF-D414-46A7-90AE-98DD58E4BC99, создавая новый дескриптор каждые две минуты. Я так понимаю, они хотят быть супер-уверены в том, что могут остановить NvXDSync? Багрепорт Nvidia отправлен.
Corsair Link Service создаёт
15 дескрипторов в секунду, не освобождает их совсем. Багрепорт отправлен.
Adobe’s Creative Cloud теряет тысячи дескрипторов (около 6500 в день, по моим подсчётам). Багрепорт отправлен.
Удивительно, что никто до этого особо не обращал внимание на подобные баги. Эй, Microsoft, возможно, стоит собирать статистику по таким случаям и что-то предпринимать по этому поводу? Эй, Intel и Nvidia, почистите немного ваш код. Помните, я наблюдаю за вами.
А теперь вы можете взять утилиту FindZombieHandles, запустить её на вашей машине и рассказать о своих находках. Также вы можете использовать в экспериментах диспетчер задач и Process Explorer.