1

我想这样做,当用户在命令之后附加 - 时,它将在后台执行。出于某种原因,如果我正常执行命令它会等待,然后如果我在后台执行命令它将起作用,但是如果我正常执行命令它不会等待它。我确定我只是在做一些小错误。有任何想法吗:

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;
        }
    }
}
4

2 回答 2

5

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

或者使用waitpid,它等待一个特定的进程:

/* Wait for child. was: wait(&status) */
waitpid(fork_return, &status, 0); 

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

除此之外,在后台作业案例中,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;
    }
于 2012-10-17T07:18:18.367 回答
1

创建的每个子进程fork()都会在父进程退出时退出。

if (fork_return == 0) {
   /* child process, do stuff */
} else {
   /* parent process, exit immediately */
   return;
}

解释

fork生成一个新进程作为当前进程(父进程)的子进程。每当类 Unix 操作系统中的进程终止时,它的所有子进程也将被终止。如果他们自己有子进程,那么这些也会被终止。

解决方案

在大多数 shell 上,如果您&在行尾添加一个 & 符号,您可以在后台启动一个进程:

我的应用程序 arg1 arg2 arg3 ... argN &
于 2012-10-17T05:39:14.313 回答