2017-12-01 22 views
1

Meine erste Stack Overflow Post, ich bin ein lurker auf der Website, aber ich wollte wirklich anfangen, einige Fragen von meinen eigenen zu stellen!C fork() verursacht unerwartete Funktionsaufrufe, die nicht angegeben werden

Ich lerne über den Systemaufruf fork() mit der Bibliothek unistd.h. Ich möchte testen, was ich lerne. Ich habe ein Lehrbuch als Beispiel für eine einfache Prozesserstellung in UNIX verwendet. Ich mache das unter OSX. Mir wurde eine Aufgabe zum Erstellen einer Shell zugewiesen, ich möchte keine vollständige Antwort auf das Erstellen einer Shell erhalten. Daher frage ich nach diesem spezifischen Systemaufruf, nicht nach dem gesamten Programm.

Der Code läuft von selbst ganz gut und gibt die erwartete Ausgabe.

Dies ist der Code funktioniert gut:

#include <stdio.h> 
#include <unistd.h> 

int main() { 
    pid_t pid; 
    pid = fork(); 
    if(pid == 0){ 
     printf("\nI'm the child\n\n"); 
    } 
    else if(pid > 0){ 
     printf("\nI'm the parent, child has pid: [%d]\n", pid); 
    } 
    else{ 
     printf("ERROR"); 
    } 
    return 0; 
} 

Mein Problem ist, dass, wenn ich dieses Programm in meiner Schale steckte es verursacht, von dem, was ich online, eine Gabel Bombe gefunden.

Die Funktion, die diesen Prozess erzeugt, wird mit dem Befehl 'test' aufgerufen. Wenn ich die Funktion ausführe, gibt es die gleiche Ausgabe wie in der Stand-alone-Version, aber wenn ich dann weitergehe und ausschließe, scheint es diese Testfunktion wieder in einer Endlosschleife aufzurufen. Ich kann dann die Prozesse nicht beenden, weil mein Computer zurückbleibt, klar, weil eine Menge Prozesse erstellt werden und ich meinen Computer neu starten muss. Ich habe das jetzt ein paar Mal gemacht und es wird unmöglich, es zu testen, weil ich jedes Mal neu starten muss, wenn ich es starte.

Ich habe eine while-Schleife, die erwartet, dass ein Beenden-Befehl endet. Der Code sollte selbsterklärend sein, siehe unten. Ich weiß, dass dies eine schlechte Übung ist. Ich versuche nur, diese Konzepte zu testen. Ich glaube, ich sollte den Systemaufruf wait() verwenden, aber ich möchte verstehen, warum mein Code das verursacht.

Das volle Programm:

main.c

#include "shell.h" 

int main() { 
    clear(); 
    printf("Jack's Shell\n"); 
    shellLoop(); 
    clear(); 
    return 0; 
} 

shell.c

#include "shell.h" 

void shellLoop(){ 
    char command[MAX_STRING_LEN]; 
    int exit = 1; 
    while (exit != 0){ 
     printf("\njackdewinter$ "); 
     scanf("%s", &command); 
     exit = commandInterpreter(command); 
    } 

} 

int commandInterpreter(char * cmd){ 
    char message[MAX_STRING_LEN]; 
    if(strcmp(cmd, "exit") == 0){ /* Best way to test strings (Not just characters.) */ 
     return 0; 
    } 
    else if(strcmp(cmd, "info") == 0){ 
     info(); 
     return 1; 
    } 
    else if(strcmp(cmd, "pwd") == 0){ 
     shellMessage("Call PWD"); 
     return 1; 
    } 
    else if(strcmp(cmd, "cd") == 0){ 
     shellMessage("Call CD"); 
     return 1; 
    } 
    else if(strcmp(cmd, "test") == 0){ 
     shellMessage("Test Called."); 
     test(); 
     return 1; 
    } 
    else if(strcmp(cmd, "help") == 0){ 
     shellMessage("Call HELP"); 
     return 1; 
    } 
    else{ 
     shellMessage("Incorrect command entered.\nType help for assistance and a list of commands."); 
     return 1; 
    } 
} 

void info(){ 
    shellMessage("This shell was created by Jack Dewinter"); 
} 

void test(){ 
    pid_t pid; 
    pid = fork(); 
    if(pid == 0){ 
     printf("Child\n"); 
    } 
    else if(pid > 0){ 
     printf("I'm the parent, child has pid %d\n", pid); 
    } 
    else{ 
     printf("ERROR"); 
    } 
} 

void shellMessage(char * message){ /* Using this for consistent shell messages. */ 
    printf("\n%s\n", message); 
} 

shell.h

#define MAX_STRING_LEN 80 

#define clear() printf("\033[H\033[J") /* Terminal escape codes for clearing the console */ 

#include <stdio.h> 
#include <string.h> 
#include <stdlib.h> 
#include <unistd.h> 

void shellLoop(); 
int commandInterpreter(char * cmd); 
void info(); 
void test(); 
void shellMessage(char * message); 

Jede Hilfe wäre sehr dankbar, danke!

+1

Dies ist keine Gabel Bombe, es ist nur ein Fehler. Der Child-Prozess sollte 'exit()' wenn fertig sein. I.e. innerhalb des 'if (pid == 0)' Blocks. Schließlich sollte dieser gegabelte Prozess enden. Aus dem, was ich sehe, gibt es keinen Grund für den Elternteil, sich darum zu kümmern, ob/wann er es tut, aber es sollte immer noch, oder es wird auch zu dem Befehlsinterpreter und dem Auswahlmenü in seinem Prozess zurückkehren. – WhozCraig

+0

Sowohl Ihr Kind als auch Ihr Elternteil kehren zu Ihrer Schleife zurück. Kein Ausgang. So, jetzt haben Sie zwei Prozesse. Wenn Sie etwas in stdin eingeben, geht es an das Kind. Aber wenn Kind austritt, läuft der Elternteil noch und wartet darauf, von der Standardeingabe zu lesen. Wenn Sie den Test mehrmals ausführen, warten mehrere Instanzen auf die Eingabe. Ich sehe jedoch keine Schleife, die eine Bombe erzeugt ... – HardcoreHenry

+1

In diesem Code gibt es einen Pufferüberlauf. Sie sollten 'scanf ("% s ", & command)' durch 'scanf ("% 80s ", & command)' ersetzen oder stattdessen 'fgets' verwenden. –

Antwort

6

Wenn Sie einen neuen Prozess mit fork erstellen, wird der untergeordnete Prozess an der Spitze der Verzweigung fortgesetzt. So druckt Ihr Kind "Kind", , aber dann kehrt es von test zurück. Daher wird weiterhin der Code ausgeführt, unter dem nur das übergeordnete Element ausgeführt werden soll.

Sie sehen dies nicht in Ihrem kleineren Code, weil Sie sofort von main zurückkehren, wodurch der untergeordnete Prozess beendet wird.

Sie müssen den Kind-Ausgang haben, nachdem es seinen Teil getan hat. Auch sollte das Elternteil wait für das Kind sein, damit Sie nicht mit Zombie-Prozessen belassen werden.

void test(){ 
    pid_t pid; 
    pid = fork(); 
    if(pid == 0){ 
     printf("Child\n"); 
     _exit(0); // exit child; use _exit instead of exit 
        //to prevent atexit handlers from being called 
    } 
    else if(pid > 0){ 
     printf("I'm the parent, child has pid %d\n", pid); 
     // wait for child to finish 
     wait(NULL); 
    } 
    else{ 
     printf("ERROR"); 
    } 
} 

Auch in shellLoop, benennen Sie die Variable exit auf etwas anderes als die Bibliothek Funktion exit Maskierung sind.

+0

Vielen Dank, das Problem wurde behoben. Mein Computer bricht nicht mehr, wenn ich versuche, das Programm auszuführen, das Warten (NULL) lässt auch die Ausführung zu, sonst bricht es ab. – jackdewinter

+0

Danke auch für den Tipp auf die Exit-Variable, ich habe dies schnell geschrieben, während ich das Buch gelesen habe, ich werde das komplett umschreiben, ich habe viele Leute gesehen, die eine Statusvariable für diese Art von Programmen verwenden. – jackdewinter

3

Das Kind, das Sie erstellen, wird wie sein Vater Schleife, und Sie werden jede Schleife einen neuen Prozess haben. fork: Erstellt das genaue Prozessabbild.

Was können Sie tun, ist: 1- das Kind kommen (dh der Vater wartet auf das Kind bis zum Ende)

2- oder nur einen Ausgang() nach der printf ("Child \ n") hinzufügen ; was das Kind beenden wird und den Vater am Laufen hält.

+0

Thanks für das. Ich verstehe, dass sie beide ab dem Punkt laufen, an dem das Kind erstellt wurde, aber die Schleife hätte die Testfunktion nicht erneut aufrufen sollen, wenn "test" nicht als Befehl eingegeben wurde. Aus irgendeinem Grund wurde diese Funktion wiederholt. – jackdewinter

Verwandte Themen