Funtionsweise von fork()



  • Hi,

    ich bin kein C-Profi, zu mehr als Hallo Welt hat bis jetzt noch nicht gereicht. Programmieren an sich kann ich allerdings, Perl, Java, PHP, SH stellen kein grösseres Problem dar..

    Ich soll als Uni-Aufgabe ein Programm erstellen, was 2 Kindprozesse startet, die dann mehr oder weniger sinnlose Sachen tun. Meines Wissens tut man das mit fork(), allerdings ist mir noch nicht ganz klar geworden was dann innerhalb des Kontextes des Kinprozesses ausgeführt wird, und was im Elternprozess. In diversen Tutorien hab ich folgendes gefunden:

    if((pid = fork()) < 0) {
        fprintf(stderr, "Fehler... %s\n", strerror(errno));
    } else if(pid == 0) {
        /* Kindprozess */
    } else {
        /* Elternprozess */
    }
    

    Das ist soweit klar, aber ich habs noch nicht geschafft, das mit 2 Kindprozessen die gleichzeitig laufen sollen hinzubekommen.. Ich habe mal folgendes versucht:

    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    
    int kind();
    
    int main(void)  
    {
            pid_t k1,k2;
            k1 = kind(); //Marke1
            if (k1 < 0) { 
                    printf("FEHLER: Beim Aufruf von kind() ist ein Fehler aufgetreten!\n");
            }
            k2 = kind(); //Marke2
            if (k2 < 0) {
                    printf("FEHLER: Beim Aufruf von kind() ist ein Fehler aufgetreten!\n");
            }
            while (waitpid(k1)==0) { sleep(1); } //Marke3
            while (waitpid(k2)==0) { sleep(1); }
            return 0;
    }
    
    pid_t kind()
    {
            pid_t pid;
            int i;
            switch( pid = fork() ) {
                    case -1:        printf("Fehler beim Forken!\n");
                                    return -1;
                                    break;
                    case 0:         for (i=1;i<=10;i++) {
                                            printf("Ich bin Prozess %d\n",getpid());
                                            sleep(1);
                                    }
                                    break;
                    default:        return pid;
                                    break;
            }
    }
    

    Zum einen wartet der Elternprozess nicht auf das Beenden der Kindprozesse, das hab ich aber noch nicht weiter verfolgt, was mich viel mehr verwirrt ist, das insgesamt 3 Prozesse erzeugt werden, erst Kind 1 und 2 parallel, und danach nochmal Kind 2 (natürlich mit anderer PID). Also scheint es so, dass er nachdem Kind 1 beendet wurde, nochmal zu der Stelle zurückzuspringen, die nach dem ersten Aufruf von kind() schonmal ausgeführt wurde..
    Davon abgesehen, ist das switch-Konstrukt mit den return und break sicher auch nicht die sauberste Lösung..
    Wo kann man mehr über die Arbeitsweise von fork erfahren, bzw. kann jmd. von euch da eine Erklärung für das o.g. Problem liefern oder sagen wie man es besser (bzw. funktionierend) löst?

    Gruss & Danke Maurice



  • Du rufst zweimal fork() auf, also hast du auch zwei Kindprozesse! Das der Eltern-Prozess nicht auf das Beenden des Kind-Prozesses wartet ist gewollt.

    int main ()
    {
        pid_t pid = fork();
        if(pid == 0)
        {
            tu_was_kind();
        }
        else
        {
            tu_was_daddy();
        }
        return 0;
    }
    

    Wenn ich nicht will, dass die Beiden Funktionen gleichzeitig ablaufen brauch ich ja auch kein fork() 😉

    Aja was ich noch sagen wollte: Du rufst zweimal kind() auf, dort rufst du dann jeweils fork() auf. Rufst du zweimal fork() auf hast du am Ende 3 Prozesse: 2 Mal Kind und einmal Vater.

    MfG SideWinder



  • Sorry, hab das vielleicht zu ungenau formuliert. Der Elternprozess soll 2 Kinder erzeugen, die dann jeweils in der Schleife ihre PID sagen und eine Sekunde warten. Der Elternprozess soll sich erst beenden, wenn die Kinder beendet sind, dazu hab ich ja das waitpid eingefügt, mit wait hats allerdings genausowenig geklappt..
    Bei dem Code von mir, wird diese Schleife 3-mal ausgeführt, 2 mal parallel und nach dem Beenden des ersten Kindes wird noch mal der zweite aufruf von kind() ausgeführt. Ich hab hier den Code mit lauter Debug-printf's ausgeführt, aber zugunsten der Lesbarkeit hier wieder entfernt..
    Ich hab oben mal ein paar Markierungen rein editiert, und so sieht der Ablauf aus:
    Marke1
    Marke2
    Marke3 *
    Marke2
    Marke3

    Beim * ist der Elternprozess dann beendet, während die beiden Kinder fröhlich ihre PID schreiben, nach den beiden läuft dasselbe nochmal nur diesmal ohne Marke1 ab..

    Was ich vielleicht auch noch erwähnen sollte, das ganze unter Gentoo Linux (Kernel 2.6.13, gcc 3.3.6, glibc 2.3.5-r2), Gentoo ist ja sourcen basiert, also sollte gcc sowohl vollständig installiert, als auch funktionierend sein..

    Gruss Maurice



  • Du hast nicht die richtigen Header eingebunden.

    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <sys/types.h> // vergessen
    #include <sys/wait.h>  // vergessen
    
    int kind();
    
    int main(void)
    {
            pid_t k1,k2;
            int status1,status2;
            k1 = kind(); //Marke1
            if (k1 < 0) {
                    printf("FEHLER: Beim Aufruf von kind() ist ein Fehler aufgetreten!\n");
            }
            k2 = kind(); //Marke2
            if (k2 < 0) {
                    printf("FEHLER: Beim Aufruf von kind() ist ein Fehler aufgetreten!\n");
            }
            while (waitpid(k1,&status1,WNOHANG)==0) { sleep(1); } //Marke3
            while (waitpid(k2,&status2,WNOHANG)==0) { sleep(1); }
            return 0;
    }
    
    pid_t kind()
    {
            pid_t pid;
            int i;
            switch( pid = fork() ) {
                    case -1:        printf("Fehler beim Forken!\n");
                                    return -1;
                                    break;
                    case 0:         for (i=1;i<=10;i++) {
                                            printf("Ich bin Prozess %d\n",getpid());
                                            sleep(1);
                                    }
                                    break;
                    default:        return pid;
                                    break;
            }
    }
    

    Habs bei mir so getestet und es geht, der Vater wartet bis alle zu Ende sind.



  • Cool danke, jetzt wartet er bis alle Kinder beendet sind.
    Trotzdem führt er immernoch den 2ten kind()-Aufruf doppelt aus, so als ob er nach dem er die Funktion kind() zum ersten Mal aufruft, im Hintergrund den neu geforkten Prozess ausführt, und sofort zu main zurücklehrt, aber nachdem der Prozess beendet ist auch nochmal zur selben Stelle zurückkehrt.
    Ist das Verhalten so richtig oder liegts an mir? Mal allgemeiner gefragt, und auch mal unabhängig von der Uni-Aufgabe: Wie ist es denn überhaupt am saubersten und sinnvollsten Funktionen als eigerner Prozess laufen zu lassen, bzw. in Unterfunktionen einen neuen Prozess zu forken?

    Gruss & Danke Maurice



  • Nach dem ersten fork() laufen sowohl Vater als auch Kind1 an der selben Stelle weiter (direkt bei der Abfrage "if(k1<0)") bzw. der switch() in kind(), also kommen auch beide (das Kind übrigens mit einem undefinierten Rückgabewert) zum Aufruf "k2=kind();" und erzeugen jeder sein eigenes kind2.

    Eventuell solltest du im "case 0:" Zweig ein exit einbauen, um die erzeugten Kinder kontrolliert zu beenden.



  • Danke CStoll, das hat mir den nötigen Durchblick verschafft. Falls jemand mal dasselbe Problem hat, die funtionierende Version sieht nun so aus:

    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <sys/types.h>
    #include <sys/wait.h>
    
    pid_t kind_start();
    void kind_wait(pid_t);
    
    int main(void)
    {
            pid_t k1,k2;
            k1 = kind_start();
            k2 = kind_start();
            kind_wait(k1);
            kind_wait(k2);
            return 0;
    }
    
    pid_t kind_start()
    {
            pid_t pid;
            int i;
            switch( pid = fork() ) {
                    case -1:        printf("Fehler beim Forken!\n");
                                    return -1;
                                    break;
                    case 0:         for (i=1;i<=10;i++) {
                                            printf("Ich bin Prozess %d\n",getpid());
                                            sleep(1);
                                    }
                                    exit(0);
                                    break; 
                    default:        return pid;
                                    break;
            }
    }
    
    void kind_wait(pid_t pid)
    {
            int status;
            while (waitpid(pid,&status,WNOHANG)==0) { sleep(1); }
    }
    

    Danke an alle die geholfen haben!


Anmelden zum Antworten