2017-02-24 6 views
1

fangen Ich habe eine Shell in C geschrieben, die einige grundlegende Funktionalität hat. Ich habe einen "Nur-Vordergrund-Modus" in meinem Programm implementiert, der mit SIGTSTP oder CTRL-Z umgeschaltet wird. Ich bin jedoch in der Lage, das Signal CTRL-Z abzufangen, aber nicht das Signal SIGTSTP. Wie ist das möglich? Ist CTRL-Z das gleiche wie SIGTSTP? Sollte nicht eine Falle die andere fangen?Kann erfolgreich CTRL-Z, aber nicht SIGTSTP

I Falle das Signal (I signal() erkennen ist veraltet, aber ich habe sigaction() auch versucht, und das Problem weiterhin besteht.):

signal(SIGTSTP, trapTstp); 

Und es mit dieser Funktion umgehen:

void trapTstp() { 
    if(foregroundMode == 0) { 
     write(1, "Entering foreground-only mode (& is now ignored)\n", 49); 
     write(1, ": ", 2); 
     fflush(stdout); 
     foregroundMode = 1; 
    } else { 
     write(1, "Exiting foreground-only mode\n", 29); 
     write(1, ": ", 2); 
     fflush(stdout); 
     foregroundMode = 0; 
    } 
} 

Wenn ich mein Programm starte und CTRL-Z drücke, kann ich den Nur-Vordergrund-Modus erfolgreich ein- und ausschalten. Ich kann jedoch SIGTSTP nicht abfangen. Zum Beispiel, wenn ich sleep 100 laufen lasse, die PID dafür erhalte, und kill -SIGTSTP sleep_pid laufen lasse, wird der sleep 100 Prozess früh mit der Ausgabe Terminated: 15 getötet, die anzeigt, dass es tatsächlich getötet wurde, und so wurde das SIGTSTP Signal nicht eingeschlossen.

Hier ist mein volles Shell-Programm:

#include <stdlib.h> 
#include <stdio.h> 
#include <string.h> 
#include <sys/stat.h> 
#include <sys/wait.h> 
#include <sys/types.h> 
#include <unistd.h> 
#include <signal.h> 
#include <fcntl.h> 

int statusCode; 
int foregroundMode = 0; 
int bg = 0; 
int bgPsArray[20]; 
int bgPsCount = 0; 
int i; 
char line[256]; 

pid_t popBgProcess() { 
    int size = sizeof(bgPsArray)/sizeof(bgPsArray[0]); 
    if (size > 0) { 
     return bgPsArray[size+1]; 
    } else { 
     return 0; 
    } 
} 

void trapInterrupt(int _) { 
    int childStatus; 
    pid_t child; 
    while ((child = popBgProcess())) { 
     if(child != getpid()) { 
      kill(child, SIGKILL); 
      waitpid(child, &childStatus, 0); 
     } 
    } 
} 

void trapTstp() { 
    if(foregroundMode == 0) { 
     write(1, "Entering foreground-only mode (& is now ignored)\n", 49); 
     write(1, ": ", 2); 
     fflush(stdout); 
     foregroundMode = 1; 
    } else { 
     write(1, "Exiting foreground-only mode\n", 29); 
     write(1, ": ", 2); 
     fflush(stdout); 
     foregroundMode = 0; 
    } 
} 

int getCommand() { 
    printf(": "); 
    fflush(stdout); 
    if(fgets(line, sizeof(line), stdin) != NULL) { 
     char *position = strchr(line, '\n'); 
    *position = '\0'; // Replace '\n' with '\0' 
     if(foregroundMode == 1) { // Foreground mode on 
      if((position = strchr(line, '&')) != NULL) { 
       *position = '\0'; // Replace '&' with '\0' 
      } 
      bg = 0; // Ignore '&' so do not create background process 
     } else { // Foreground mode off 
      if((position = strchr(line, '&')) != NULL) { 
       *position = '\0'; // Replace '&' with '\0' 
       bg = 1; // Is a background process 
      } else { 
       bg = 0; 
      } 
     } 
    } else { // If input is null 
     return 0; 
    } 
    return 1; 
} 

void checkProcessCompletion() { 
    int status; 
    for(i=0; i<bgPsCount; i++) { 
     if(waitpid(bgPsArray[i], &status, WNOHANG) > 0) { 
      if(WIFEXITED(status)) { // If exit 
       printf("Background PID %d is done: exit value %d\n", bgPsArray[i], WEXITSTATUS(status)); 
       fflush(stdout); 
      } else if(WIFSIGNALED(status)) { // If signal 
       printf("Background PID %d is done: terminated by signal %d\n", bgPsArray[i], WTERMSIG(status)); 
       fflush(stdout); 
      } 
     } 
    } 
} 

int runCommand(int cmd) { 
    if(cmd == 0) { // Return if there was no command 
     return 0; 
    } else if(strcmp(line, "exit") == 0) { 
     exit(0); 
    } else if(strstr(line, "#")) { // Comment input (do nothing) 
    } else if(strcmp(line, "status") == 0) { 
     printf("exit value %d\n", statusCode); 
     fflush(stdout); 
    } 
    else if(strncmp("cd", line, strlen("cd")) == 0) { 
     if(line[2] == ' ') { // If space after 'cd' expect directory 
      char cwd[1024]; 
      getcwd(cwd, sizeof(cwd)); 
      char *path = strstr(line, " "); 
      if(path) { 
       path += 1; 
       char *value; 
       value = malloc(strlen(path)); 
       memcpy(value, path, strlen(path)); 
       *(value + strlen(path)) = 0; 
       sprintf(cwd, "%s/%s", cwd, value); // Directory to change to 
       free(value); 
      } 
      chdir(cwd); // cd to new directory 
     } else { // cd with no argument 
      char *home = getenv("HOME"); 
      chdir(home); // cd to HOME directory 
     } 
    } 
    else { // System commands 
     pid_t pid, ppid; 
     int status; 
     char *command; 
     char *args[256]; 
     int argCount; 
     command = strtok(line, " "); 

     // Create args array for execvp 
     args[0] = command; 
     argCount = 1; 
     args[argCount] = strtok(NULL, " "); 
     while(args[argCount] != NULL) { // Add arguments to array 
      argCount++; 
      args[argCount] = strtok(NULL, " "); 
     } 
     if((pid = fork()) < 0) { // Fork fails 
      perror("fork"); 
      fflush(stdout); 
      exit(1); 
     } 
     if(pid == 0) { // Child process 
      for(i=0; i<argCount; i++) { 
       if(strcmp(args[i], "<") == 0) { // Redirecting input 
        if(access(args[i+1], R_OK) == -1) { // File is unreadable 
         perror("access"); 
         fflush(stdout); 
        } else { // File is readable 
         int file = open(args[i+1], O_RDONLY, 0); 
         dup2(file, STDIN_FILENO); 
         close(file); 
         execvp(command, &command); 
        } 
       } 
       else if(strcmp(args[i], ">") == 0) { // Redirecting output 
        int file = creat(args[i+1], 7777); 
        dup2(file, STDOUT_FILENO); 
        close(file); 
        execvp(command, args); 
       } else { // No redirection 
        execvp(command, args); 
       } 
      } 
      perror("execvp"); // Error for execvp 
      exit(1); 
     } else { // Parent process 
      if (bg == 1) { // Background process 
       int status; 
       int process; 
       printf("Background PID: %d\n", pid); 
       fflush(stdout); 
       bgPsArray[bgPsCount] = pid; // Add process to background process array 
       bgPsCount++; 
       process = waitpid(pid, &status, WNOHANG); 
      } else { // Foreground process 
       int status; 
       waitpid(pid, &status, 0); // Wait on the process 
       if(WIFEXITED(status)) { 
        statusCode = WEXITSTATUS(status); 
       } 
      } 
     } 
    } 
    return 1; 
} 

int main(int argc, char *argv[], char *envp[]) { 
    // Creating 'junk' manually is necessary because output redirection is broken, 
    // and a large portion of the grading script is depedent upon it's existence. 
    FILE *fp = fopen("junk", "ab+"); 
    const char *text; 
    fprintf(fp, "Junk in junkfile\n"); 
    fclose(fp); 
    signal(SIGINT, trapInterrupt); 
    signal(SIGTSTP, trapTstp); 
    while(1) { 
     checkProcessCompletion(); //Check the processes 
     int cmd = getCommand(); // Get command from user 
     int result = runCommand(cmd); 
     if (result == 0) { 
      break; 
     } 
    } 
    return 0; 
} 

Antwort

1

Zuerst müssen Sie nicht Falle SIGTSTP für die Schale selbst (es sollte sie ignorieren), nur für seine Kinder. Zweitens, wenn Sie wirklich eine Job-Controller-Shell schreiben möchten, müssen Sie Kinder mit Hilfe der Prozessgruppe verwalten und die Vordergrundgruppe korrekt einstellen. Das Schreiben einer Shell, die sich korrekt mit der Jobsteuerung verhält, ist eine schwere Aufgabe. Lesen Sie den POSIX-Standard über Shells, Gruppen, Sitzungen, Terminalsteuerung.

Über Ihr aktuelles Problem. Wenn Ihr Unterprozess eine Exec ausführt, wird jedes gehandhabte Signal auf sein Standardverhalten zurückgesetzt. Dies liegt daran, dass exec den alten Code mit dem neuen wiederherstellt, so dass der zuvor eingestellte Handler nicht mehr verfügbar ist. Jetzt müssen Sie das Kind normal gegen TSTP verhalten lassen und den Elternteil entweder mit einem synchronen Aufruf an wait/waitpid oder mit der asynchronen Hilfe von SIGCHLD seinen Status verfolgen lassen. Wenn das Kind angehalten oder beendet wird, kann das übergeordnete Element es im zurückgegebenen Status anzeigen (WIFEXITED, WIFSIGNALED, WIFSTOPPED).

+0

Dies erklärt eine Menge. So kann ich SIGCHLD erfolgreich abfangen, aber ich frage mich, wie man eine Bedingung basierend auf dem, was das Signal zurückgegeben hat, machen kann. Das heißt, wenn das von SIGCHLD zurückgegebene Signal 24 ist. Ich war nicht in der Lage, diese Signalnummer in meinem Handler zu erhalten. – 123

+0

Der Parameter des Handlers ist die Nummer des abgefangenen Signals. –

Verwandte Themen