gpt4 book ai didi

手工制作 shell 的后台问题中的子进程

转载 作者:行者123 更新时间:2023-11-30 15:51:29 25 4
gpt4 key购买 nike

我在实现自己手工制作的 shell 时遇到了一些麻烦。我已经能够 fork 一个进程并使用 waitpid 在前台运行它,但是当我尝试在后台运行简单的进程(例如“sleep 5 &”)时,该进程似乎会永远运行。 checkListJobs 将确定进程是否已完成运行,但它永远不会停止。任何帮助将不胜感激。我假设错误出在我的“foo”函数中。

void insertJob(int pid) {
printf("beginning job %d.\n", pid);
struct job *node = malloc(sizeof(struct job));
node->pid = pid;
node->next = NULL;

if(root == NULL) {
root = node;
} else {
node->next = root;
root = node;
}
}

void checkListJobs(int z) {
curr = root;
while(curr!=NULL) {
if(kill(curr->pid,0) != 0) {
if(prev==NULL) {
prev = curr;
root = curr;
} else {
prev->next = curr->next;
}
} else {
if(!z) printf("%d is still running.\n", curr->pid);
}
prev = curr;
curr = curr->next;
}
}


//code for child forking
void foo(char *cmd, char *argv[], int args) {
int bgFlag;

if(!strcmp(argv[args], "&")){
argv[args] = '\0';
bgFlag = 1;
}

int pid = fork();
int status = 0;

if(pid==0){
if(bgFlag) {
fclose(stdin); // close child's stdin
fopen("/dev/null", "r"); // open a new stdin that is always empty
}
execvp(cmd, argv);
// this should never be reached, unless there is an error
fprintf (stderr, "unknown command: %s\n", cmd);
exit(0);
} else {
if(!bgFlag) {
waitpid(pid, &status, 0);
} else {
insertJob(pid);
}
if (status != 0) {
fprintf (stderr, "error: %s exited with status code %d\n", cmd, status);
} else {
// cmd exec'd successfully
}
}

// this is the parent still, since the child always terminates from exec or exit

// continue being a shell...
}

最佳答案

您需要为 SIGCHLD 安装一个信号处理程序,因为它会告诉您的程序子进程何时完成。收到 SIGCHLD 后,您应该调用 wait()(或 PID 值为 -1 的 waitpid(),因为您不知道哪个子进程完成,只是知道一个子进程已完成)。

编写处理程序的最安全方法是:

volatile sig_atomic_t sigchld;
int handle_child(int sig)
{
if (sig == SIGCHLD)
sigchld = 1;
}

在你的主循环中,检查sigchld是否为1。如果是,则子进程结束,然后你可以调用waidpid()(使用PID 为 -1,因为您不知道哪个子进程在循环中结束(见下文),因为多个子进程可能同时结束。另外,如果任何系统调用返回错误并且 errnoEINTR,则它被信号中断,因此要么返回到主循环的顶部,要么检查 sigchld 并进行相应处理(并且不要忘记尽快将 sigchld 重置回 0)。

for(;;)
{
int status;
pid_t child;

child = waitpid(-1,&status,WNOHANG);
if (child == -1)
{
if (errno == ECHILD) break; /* no more children */
/* error, handle how you wish */
}
/* handle the return status of the child */
}
sigchld = 0;

可以从信号处理程序中调用waitpid()(POSIX 表示这样做是安全的),但您确实不应该做其他任何事情 在信号处理程序中,因为它可能会导致非常微妙的错误(例如,在调用 malloc() 期间引发 SIGCHLD——信号处理程序中导致调用malloc() 会导致非常讨厌的问题。这就是为什么我建议在信号处理程序中设置一个标志——在信号处理程序中做的越少越好) .

关于手工制作 shell 的后台问题中的子进程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15081658/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com