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

2024-02-28

In this https://stackoverflow.com/questions/873620/how-do-i-clear-this-array-pointer-in-c上一个问题我发布了大部分我自己的 shell 代码。我的下一步是实现前台和后台进程执行,并正确等待它们终止,这样它们就不会保持“僵尸”状态。

在添加在后台运行它们的可能性之前,所有进程都在前台运行。为此,我只是在使用 execvp() 执行任何进程后调用 wait(NULL)。现在,我检查“&”字符作为最后一个参数,如果存在,则通过不调用 wait(NULL) 在后台运行该进程,并且当我返回到 shell 时,该进程可以在后台愉快地运行。

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

我相信我必须处理 SIGCHLD 并在那里做一些事情,但我还没有完全理解何时发送 SIGCHLD 信号,因为我尝试将 wait(NULL) 添加到 childSignalHandler() 但它不起作用,因为一旦我在后台执行一个进程时,会调用 childSignalHandler() 函数,因此会出现 wait(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;
}

有多种选择waitpid()为您提供帮助(引自 POSIX 标准):

W续

waitpid() 函数应报告 pid 指定的任何继续子进程的状态,该子进程的状态自作业控制停止后继续运行以来尚未报告。

WNOHANG

如果 pid 指定的子进程之一的状态不能立即可用,则 waitpid() 函数不应挂起调用线程的执行。

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

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

您可能不想忽略 SIGCHLD 等,并且您的信号处理程序可能应该设置一个标志来告诉您的主循环“哎呀;有死孩子 - 去收集尸体!”。

SIGCONT 和 SIGSTOP 信号也与您相关 - 它们分别用于重新启动和停止子进程(在这种情况下,无论如何)。

我建议您查看 Rochkind 的书或 Stevens 的书 - 他们详细介绍了这些问题。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

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

随机推荐