Heim  >  Artikel  >  Betrieb und Instandhaltung  >  Was ist ein Linux-Zombie-Prozess?

Was ist ein Linux-Zombie-Prozess?

藏色散人
藏色散人Original
2023-04-06 11:37:083615Durchsuche

Der Linux-Zombie-Prozess ist ein Prozess, der schon vor langer Zeit gestorben ist, aber er belegt immer noch eine Position in der Prozesstabelle. Wenn der übergeordnete Prozess nicht über wait() verfügt, wird er normalerweise als „beendet“ angezeigt. " mit ps, so werden Zombie-Prozesse generiert; wenn eine große Anzahl von Zombie-Prozessen generiert wird, kann das System keine neuen Prozesse generieren, da keine Prozessnummern verfügbar sind, daher sollten Zombie-Prozesse vermieden werden.

Was ist ein Linux-Zombie-Prozess?

Die Betriebsumgebung dieses Tutorials: Linux5.9.8-System, Dell G3-Computer.

Die Generierung und Vermeidung von Zombie-Prozessen (Defunct Processes) unter Linux

1. Was ist ein Zombie-Prozess?

In einem UNIX-System endet ein Prozess, aber sein übergeordneter Prozess wartet nicht darauf (Aufruf wait/waitpid ), dann wird es ein Zombie-Prozess. Wenn Sie den Befehl ps verwenden, um den Ausführungsstatus des Prozesses zu beobachten, werden Sie feststellen, dass die Statusleiste dieser Prozesse nicht mehr funktioniert. Ein Zombie-Prozess ist ein Prozess, der vor langer Zeit gestorben ist, aber immer noch einen Platz in der Prozesstabelle einnimmt.

Aber wenn der übergeordnete Prozess des Prozesses zuerst beendet wurde, wird der Prozess nicht zu einem Zombie-Prozess. Denn wenn jeder Prozess endet, scannt das System alle im aktuellen System ausgeführten Prozesse, um festzustellen, ob es sich bei einem Prozess um einen untergeordneten Prozess des gerade beendeten Prozesses handelt. Wenn dies der Fall ist, übernimmt der Init-Prozess ihn und wird zum übergeordneten Prozess Dadurch wird sichergestellt, dass jeder Prozess einen übergeordneten Prozess hat. Der Init-Prozess wartet automatisch auf seine untergeordneten Prozesse, sodass alle von Init übernommenen Prozesse nicht zu Zombie-Prozessen werden.

2. Wie Prozesse unter UNIX funktionieren

Jeder Unix-Prozess hat einen Einstiegspunkt (Eintrag) in der Prozesstabelle. KernprozessAlle bei der Ausführung des Prozesses verwendeten Informationen werden am Einstiegspunkt gespeichert. Wenn Sie den Befehl ps verwenden, um Prozessinformationen im System anzuzeigen, werden die relevanten Daten in der Prozesstabelle angezeigt. Wenn mit dem Systemaufruf fork() ein neuer Prozess erstellt wird, weist der Kernprozess dem neuen Prozess einen Einstiegspunkt in der Prozesstabelle zu und speichert dann relevante Informationen in der Prozesstabelle, die dem Einstiegspunkt entsprechen. Eine dieser Informationen ist die Identifikationsnummer des übergeordneten Prozesses.

Das Ende des untergeordneten Prozesses und die Ausführung des übergeordneten Prozesses sind ein asynchroner Prozess, das heißt, der übergeordnete Prozess kann niemals vorhersagen, wann der untergeordnete Prozess enden wird. Gehen also die Statusinformationen beim Ende des untergeordneten Prozesses verloren, weil der übergeordnete Prozess zu beschäftigt ist, um auf den untergeordneten Prozess zu warten, oder weil er nicht weiß, wann der untergeordnete Prozess endet?

Nein. Da UNIX einen Mechanismus bereitstellt, der sicherstellt, dass der übergeordnete Prozess die Statusinformationen abrufen kann, wenn der untergeordnete Prozess endet, solange er sie erhalten möchte. Dieser Mechanismus ist: Wenn der untergeordnete Prozess seinen Lebenszyklus abschließt, führt er den Systemaufruf „exit()“ aus und der Kernel gibt alle Ressourcen des Prozesses frei, einschließlich geöffneter Dateien, belegtem Speicher usw. Es werden jedoch weiterhin bestimmte Informationen dazu gespeichert (einschließlich der Prozess-ID, des Exit-Codes, des Beendigungsstatus des Prozesses, der vom Prozess benötigten CPU-Zeit usw.), und diese Daten werden bis zum Systemdurchlauf beibehalten Es wird an seinen übergeordneten Prozess übergeben und erst freigegeben, wenn der übergeordnete Prozess es über wait/waitpid abruft.

Mit anderen Worten: Wenn ein Prozess stirbt, verschwindet er nicht vollständig. Der Prozess wird beendet, er wird nicht mehr ausgeführt, aber es sind noch einige Restdaten vorhanden, die darauf warten, dass der übergeordnete Prozess sie zurückfordert. Wenn ein übergeordneter Prozess einen untergeordneten Prozess forkt(), muss er mit wait() (oder waitpid()) darauf warten, dass der untergeordnete Prozess beendet wird. Es ist diese Aktion wait(), die die Restdaten des untergeordneten Prozesses verschwinden lässt.

3. Der Schaden von Zombie-Prozessen

Wenn der übergeordnete Prozess nicht wait/waitpid aufruft, werden die gespeicherten Informationen nicht freigegeben und seine Prozessnummer wird immer belegt, aber die Prozesstabellenkapazität des Systems ist begrenzt Wenn eine große Anzahl von Zombie-Prozessen generiert wird, kann das System keine neuen Prozesse generieren, da keine Prozessnummern verfügbar sind.

Deaktivierte Prozesse belegen also nicht nur die Speicherressourcen des Systems und beeinträchtigen die Leistung des Systems, sondern können, wenn es zu viele davon gibt, auch zu einer Systemlähmung führen. Da der Scheduler den Defunct-Prozess außerdem nicht auswählen kann, kann der Kill-Befehl nicht zum Löschen des Defunct-Prozesses verwendet werden. Die einzige Möglichkeit besteht darin, das System neu zu starten.

4. Generierung eines Zombie-Prozesses

Wenn der übergeordnete Prozess nicht wartet (), wenn der untergeordnete Prozess stirbt, können Sie normalerweise mit ps sehen, dass er als „21eba13340b1d3a525d7480ca5bbd370“ angezeigt wird, sodass ein Zombie-Prozess generiert wird . Dies bleibt für immer so, bis der übergeordnete Prozess wait() ausführt.

Es ist ersichtlich, dass der nicht mehr funktionierende Prozess erscheint, nachdem der untergeordnete Prozess beendet wurde, aber bevor der übergeordnete Prozess die Daten gelesen hat. Aus diesem Grund können wir mit dem folgenden Programm einen nicht mehr existierenden Prozess erstellen:

#include ade979de5fc0e1ca0540f360a64c230b 
 
#includeda996ff59ef1c1fa2f19eea6833e0f6c 
 
main()  
{  
 
 if(!fork())  
    {  
 
        printf(“child pid=%d\n”, getpid());  
 
        exit(0);  
 
    }  
 
    sleep(20);  
 
    printf(“parent pid=%d \n”, getpid());  
 
    exit(0);  
 
}

当上述程序以后台的方式执行时,第17行强迫程序睡眠20秒,让用户有时间输入ps -e指令,观察进程的状态,我们看到进程表中出现了defunct进程。当父进程执行终止后,再用ps -e命令观察时,我们会发现defunct进程也随之消失。这是因为父进程终止后,init 进程会接管父进程留下的这些“孤儿进程”(orphan process),而这些“孤儿进程”执行完后,它在进程表中的进入点将被删除。如果一个程序设计上有缺陷,就可能导致某个进程的父进程一直处于睡眠状态或是陷入死循环,父进程没有wait子进程,也没有终止以使Init接管,该子进程执行结束后就变成了defunct进程,这个defunct 进程可能会一直留在系统中直到系统重新启动。

在看一个产生僵尸进程的例子。

子进程要执行的程序test_prog

//test.c 
#include ade979de5fc0e1ca0540f360a64c230b int main()  {  
 int i = 0;  
 for (i = 0 ; i < 10; i++)  
        {  
                printf ("child time %d\n", i+1);  
                sleep (1);  
        }  
 return 0;  }

父进程father的代码father.c

#include <stdio.h> 
#include <unistd.h> 
#include <sys/types.h> 
#include <sys/wait.h> 
int main()  
{  
 int pid = fork ();  
 if (pid == 0)  
        {  
                system ("./test_prog");  
                _exit (0);  
        }else 
        {  
 int i = 0;  
 /* 
                                int status = 0; 
                while (!waitpid(pid, &status, WNOHANG)) 
                { 
                        printf ("father waiting%d\n", ++i); 
                        sleep (1); 
                }*/ 
 while (1)  
                {  
                        printf ("father waiting over%d\n", ++i);  
                        sleep (1);  
                }  
 return 0;  
        }  
 
}

执行./father,当子进程退出后,由于父进程没有对它的退出进行关注,会出现僵尸进程

20786 pts/0    00:00:00 father  
20787 pts/0    00:00:00 father 21eba13340b1d3a525d7480ca5bbd370

总结:子进程成为 defunct 直到父进程 wait(),除非父进程忽略了 SIGCLD 。更进一步,父进程没有 wait() 就消亡(仍假设父进程没有忽略 SIGCLD )的子进程(活动的或者 defunct)成为 init 的子进程,init 着手处理它们。

五、如何避免僵尸进程

1、父进程通过wait和waitpid等函数等待子进程结束,这会导致父进程挂起。

在上个例子中,如果我们略作修改,在第8行sleep()系统调用前执行wait()或waitpid()系统调用,则子进程在终止后会立即把它在进程表中的数据返回给父进程,此时系统会立即删除该进入点。在这种情形下就不会产生defunct进程。

2. 如果父进程很忙,那么可以用signal函数为SIGCHLD安装handler。在子进程结束后,父进程会收到该信号,可以在handler中调用wait回收。

3. 如果父进程不关心子进程什么时候结束,那么可以用signal(SIGCLD, SIG_IGN)或signal(SIGCHLD, SIG_IGN)通知内核,自己对子进程的结束不感兴趣,那么子进程结束后,内核会回收,并不再给父进程发送信号

4. fork两次,父进程fork一个子进程,然后继续工作,子进程fork一个孙进程后退出,那么孙进程被init接管,孙进程结束后,init会回收。不过子进程的回收还要自己做。 下面就是Stevens给的采用两次folk避免僵尸进程的示例:

#include "apue.h" #include 069d3d88bce9f46cf91e8b21a3bb1b3f  int main(void)  ...{  
     pid_t    pid;  
 
 if ((pid = fork()) 7556d38bf89e4c298ea18b0165fac49b 0)  
             exit(0);    /**//* parent from second fork == first child */ 
 /**//* 
          * We're the second child; our parent becomes init as soon 
          * as our real parent calls exit() in the statement above. 
          * Here's where we'd continue executing, knowing that when 
          * we're done, init will reap our status. 
         */ 
         sleep(2);  
         printf("second child, parent pid = %d ", getppid());  
         exit(0);  
     }  
 
 if (waitpid(pid, NULL, 0) != pid)  /**//* wait for first child */ 
         err_sys("waitpid error");  
 
 /**//* 
      * We're the parent (the original process); we continue executing, 
      * knowing that we're not the parent of the second child. 
     */ 
     exit(0);  }

相关推荐:《Linux视频教程

Das obige ist der detaillierte Inhalt vonWas ist ein Linux-Zombie-Prozess?. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Vorheriger Artikel:Was ist Linux im April?Nächster Artikel:Was ist Linux im April?