Systemaufrufe fork, waitpid, execvp
-
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <cstdlib> #include <iostream> #include <unistd.h> #include <sys/wait.h> #include <sys/types.h> void parse(char *line, char **argv) { while (*line != '\0') { while (*line == ' ' || *line == '\t' || *line == '\n'){ *line++ = '\0'; } *argv++ = line; while (*line != '\0' && *line != ' ' && *line != '\t' && *line != '\n'){ line++; } } *argv = '\0'; } void execute(char **argv) { pid_t pid; int status; // Ein Fork darf nicht kleiner 0 sein, sonst schlägt dieser fehl! if ((pid = fork()) < 0) { printf("FEHLER: 'forking' eines Kind-Prozesses fehlgeschlagen!\n"); exit(1); // KIND-PROZESS: // Erst wenn die PID uns eine 0 zurückgibt kommen wir in das Kindprozess!! } else if (pid == 0) { if (execvp(*argv, argv) < 0) { // Zum Hinweis 5: // Falls das COMMAND nicht exisiert kommt es zum ERROR printf("FEHLER: 'exec'!\n"); exit(1); } //ELTERN-PROZESS: //da der Wert nur noch größer 0 sein kann also, PID > 0! } else { while (wait(&status) != pid) // Wartet auf Beendigung des aktuellen Prozesses /* do nothing :))) */ ; } } int main() { char line[1024]; char *argv[64]; while (1) { printf("SHELL-COMMAND: "); gets(line); printf("\n"); parse(line, argv); // Vergleiche ob Eingabe ein Logout ist [Aufgabe 1.4] if (strcmp(argv[0], "logout") == 0){ exit(0); } execute(argv); } return 0; }
Danke für die zahlreichen hilfen :p :p
Wie könnte ich denn eventuell meine "Shell" so erweitern, dass Befehle mit einem & am Ende auch akzeptiert werden, also dass er nicht mehr wartet sondern er fortführt? Aktuell funktioniert dies bei meinem Code noch nicht.. Bin schon wieder mal am zweifeln
-
depream schrieb:
Wie könnte ich denn eventuell meine "Shell" so erweitern, dass Befehle mit einem & am Ende auch akzeptiert werden, also dass er nicht mehr wartet sondern er fortführt? Aktuell funktioniert dies bei meinem Code noch nicht.. Bin schon wieder mal am zweifeln
Wer soll fortführen? Die Frage macht keinen Sinn. Du hast hier doch nur ein Programm, welches einen Prozess spawnt, keine Shell. Das '&' ist etwas spezifisches für Shells, das von der Shell ausgewertet wird und ihr sagt, wie diese ihre Unterprozesse spawnt. Wenn du selber so etwas wie eine Shell programmieren möchtest, dann musst du die Kommandozeile in deinem Programm auswerten und dann entsprechend beim Spawnen deiner Unterprozesse vorgehen; also beispielsweise nicht auf einen Kindprozess warten.
-
SeppJ hat ja bereits deine Kernfrage ja bereits beantwortet, aber noch ein paar Anmerkungen zum Code:
- Die Header in deinem Code sind C/C++-Mischmasch der übleren Sorte: cstdlib+stdlib.h, iostream+stdio.h usw. Bitte entferne die C++-Header, dein Code sieht eher so aus als wolltest du C programmieren.
- gets ist deprecated und gefährlich. Nimm man: getline.
- Bitte verwende für deine Code-Snippets
code="c"
statt nurcode
. - Hast du irgendein C-Grundlagen-Buch? Falls ja lies es, falls nein, hol dir eines.
-
Danke für die Antworten. Werde dieses in Zukunft in acht nehmen, jedoch hat einer meiner Kollegen mir dabei helfen können. Wahrscheinlich lag es eher daran, dass ich mein Problem nicht richtig erläutern konnte.
Hab es jetzt so gemacht und es funktioniert so wie ich es möchte. Hab auch meinen Prof heute gefragt, er meinte dasselbe wie 'nman'. Werde es wie gesagt versuchen in Zukunft so umzusetzen.
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <cstdlib> #include <iostream> #include <unistd.h> #include <sys/wait.h> #include <sys/types.h> using namespace std; void zerlegen(char *line, char **argv) { while (*line != '\0') { while (*line == ' ' || *line == '\t' || *line == '\n') { *line++ = '\0'; } *argv++ = line; while (*line != '\0' && *line != ' ' && *line != '\t' && *line != '\n') { line++; } } *argv = '\0'; } bool aufUNDpruefen(char *argv) { for (int i = 0; i <= sizeof (argv); i++) { if (argv[i] == '&') { // cout << "TRUE" << endl; return true; } } } char* UNDentfernen(char *argv) { for (int i = 0; i <= sizeof (argv); i++) { if (argv[i] == '&') { argv[i] = '\0'; return argv; } } } void ausfuehren(char **argv) { pid_t pid = fork(); int status; bool containsAnd = false; containsAnd = aufUNDpruefen(*argv); if (containsAnd == true) { *argv = UNDentfernen(*argv); } // Ein Fork darf nicht kleiner 0 sein, sonst schlägt dieser fehl! if (pid < 0) { printf("FEHLER: 'forking' eines Kind-Prozesses fehlgeschlagen!\n"); exit(1); // KIND-PROZESS: // Erst wenn die PID uns eine 0 zurückgibt kommen wir in das Kindprozess!! } else if (pid == 0) { if (execvp(*argv, argv) < 0) { // Zum Hinweis 5: // Falls das COMMAND nicht exisiert kommt es zum ERROR printf("FEHLER: 'exec'!\n"); exit(1); } //ELTERN-PROZESS: //da der Wert nur noch größer 0 sein kann also, PID > 0! } else { if (containsAnd == true) { ; } else { while (wait(&status) != pid) // Wartet auf Beendigung des aktuellen Prozesses /* do nothing :))) */; } } } int main() { char line[1024]; char *argv[64]; while (1) { printf("SHELL-COMMAND: "); gets(line); printf("\n"); zerlegen(line, argv); // Vergleiche ob Eingabe ein Logout ist [Aufgabe 1.4] if (strcmp(argv[0], "logout") == 0) { exit(0); } ausfuehren(argv); } return 0; } //fgets od. getline
So sieht mein fertiger Code nun aus. Ich werde wohl möglich noch weitere Fragen haben, DANKE nochmals an alle.
Ansonsten hätte sich dieses Thema erledigt.
-
Bei mir ist eben noch eine kleine Frage aufgefallen:
Durch den Befehl "ps ax" kann man ja die Prozesse sehen, jedoch gibt meiner "leider" keine Zombie's zurück. Was habe ich denn "falsch" programmiert, dass es nicht erscheint.
$ firefox Ctrl+C // firefox wird unterbrochen $ ps ax .... [kein Z für Zombie und firefox wird auch nicht angezeigt]
-
Damit das zu einem Zombie wird, dürfte der Mutterprozess nicht auf den abgebrochenen Kindprozess warten.
-
SeppJ schrieb:
Damit das zu einem Zombie wird, dürfte der Mutterprozess nicht auf den abgebrochenen Kindprozess warten.
Ich habe den "Eltern-Prozess" komplett weggelassen und es kommt ständig zu Fehlern. Etwas läuft nicht so wie ich es geplant habe.
Könntest du mir eventuell mit einem Code helfen?
-
depream schrieb:
Ich habe den "Eltern-Prozess" komplett weggelassen
Dann kann es natürlich auch keine Zombies geben.
Könntest du mir eventuell mit einem Code helfen?
Das hier sollte dir einen Zombieprozess erzeugen:
#include <stdlib.h> #include <sys/types.h> #include <unistd.h> int main () { pid_t child_pid; child_pid = fork (); if (child_pid > 0) { sleep (60); } else { exit (0); } return 0; }
-
Liege ich bei der Aufgabe eigentlich in der richtigen Spur?
http://abload.de/img/unbenanntrrsk5.jpg
#include <string.h> #include <cstdlib> #include <iostream> #include <unistd.h> #include <sys/wait.h> #include <sys/types.h> #include <signal.h> // für SIGINT using namespace std; pid_t childpid = 0; // SIGINT (Ctrl+C) = Unterbrechung eines Prozesses // Beispiel: firefox -> Ctrl+C unterbricht den Prozess! void signalhandler_SIGINT(int signum) { cout << endl; cout << "Ein SIGINT wurde erkannt!\n"; } // SIGTSTP (Ctrl+Z) = Anhalten eines Prozesses void signalhandler_SIGTSTP(int signum) { // if (childpid!=0){ // vpid.push_back(childpid); // cout << "Stopped "<< child // pid<<"\n"; // kill(childpid,SIGTSTP); // childpid=0; // } cout << "\nCaught SIGTSTP\n"; } // Bringt einen "angehaltenen Prozess" durch 'Ctrl+C' in den Vordergrund! void fg() { ; } // Zerlegt die "Getline" void zerlegen(char *line, char **argv) { while (*line != '\0') { while (*line == ' ' || *line == '\t' || *line == '\n') { *line++ = '\0'; } *argv++ = line; while (*line != '\0' && *line != ' ' && *line != '\t' && *line != '\n') { line++; } } *argv = '\0'; } // Prüft ob ein &-Symbol im Char enthalten ist bool aufUNDpruefen(char *argv) { for (int i = 0; i <= sizeof (argv); i++) { if (argv == '&') { return true; } } } // Falls ein &-Zeichen vorhanden ist wird dieser gelöscht char* UNDentfernen(char *argv) { for (int i = 0; i <= sizeof (argv); i++) { if (argv[i] == '&') { argv[i] = '\0'; return argv; } } } void ausfuehren(char **argv) { pid_t pid = fork(); bool containsAnd = false; containsAnd = aufUNDpruefen(*argv); if (containsAnd == true) { *argv = UNDentfernen(*argv); } int status; // Ein Fork darf nicht kleiner 0 sein, sonst schlägt dieser fehl! if (pid < 0) { cout << "FEHLER: 'forking' eines Kind-Prozesses fehlgeschlagen!\n"; exit(1); // KIND-PROZESS: // Erst wenn die PID uns eine 0 zurückgibt kommen wir in das Kindprozess!! } else if (pid == 0) { if (execvp(*argv, argv) < 0) { // Zum Hinweis 5: // Falls das COMMAND nicht exisiert kommt es zum ERROR cout << "FEHLER: 'exec'!\n"; exit(1); } //ELTERN-PROZESS: //da der Wert nur noch größer 0 sein kann also, PID > 0! } else { if (containsAnd == true) { ; } while (waitpid(pid, &status, WNOHANG) != pid) { // Wartet auf Beendigung des aktuellen Prozesses ; } } } int main() { signal(SIGINT, signalhandler_SIGINT); signal(SIGTSTP, signalhandler_SIGTSTP); cout << "Signal Handler Installed\n"; char line[1024]; char *argv[64]; // if (signal(SIGCHLD, SIG_IGN) == SIG_ERR) { // exit(1); // } while (1) { cout << "SHELL-COMMAND: "; cin.getline(line, 1024); cout << "\n"; zerlegen(line, argv); // Vergleiche ob Eingabe ein Logout ist [Aufgabe 1.4] if (strcmp(argv[0], "logout") == 0) { exit(0); } ausfuehren(argv); } return 0; }
Problem:
[i]Ctrl+C* "bricht" den aktuellen Prozess ab, aber bei ping google.de funktioniert es nicht. Funktioniert aktuell nur bei firefox und bestimmt bei anderen Befehlen. Ctrl+Z sollte auch funktionieren, nur weil mein Elternprozess auf das Beenden des Kindprozesses wartet kann ich es auch nicht weiter-testen. Das ich etwas falsch mache ist mir bewusstBin leider schon verzweifelt...
-
depream schrieb:
Liege ich bei der Aufgabe eigentlich in der richtigen Spur?
Bis jetzt ist es ja nur ein leeres Gerüst.
Was mir vor allem fehlt, ist erst einmal eine vernünftige Verwaltung der Prozesse. Du musst die Daten der Prozesse irgendwo speichern, sonst kannst du sie schließlich nirgendwo verarbeiten. Es könnte nötig sein, dass du dein Programm dafür vollkommen umbauen musst.