Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Борьба с процессами зомби
Форум разработчиков электроники ELECTRONIX.ru > Cистемный уровень проектирования > Операционные системы > Программирование
snedelko
Ситуация следующая: есть процесс, который запускается по автозапуску системы. Проблема: процесс при неизвесных обстоятельствах вылетает. Причины: разные java script:emoticon(':)', 'smid_2'). Возможно это утечка памяти, аппаратный сбой (программа работает с драйверами устройств), ошибка при работе с памятью и т. д. Мне нужно отследить "вылетание", и заново запустить программу. Я решил сделать это при помощи скрипта (см. влож. файл), который я поместил в автозапуск (вместо самой программы). Скрипт простой (напомню, что pidof - команда, которая возвращает pid процесса по имени, и если процесса нет, возвращает 0). Все работает, когда программы выходит по exit(0). Но вот при segmentation fault скрипт не помогает, так как процесс становиться зомби (т.е. имеет pid != 0).

Подскажите почему, или предложите свой вариант.
InvisibleFed
Известное мне определение процесса зомби - процесс не имеющий родителя (в случае, когда родител мертв, а потомок остался жив). Может посмотреть, жив ли родитель процесса? Если мертв - значит твой процесс - зомби. Кажды процесс, насколько я помню хранит pid родителя.
Olej
Цитата(InvisibleFed @ Jan 21 2007, 08:11) *
Известное мне определение процесса зомби - процесс не имеющий родителя (в случае, когда родител мертв, а потомок остался жив). Может посмотреть, жив ли родитель процесса? Если мертв - значит твой процесс - зомби.

Почти так да не совсем:
- зомби - это всегда завершившийся процесс (а не завершившийся родитель) ...
- процесс становится зомби, когда он отправляет по завершению SIGCHLD - а его некому обработать, например процесс родитель просто не обрабатывает его, например, не ожидает завершения на wait() или waitpid()....
- ... можете в запускающем процессе просто добавить пустой обработчик SIGCHLD wink.gif ...

Цитата(InvisibleFed @ Jan 21 2007, 08:11) *
Кажды процесс, насколько я помню хранит pid родителя.


pid_t getppid( void );
snedelko
Вообще, с процессами зомби немного знаком - боролся я с ними, вызывая в обработчике SIGCHLD функцию wait(). В моей ситуации родителем кто является? Скрипт? Объясните пожалуйста. Если скрипт - то есть ли команда - аналог функции wait()?
И подскажите мне вот в чем: мой скрипт срабатывает около 3-х раз (в ситуации, когда программа завершается сама (по exit)), а на 4-й раз появляются зомби. Откуда? И почему не появлялись раньше? А вот когда программа завершается по segmentation fault (то бишь её рубит Линукс, тем же kill наверно), то зомби появляються СРАЗУ....
Olej
Цитата(snedelko @ Jan 22 2007, 11:50) *
Вообще, с процессами зомби немного знаком - боролся я с ними, вызывая в обработчике SIGCHLD функцию wait(). В моей ситуации родителем кто является? Скрипт? Объясните пожалуйста. Если скрипт - то есть ли команда - аналог функции wait()?
И подскажите мне вот в чем: мой скрипт срабатывает около 3-х раз (в ситуации, когда программа завершается сама (по exit)), а на 4-й раз появляются зомби. Откуда? И почему не появлялись раньше? А вот когда программа завершается по segmentation fault (то бишь её рубит Линукс, тем же kill наверно), то зомби появляються СРАЗУ....

Я мало что понял из этого объяснения sad.gif...
Скрипт не может быть родителем wink.gif - родителем может быть только породивший процесс, это может быть, например, скриптовый интерпретатор, выполняющий ваш скрипт...

P.S. предложение-подсказка - как разобраться в вашей trouble:
- сделайте над своим процессом пустую "обёртку", которая будет только exec/spawn ваш процесс...
- в ней вы можете перехватывать - наблюдать - обрабатывать все события...
- разобравшись что происходит - выбросите обёртку wink.gif.
InvisibleFed
Цитата
- зомби - это всегда завершившийся процесс (а не завершившийся родитель) ...
- процесс становится зомби, когда он отправляет по завершению SIGCHLD - а его некому обработать, например процесс родитель просто не обрабатывает его, например, не ожидает завершения на wait() или waitpid()....


А если процесс еще не завершился, а родитель уже откинулся - как ни крути, а процесс уже по любому зомби (его просто некому будет убить по завершению).
Если родитель еще жив и по каким-то причинам не может обработать SIGCHLD - процесс не может считаться зомби, т. к. потенциально родитель еще может его кончить. Интересно, что произойдет с родителем, если он не будет "ждать детей" (wait, waitpid) - он просто заврешиться и все потомки станут зомби. Тонкости, конечно, но все что изложил Olej умещается в моем определении зомби. Или существет пример, когда оно неверно?
snedelko
А если процесс зомби, то потоки, запущенные им, тоже зомби? И вообще, тонкостей тут куча, где бы про это подробно почитать ?
niid
Цитата(snedelko @ Jan 23 2007, 11:13) *
А если процесс зомби, то потоки, запущенные им, тоже зомби? И вообще, тонкостей тут куча, где бы про это подробно почитать ?


Вот:
Цитата
Если же потомок уже завершил работу, а предок не готов принять от системы сигнал об этом событии, то потомок не исчезает полностью, а превращается в "зомби" (zombie)


Процесс зомби, следовательно завершил работу и не может принимать сигналы.
Потомки станут зомби, когда завершат свою работу.

Вот Вам еще несколько цитат:

Цитата
Если родительский процесс по какой-то причине завершится раньше дочернего, последний становится "сиротой" (orphaned process). "Сироты" автоматически "усыновляются" программой init, выполняющейся в процессе с номером 1, которая и принимает сигнал об их завершении.

Если же потомок уже завершил работу, а предок не готов принять от системы сигнал об этом событии, то потомок не исчезает полностью, а превращается в "зомби" (zombie); в поле Stat такие процессы помечаются буквой Z. Зомби не занимает процессорного времени, но строка в таблице процессов остается, и соответствующие структуры ядра не освобождаются. После завершения родительского процесса "осиротевший" зомби на короткое время также становится потомком init, после чего уже "окончательно умирает".


Цитата
Последнее из состояний процесса, достижимых внутренней синхронизацией, есть состояние промежуточного завершения текущего процесса - SZOMB (состояние "зомби"). Состояние "зомби" имеет место, если процесс-потомок завершается по системному вызову exit или по сигналу до планируемой реализации системного вызова wait в процессе-предке. При этом образ завершившегося процесса освобождает адресное пространство, но его дескриптор временно сохраняется в таблице процессов, чтобы обеспечить корректную обработку системного вызова wait в процессе-предке.


Цитата
В состоянии "зомби" процесс не имеет образа в RAM, но информация о нем сохраняется в таблице процессов. Он не поедает ресурсы, но теоретически могут кончится PID'ы.

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


Если есть желание, можете почитать про организацию процессов:
http://cad.narod.ru/methods/os_unix/unibas/process.html
http://linux-admin.net.ru/content/257

А вообще, можете поискать в Google-Linux.
Olej
Цитата(snedelko @ Jan 23 2007, 12:13) *
А если процесс зомби, то потоки, запущенные им, тоже зомби? И вообще, тонкостей тут куча, где бы про это подробно почитать ?


Вопрос в общих контурах - понятный и описанный, а в деталях - заинтересовал...
Я тут набросал тест маленький, на скорую руку, который позволяет создавать "временных зомби", наблюдать их... (пример очень draft - писался на коленке - no comments):
Код
#include <signal.h>
#include <iostream>

using std::cout;
using std::endl;
using std::flush;

static const int MSG_BUF = 160;
static char msgbuf[ MSG_BUF ];
static char *puthead( void ) {
   struct timespec t;
   clock_gettime( CLOCK_REALTIME, &t );
   int sec = t.tv_sec % 60, min = t.tv_sec % 3600 / 60,
       ms = t.tv_nsec / 1000000, mks = t.tv_nsec % 1000000 / 1000;
   sprintf( msgbuf, "[%02d:%02d.%03d.%03d] %d: ", min, sec, ms, mks, getpid() );
   return msgbuf + strlen( msgbuf );
};

static void chend( int ) {
   sprintf( puthead(), "oh, my God - my child is dead!\n" );
   cout << msgbuf << flush;
};

int main( int argc, char* argv[] ) {
   int c, msec = 300, level = 0;
   while( ( c = getopt( argc, argv, "c:t:" ) ) != -1 ) {
      switch( c ) {
         case 'c': level = atoi( optarg ); break;
         case 't': msec = atoi( optarg ); break;
         default : exit( EXIT_FAILURE );
      };
   };
   signal( SIGCHLD, chend );
   pid_t id; // = -1;
   int w = 4, j = -1;
   while( --level > 0 && ( id = fork() ) == 0 ) { w += j; j = ( j < 0 ? 2 : -1 );  };
   sprintf( puthead(), "start - my parent is %d\n", getppid() );
   cout << msgbuf << flush;
   for( int i = 0; i < w; i++ )
      if( delay( msec ) != 0 ) --i;
      else {
         sprintf( puthead(), "continue %d - parent is %d\n", i + 1 , getppid() );
         cout << msgbuf << flush;
      };
   sprintf( puthead(), "finished\n" );
   cout << msgbuf << flush;
   exit( EXIT_SUCCESS );
};



а вот вариант его прогона:
Код
# ./zomby2 -c4 -t2000
[38:04.744.723] 6467647: start - my parent is 4710443
[38:04.746.723] 6471744: start - my parent is 6467647
[38:04.748.722] 6471745: start - my parent is 6471744
[38:04.748.722] 6471746: start - my parent is 6471745
[38:06.746.417] 6467647: continue 1 - parent is 4710443
[38:06.748.416] 6471744: continue 1 - parent is 6467647
[38:06.750.416] 6471745: continue 1 - parent is 6471744
[38:06.750.416] 6471746: continue 1 - parent is 6471745
[38:08.748.110] 6467647: continue 2 - parent is 4710443
[38:08.750.110] 6471744: continue 2 - parent is 6467647
[38:08.752.110] 6471745: continue 2 - parent is 6471744
[38:08.752.110] 6471746: continue 2 - parent is 6471745
[38:10.749.804] 6467647: continue 3 - parent is 4710443
[38:10.751.804] 6471744: continue 3 - parent is 6467647
[38:10.751.804] 6471744: finished
[38:10.752.804] 6467647: oh, my God - my child is dead!
[38:10.753.804] 6471745: continue 3 - parent is 6471744
[38:10.753.804] 6471746: continue 3 - parent is 6471745
[38:12.754.497] 6467647: continue 4 - parent is 4710443
[38:12.754.497] 6467647: finished
[38:12.755.497] 6471745: continue 4 - parent is 6471744
[38:12.755.497] 6471746: continue 4 - parent is 6471745
[38:12.755.497] 6471746: finished
[38:12.756.497] 6471745: oh, my God - my child is dead!
# [38:14.758.191] 6471745: continue 5 - parent is 1
[38:14.758.191] 6471745: finished

#

# pidin
6467647   1 ./zomby2            10o NANOSLEEP
6471744     (Zombie)
6471745   1 ./zomby2            10o NANOSLEEP
6471746   1 ./zomby2            10o NANOSLEEP

- проганял я, естественно wink.gif, не в Linux - но он весь - POSIX:­ вы
можете взять и поганять нечто подобное в вашей любимой ОС - он создаёт любое число процессов, каждый из которых может поочерёдно становиться зомби.

P.S. мне особенно понравился wink.gif цикл:
while( --level > 0 && ( id = fork() ) == 0 ) { w += j; j = ( j < 0 ? 2 : -1 ); };
- который циклом и не является ("... не верь глазам своим"(с) К.Прутков), и в каждом процессе выполняется по 1-й итерации этого "якобы цикла" ровно по 1-му разу : "цикл между процессами" wink.gif
KirillS
Цитата(Olej @ Jan 22 2007, 10:20) *
Цитата(snedelko @ Jan 22 2007, 11:50) *

Вообще, с процессами зомби немного знаком - боролся я с ними, вызывая в обработчике SIGCHLD функцию wait(). В моей ситуации родителем кто является? Скрипт? Объясните пожалуйста. Если скрипт - то есть ли команда - аналог функции wait()?
И подскажите мне вот в чем: мой скрипт срабатывает около 3-х раз (в ситуации, когда программа завершается сама (по exit)), а на 4-й раз появляются зомби. Откуда? И почему не появлялись раньше? А вот когда программа завершается по segmentation fault (то бишь её рубит Линукс, тем же kill наверно), то зомби появляються СРАЗУ....

Я мало что понял из этого объяснения sad.gif...
Скрипт не может быть родителем wink.gif - родителем может быть только породивший процесс, это может быть, например, скриптовый интерпретатор, выполняющий ваш скрипт...

P.S. предложение-подсказка - как разобраться в вашей trouble:
- сделайте над своим процессом пустую "обёртку", которая будет только exec/spawn ваш процесс...
- в ней вы можете перехватывать - наблюдать - обрабатывать все события...
- разобравшись что происходит - выбросите обёртку wink.gif.


Я б именно так и поступил. Как перехватывать/наблюдать/обрабатывать сигналы неплохо описано тут
Mihail Gluhowchenko
Извините нет времени подробно прочитать тему, я взял из бизибокса реализацию ps дальше брал состояние процесса и всё. В /proc оно вроде отображается. Ну а далее запускаем по крону с нужной частотой. Можно сделать чтобы процесс писал что-то в файлик а другая програмка его анализировала или скрипт.
733259
Может не в тему, но у меня нормально получалось следить, перезапускать процессы из perl, типа

foreach $pid(@processes){
if(waitpid($pid,WNOHANG)){

это я к тому, что дспетчер можно и на C написать, но скриптом быстрее, прблем меньше, ИМХО.

ЗЫ: пример, буквально за 3 минуты
Код
#! /usr/bin/perl -w

use strict;
use POSIX;

my $proc;
my @program = ("wget","-c","-t","1","-T","10",
"http://download.xilinx.com/direct/webpack/91/WebPACK_SFD_91i.zip");

unless($proc = fork()){
  exec(@program);
}

for(;;){
  if(waitpid($proc,WNOHANG)){
    if($? != 0){
      unless($proc = fork()){
        exec(@program);
      }
    }
  }
  select(undef,undef,undef,0.1);
}
wget просто так, ничего в голову не пришло
Olej
to модератор:
- эту тему, наверное, нужно перенести в подфорум "Программирование" ? :
http://electronix.ru/forum/index.php?showforum=154
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.