Problem: Signal SIGCHLD abfangen



  • Hallo,

    ich habe ein Problem mit dem Abfangen von dem Signal SIGCHLD, das ausgelöst wird, wenn sich ein Kindprozess beendet.

    Ich habe gelesen, dass das Signal "normalerweise ignoriert wird". Steht zumidnest hier:
    http://linuxreviews.org/man/signal/index.html.de

    Ok, dann muss man wohl dieses Signal stets "freigeben", oder?
    Zumindest hab ich das Problem, dass es ohne meinem Versuch das Signal freizugeben nicht klappt, aber auch mein Freigeben scheint falsch zu sein.

    Ich möchte also das Singal SIGCHLD per Signalhandler Funktion abfangen. Aber die Funktion wird nicht ausgeführt, obwohl ich den Kindprozess per exit(0) beende. Dieser wandert in der Prozesstabelle dann als Zombie herum.

    Übrigens funktioniert der Signalhandler, wenn ich SIGINT abfange.

    void init_signal_handler(int signal)
    {
    	struct sigaction sa;
    	sigset_t sigmask;
    
    	Sigemptyset(&sigmask);
    	Sigaddset(&sigmask, SIGCHLD);
    	Sigprocmask(SIG_UNBLOCK, &sigmask, NULL);
    
    	memset(&sa, 0, sizeof(sa));
    	sa.sa_handler 	= sig_child_handler;
    	sa.sa_mask		= sigmask;
    	Sigaction(signal, &sa, NULL);
    }
    


  • Ich kann dir nicht sagen warum dein nicht geht hab noch zu wenig erfahrung in C

    Aber kann dir zeigen wie ich es mache. (Ist doch schonmal was) 🙂

    struct sigaction sa1;
    	memset(&sa1,0,sizeof(sa1));
    	sa1.sa_handler=sigChildHandler;
    	sa1.sa_flags	=SA_NOCLDWAIT;
    	Sigaction(SIGCHLD,&sa1,NULL);
    
    static void sigChildHandler(int sig)//Prüfen auf beendigung von Kindprozessen
    {
    	if(wait(NULL)==-1)
    	{	
    		childnum--;
    		printf("Childnum:%d\n",childnum);
    	}
    
    }
    

    Vielleicht hilft es ja



  • ich hab die Unterschiede zu meinem Code übernommen, aber hat nix gebracht. Ich habs auch versucht ohne dem Bearbeiten der Signalmaske in meinem Code.



  • Also erst einmal, Signals muss man explizit freigeben.
    Ich benutze immer die ur-alten Signal-Routinen (und nicht sigaction()), das Prinzip sollte aber ähnlich sein:

    void *sigChld()
    {
    int errnosav;
    errnosav = errno;
    signal(SIGCHLD,SIG_IGN);  <== disable Signal
    incSigCnt(SC_CHLD);       <== eigene Routine, die nur einen Zähler hochzählt!!!
    signal(SIGCHLD,(void (*)()) sigChld);  <== re-enable signal
    errno = errnosav;
    return(NULL);		/* dummy assign to avoid compiler warnings */
    }
    
    ...
    
    void sigCatchEnable()
    {
    void *sigint;
    sigint = signal(SIGCHLD,(void (*)()) sigChld);    <== enable das Signal!!!
    if (sigint < 0)
        perror("set sigChld");
    ...
    

    Zu empfehlen sind aber eher die neuen Routinen zum Signal-Catchen.

    Noch ein paar Tipps:
    - Zombies sind systemspezifisch, auf manchen Unix/Linux-Systemen gibt es sie nicht. Weiter Info s.u.
    - Die Signal-Catching Routinen kommen völlig asynchron und können ggf. die globale int variable "errno" beeinflussen! Deshalb am Anfang kurz speichern und zum Schluss wieder restaurieren. Sonst kann im Hauptprogramm evtl. eine Abfrage auf "errno" schiefgehen....
    - Innerhalb der Signal-Catching Routine möglichst wenig Aufrufe, auf KEINEN Fall printf's mit Zeitausgaben. Das kann im Dauerbetrieb zum Prozessstillstand führen, da hierbei mutex-Bereiche benutzt werden. Teilweise funktioniert dann ein "kill -9" auf den Vaterprozess nicht mehr 😉 Im Beispiel wird nur ein Zahler höchgezählt.
    - Zumindest bei den alten signal-Catching Routinen können Signal-Interrupts verloren gehen (das ist m.E. bei den neuen Routinen nicht mehr der Fall).

    Zombies fängt man so ab:

    waitpid(child_pid,&status,WNOHANG);
    

    Man kann ggf. auch child_pid == -1 nehmen, da must du noch mal in den man-pages nachschauen.



  • Hilft mir auch nicht weiter.

    Wie gesagt, der selbe Code funktioniert, wenn ich SIGINT abfange, statt SIGCHLD.

    Evtl. liegts an meinem Kindprozess, ich überlager in per execl() mit einem Programm.

    Ich zeig mal wie ich den Kindprozess erzeuge:

    void init_time_child(void)
    {
    	pid_t pid;
    
    	switch(pid = Fork())
    	{
    		case 0:/* KP */
    			/* overlaying the process */
    			Execl(TIME_CHILD);
    		break;
    		default:
    		break;
    	}
    }
    

    Hier der Code vom Programm, welches den Kindprozess überlagert:

    int main(int argc, char *argv[])
    {
    	auction_t* auction;
    
    	set_flag(0);
    
    	init_auction_struct(auction);
    
    	init_shm_sem(auction);
    
    	//Close(client);
    
    	printf("time ende\n");
    	exit(0);
    
    	return 0;
    }
    

    das printf() wird ausgegeben.



  • ok, ichhab das Problem ausgemacht. Ich hab ein accept() nachdem ich den Kindprozess überlager. Und da der Prozess natürlich nicht in jedem Fall vor dem accept() sich beendet, wird accept() aufgerufen (blockiert) und das Signal dann anscheinend ignoriert.

    Hat auch was mit der Zufälligkeit des Schedulers zu tun. Aber kann das sein, dass das Signal SIGCHLD ignoriert wird, nur weil eine Funktion blockiert?



  • Ja, ist so!

    Ich habe eben mal kurz bei meinem Programmpaket den Debug-Modus eingeschaltet und ein paar Kinder in den Tod geschickt ...
    ... das accept() wird nicht unterbrochen, und das Signal kommt dann irgendwann später durch.

    Getestet bei Linux FC3, das Auflösen eines blockierenden Wartens durch SIG_CHLD habe ich aber auch bei anderen Systemen noch nicht beobachtet.



  • ok danke. Scheiße wenn man sowas nicht weiß und dann ewig Zeit damit verschwende nmuss...aaahh


Anmelden zum Antworten