问题描述
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信号,并且信号处理程序将执行等待工作.