如何在C中正确等待我自己的外壳中的前景/背景过程?[英] How to properly wait for foreground/background processes in my own shell in C?

本文是小编为大家收集整理的关于如何在C中正确等待我自己的外壳中的前景/背景过程?的处理方法,想解了如何在C中正确等待我自己的外壳中的前景/背景过程?的问题怎么解决?如何在C中正确等待我自己的外壳中的前景/背景过程?问题的解决办法?那么可以参考本文帮助大家快速定位并解决问题。

问题描述

in this 我发布了最多的问题我自己的外壳代码.我的下一步是实现前景和背景过程执行,并正确等待它们终止,以免它们作为"僵尸".

在添加在后台运行它们的可能性之前,所有过程都在前景中运行.为此,我只是在用execvp()执行任何进程后才致电Wait(null).现在,我检查了最后一个参数的"&"字符,如果它在那里,请通过不调用Wait(NULL)在后台运行该过程,并且该过程可以在后台愉快地运行,我将返回我的外壳.

这一切都正常工作(我认为)现在的问题是,我还需要以某种方式致电wait()()()()()?),以便背景过程不会保留"僵尸".那是我的问题,我不确定该怎么做...

我相信我必须处理sigchld并在那里做某事,但是我还没有完全理解何时发送sigchld信号,因为我也尝试将wait(null)添加到childsignalhandler(),但它不起作用,因为当我在后台执行一个过程时,调用了ChildSignalHandler()函数,因此,等待(null),这意味着我在"背景"过程完成之前对我无法做任何事情.由于信号处理程序的等待,它不再在后台运行.

我在这一切中缺少什么?

最后一件事,本练习的一部分我还需要打印流程状态的更改,例如过程终止.因此,对此的任何见解也非常感谢.

这是我目前的完整代码:

#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标准的报价):

wcontinued

WaitPID()函数应报告PID指定的任何持续儿童进程的状态,其状态尚未报告以来该状态从工作控制停止.

.

wnohang

如果未立即使用PID指定的子进程之一.

,waitpid()函数不得暂停调用线程执行.

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

如果通话过程具有SA_NOCLDWAIT集或SIGCHLD设置为SIG_IGN,并且该过程没有未经涉及的儿童转换为僵尸过程,则该调用线程应阻止,直到所有包含调用线程的过程中的所有孩子终止,wait()和waitpid()将失败并将errno设置为[echild].

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

Sigcont和Sigstop信号也将与您相关 - 它们分别用于重新启动和停止儿童过程(无论如何,在这种情况下,无论如何).

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

其他推荐答案

这应该让您入门.主要区别在于,我摆脱了儿童处理程序,并在主循环中添加了一些反馈.测试和工作,但显然需要更多的TLC.

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

int main(int argc, char **argv) {
        char bBuffer[BUFSIZ], *pArgs[10], *aPtr = NULL, *sPtr;
        int background;
        ssize_t rBytes;
        int aCount;
        pid_t pid;
        int status;
        while(1) {
                pid = waitpid(-1, &status, WNOHANG);
                if (pid > 0)
                        printf("waitpid reaped child pid %d\n", pid);
                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 = (strcmp(pArgs[aCount-2], "&") == 0);
                if (background)
                        pArgs[aCount-2] = NULL;
                if (strlen(pArgs[0]) > 1) {
                        pid = fork();
                        if (pid == -1) {
                                perror("fork");
                                exit(1);
                        } else if (pid == 0) {
                                execvp(pArgs[0], pArgs);
                                exit(1);
                        } else if (!background) {
                                pid = waitpid(pid, &status, 0);
                                if (pid > 0)
                                        printf("waitpid reaped child pid %d\n", pid);
                        }
                }
        }
        return 0;
}

编辑:使用wnohang使用waitpid()添加信号处理并不困难.这就像将waitpid()的东西从循环顶部移动到信号处理程序一样简单.但是,您应该知道两件事:

首先,即使是"前景"过程也会发送sigchld.由于只能有一个前景过程,您可以简单地将前景PID存储在信号处理程序可见的变量中,如果您想对前景进行特殊处理与背景.

.

.

第二,您当前正在对标准输入(主循环顶部的read())进行阻止I/O.当Sigchld发生时,您极有可能在read()上被阻止,从而导致系统呼叫中断.根据OS,它可能会自动重新启动系统调用,或者可能会发送您必须处理的信号.

其他推荐答案

您可以使用:

if(!background)
    pause();

这样,该过程会阻止其接收SIGCHLD信号,并且信号处理程序将执行等待工作.

本文地址:https://www.itbaoku.cn/post/359234.html