如何在C中正确等待我自己的shell中的前台/后台进程?

Ric*_*ral 8 c signals process background-foreground

之前的问题,我最贴出我自己的shell代码.我的下一步是实现前台和后台进程执行并正确等待它们终止,这样它们就不会像"僵尸"那样停留.

在添加在后台运行它们的可能性之前,所有进程都在前台运行.为此,我在使用execvp()执行任何进程后简单地调用wait(NULL).现在,我检查'&'字符作为最后一个参数,如果它在那里,通过不调用wait(NULL)在后台运行该进程,并且进程可以在后台运行愉快我将返回到我的shell.

这一切都正常(我认为),现在的问题是,我还需要以某种方式调用wait()(或waitpid()?),以便后台进程不会保持"僵尸".这是我的问题,我不知道该怎么做......

我相信我必须处理SIGCHLD并在那里做一些事情,但是我还没有完全理解何时发送SIGCHLD信号,因为我还试图将wait(NULL)添加到childSignalHandler()但是它不起作用因为我一旦在后台执行了一个进程,调用了childSignalHandler()函数,因此等待(NULL),这意味着在"后台"进程完成之前我无法对我的shell做任何事情.由于信号处理程序中的等待,它不再在后台运行.

我在这一切中缺少什么?

最后一件事,这个练习的一部分我还需要打印进程状态的变化,比如进程终止.因此,对此的任何见解也非常感激.

这是我目前的完整代码:

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

#include "data.h" // Boolean typedef and true/false macros


void childSignalHandler(int signum) {
    //
}

int main(int argc, char **argv) {
    char bBuffer[BUFSIZ], *pArgs[10], *aPtr = NULL, *sPtr;
    bool background;
    ssize_t rBytes;
    int aCount;
    pid_t pid;

    //signal(SIGINT, SIG_IGN);

    signal(SIGCHLD, childSignalHandler);

    while(1) {
        write(1, "\e[1;31mmyBash \e[1;32m# \e[0m", 27);
        rBytes = read(0, bBuffer, BUFSIZ-1);

        if(rBytes == -1) {
            perror("read");
            exit(1);
        }

        bBuffer[rBytes-1] = '\0';

        if(!strcasecmp(bBuffer, "exit")) {
            exit(0);
        }

        sPtr = bBuffer;
        aCount = 0;

        do {
            aPtr = strsep(&sPtr, " ");
            pArgs[aCount++] = aPtr;
        } while(aPtr);

        background = FALSE;

        if(!strcmp(pArgs[aCount-2], "&")) {
            pArgs[aCount-2] = NULL;
            background = TRUE;
        }

        if(strlen(pArgs[0]) > 1) {
            pid = fork();

            if(pid == -1) {
                perror("fork");
                exit(1);
            }

            if(pid == 0) {
                execvp(pArgs[0], pArgs);
                exit(0);
            }

            if(!background) {
                wait(NULL);
            }
        }
    }

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

Jon*_*ler 5

有多种方法waitpid()可以帮助您(POSIX标准的引用):

WCONTINUED

waitpid()函数应报告由pid指定的任何继续子进程的状态,该进程的状态自作业控制停止后继续报告.

WNOHANG

如果状态不能立即用于由pid指定的其中一个子进程,则waitpid()函数不应挂起调用线程的执行.

特别是,WNOHANG将允许您查看是否有任何尸体可以收集而不会导致您的进程阻止等待尸体.

如果调用进程设置了SA_NOCLDWAIT或SIGCHLD设置为SIG_IGN,并且进程没有被转换为僵尸进程的未被等待的子进程,则调用线程将阻塞,直到包含调用线程的进程的所有子进程终止,并且wait()和waitpid()将失败并将errno设置为[ECHILD].

你可能不想忽略SIGCHLD等,你的信号处理程序应该设置一个标志来告诉你的主循环"糟糕;有死孩子 - 去收集那个尸体!".

SIGCONT和SIGSTOP信号也将与您相关 - 它们分别用于重新启动和停止子进程(在此上下文中,无论如何).

我建议看看Rochkind的书或史蒂文斯的书 - 他们详细介绍了这些问题.