简体   繁体   English

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

[英]Background Jobs in C (implementing & in a toy shell)

I want to make it so when a user attaches a - after a command it will be executed in the background. 我想这样做,以便当用户附加-命令后将在后台执行。 For some reason if I execute a command normally it will wait, then if I execute a command in the background it will work but then if I execute a command normally it won't wait for it. 由于某种原因,如果我正常执行命令将等待,然后在后台执行命令将起作用,但是如果我正常执行命令将不会等待。 I am sure I am just doing something small-ish wrong. 我确定我只是在做些小错误。 Any ideas: 有任何想法吗:

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

The call to wait made for the third job returns immediately because the second job has finished and is waiting to be handled (also called "zombie"). 因为第二个作业已经完成并且正在等待处理,所以第三个作业的wait调用立即返回(也称为“僵尸”)。 You could check the return value of wait(&status) , which is the PID of the process that has exited, and make sure it is the process you were waiting for. 您可以检查wait(&status)的返回值,该返回值是已退出的进程的PID,并确保它是您正在等待的进程。 If it's not, just call wait again. 如果不是,请再次致电wait

Alternatively use waitpid , which waits for a specific process: 或者使用waitpid ,它等待特定的过程:

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

If you do this you should implement a signal handler for SIGCHLD to handle finished background jobs to prevent the accumulation of "zombie" child processes. 如果这样做,则应为SIGCHLD实现一个信号处理程序,以处理完成的后台作业,以防止“僵尸”子进程的累积。

In addition to that, in the background job case, the branch where fork() returns 0 you are already in the new process, so the call to addJobToTable happens in the wrong process. 除此之外,在后台作业情况下, fork()返回0的分支已经在新进程中,因此对addJobToTable的调用发生在错误的进程中。 Also, you should check the return values of all the calls; 另外,您应该检查所有调用的返回值。 otherwise something may be failing and you don't know it. 否则,某些事情可能会失败,而您却一无所知。 So the code for running a job in the background should look more like this: 因此,用于在后台运行作业的代码应如下所示:

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

Every child process created with fork() will exit when the parent process exits. 当父进程退出时,用fork()创建的每个子进程都将退出。

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

Explanation 说明

fork spawns a new process as a child process of the current process (parent). fork产生一个新进程作为当前进程(父进程)的子进程。 Whenever a process in Unix-like operating systems terminates all of its child processes are going to be terminated too. 每当类Unix操作系统中的进程终止时,其所有子进程也将终止。 If they have child processes on their own, then these will get terminated too. 如果他们自己拥有子进程,那么这些子进程也将被终止。

Solution

On most shells you can start a process in background if you add an ampersand & to the end of the line: 在大多数shell上,如果在行末添加&符&则可以在后台启动进程:

myApplication arg1 arg2 arg3 ... argN &

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM