C中的后台作业(在玩具外壳中实现)

Jon*_*ono 1 c c++ c++11

我希望当用户附加一个命令后,它将在后台执行.出于某种原因,如果我正常执行命令它将等待,然后如果我在后台执行命令它将工作,但如果我正常执行命令它将不会等待它.我确信我只是在做一些小错误的事情.有任何想法吗:

void executeSystemCommand(char *strippedCommand, char *background, int argc, char **args) {
    char pathToExecute[80];


    // Check if command will be executed in the background
    int shellArgs;
    bool bg; 
    if (!strcmp(background, "-")) {
        bg = true;
        shellArgs = argc -1; 
    } else {
        bg = false;
        shellArgs = argc;
    }   

    // Save the linux commands in a new array
    char *executableCommands[shellArgs+1];
    int j;
    for (j = 0; j < shellArgs+1; j++) {
        executableCommands[j] = args[j];
    }   
    executableCommands[shellArgs] = NULL;

    // Check the $PATH
    const char delimiters[] = ":";
    char *token, *cp;
    char *forLater;
    int count = 0;
    char *path;
    path = getenv("PATH");

    // All of this just breaks up the path into separate strings
    cp = strdup(path);    
    forLater = strdup(path);
    token = strtok (cp, delimiters); 
    while ((token = strtok (NULL, delimiters)) != NULL) {
        count++;
    }   
    char **argv;
    int size = count+1;
    argv = (char**) malloc (size);
    count = 0;
    token = strtok (forLater, delimiters); 
    argv[0] = (char*) malloc (50);
    argv[0] = token;
    strcpy(argv[0],token);
    while ((token = strtok (NULL, delimiters)) != NULL) {
        count++;
        argv[count] = (char*) malloc (50);
        argv[count] = token;
    }   

    // This goes through the path to see if the linux command they entered
    // Ex: sleep exists in one of those files and saves it to a var
    int i;
    bool weHaveIt = false;
    int ac; 
    for (i = 0; i < count; i++) {
        char str[80];
        strcpy(str, argv[i]);
        strcat(str, "/");
        strcat(str, args[0]);
        ac = access(str, F_OK);
        if (ac == 0) {
            weHaveIt = true;
            strcpy(pathToExecute, str);
            break;
        }
    }

    if (!weHaveIt) {
        printf("That is not a valid command. SORRY!\n");
        return;
    }

    executableCommands[0] = pathToExecute;
    int status;

    // Get the array for 

    // If user wants command to be a background process
    if (bg) {
        int background_process_id;
        pid_t fork_return;
        fork_return = fork();

        if (fork_return == 0) {
            background_process_id = getpid();
            addJobToTable(strippedCommand, background_process_id);
            setpgid(0, 0);
            execve(executableCommands[0], executableCommands, NULL);
            exit(0);
        } else {
            return;
        }
    } else {
        int background_process_id;
        pid_t fork_return;
        fork_return = fork();

        if (fork_return == 0) {
            background_process_id = getpid();
            status = execve(executableCommands[0], executableCommands, NULL);
            exit(0);
        } else {
                wait(&status);
                return;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

Jon*_*oni 5

wait对第三个作业的调用立即返回,因为第二个作业已经完成并且正在等待处理(也称为"僵尸").您可以检查返回值wait(&status),即已退出的进程的PID,并确保它是您正在等待的进程.如果不是,请wait再次打电话.

或者使用waitpid,等待特定的过程:

/* Wait for child. was: wait(&status) */
waitpid(fork_return, &status, 0); 
Run Code Online (Sandbox Code Playgroud)

如果你这样做,你应该实现一个信号处理程序SIGCHLD来处理完成的后台作业,以防止"zombie"子进程的累积.

除此之外,在后台作业的情况下,fork()返回0 的分支你已经在新进程中了,所以调用addJobToTable发生在错误的进程中.此外,您应该检查所有呼叫的返回值; 否则某些事情可能会失败而你却不知道.因此,在后台运行作业的代码应该更像这样:

    if (fork_return == 0) {
        setpgid(0, 0);
        if (execve(executableCommands[0], executableCommands, NULL) == -1) {
            perror("execve");
            exit(1);
        }
    } else if (fork_return != -1) {
        addJobToTable(strippedCommand, fork_return);
        return;
    } else {
        perror("fork"); /* fork failed */
        return;
    }
Run Code Online (Sandbox Code Playgroud)