【问题标题】:Regarding background processes using fork() and child processes in my dummy shell关于在我的虚拟外壳中使用 fork() 和子进程的后台进程
【发布时间】:2012-01-09 07:22:56
【问题描述】:

我正在尝试用 C 创建一个简单的 shell 程序。我需要它为用户提供一个提示,让他们可以在其中运行其他本地程序。我可以很好地完成这部分,使用一个 fork(),其中父进程 waits() 在子进程上,子进程 execvp() 是程序。

但是,如果 '&' 字符附加到用户命令的末尾,我需要他们的程序在后台运行,这意味着我需要父进程不要等待子进程,而是立即返回提示用户,同时允许后台进程继续运行,但不允许它在屏幕上显示任何内容。我只是希望能够通过 ps 命令检查它是否仍然存在。

我试图理解使用 fork() 创建子进程背后的想法,然后让子进程 fork() 再次创建各种孙子进程,然后立即 exit()-ing 子进程。即,使孙子成为孤儿。假设这允许父母仍然等待孩子,但由于孩子几乎立即有效地结束,就像它根本不等待?关于僵尸疯狂的事情?我不知道。我遇到的几个站点似乎都建议将此作为在后台运行进程的一种方式。

但是,当我尝试执行此操作时,程序流程中发生了一些疯狂的事情,“后台”进程继续在屏幕上显示输入,我真的不知道从哪里开始。

这是我的代码实现,我敢肯定这是完全错误的。我只是想知道这整个孙子的事情是否甚至是我需要采取的路线,如果是这样,我的代码有什么问题?

36 int main(int argc, char *argv[])
37 {
38     char buffer[512];
39     char *args[16];
40     int background;
41     int *status;
42     size_t arg_ct;
43     pid_t pid;
44 
45     while(1)
46     {
47         printf("> ");
48         fgets(buffer, 512, stdin);
49         parse_args(buffer, args, 16, &arg_ct);
50 
51         if (arg_ct == 0) continue;
52 
53         if (!strcmp(args[0], "exit"))
54         {
55             exit(0);
56         }
57 
58         pid = fork();  //here I fork and create a child process
61 
62         if (pid && !background)  //i.e. if it's the parent process and don't need to run in the background (this is the part that seems to work)
63         {
64             printf("Waiting on child (%d).\n", pid);
65             pid = wait(status);
66             printf("Child (%d) finished.\n", pid);
67         }
68         else
69         {
70             if (background && pid == 0) { //if it's a child process and i need to run the command in background
71                
72                 pid = fork(); //fork child and create a grandchild
73                 if (pid) exit(0); //exit child and orphan grandchild
74                 
75                 execvp(args[0], args); //orphan execs the command then exits
76                 exit(1);
77 
78             } else exit(0);
79         }
80     }
81     return 0;
82 }

P.S. 明确地说,我需要我在后台运行的进程不再发出声音,即使它有无限循环的打印语句或其他东西。我只是想确保它仍然通过 ps -a 或其他方式在后台运行。

对不起,我只是不知道如何更好地解释它。

提前致谢

P.P.S我将实现它,以便每个后续命令都将确定“背景”的布尔值,抱歉造成混淆

【问题讨论】:

  • 子进程可能应该关闭所有文件描述符。
  • @tlw11591 后台进程并不意味着他们不能在标准输出中打印他们的输出。这意味着它不会阻止您执行新命令。

标签: c unix fork grandchild


【解决方案1】:

您不想在 shell 中使用 double-fork() 方法 - 这是为了编写特别想逃避运行它们的 shell 监督的守护程序。

相反,在现有 shell 中复制 & 行为的方法是调用 fork(),然后在子进程中调用 setpgid(0, 0); 以将子进程放入一个新的进程组。父母只是继续(也许在打印孩子的PID 之后) - 它不会调用wait()

每个终端只能有一个前台进程组,即当前允许向终端发送输出的进程组。这将保留您的 shell 所属的进程组,因此您的 shell 将保留在前台。

如果后台进程组试图从终端读取,它将被发送一个信号来停止它。仍然允许输出到终端 - 这是正常的(在您的常规 shell 中尝试ls &)。要在您的 shell 中实现 fg 命令,您将需要 tcsetpgrp() 函数来更改前台进程组。

您还需要定期使用WNOHANG 选项调用waitpid() - 例如,在您显示shell 提示符之前。这将允许您检测后台进程何时退出或停止(或者,您可以处理SIGCHLD 信号)。

【讨论】:

  • 好的,我想我已经遵循了所有这些。不过,它向我提出了一个问题。你说“仍然允许输出到终端 - 这是正常的(在你的常规 shell 中尝试 ls & )。”这是有道理的(我确实在我的常规 bash 中尝试过,我明白你在说什么。)但是,有没有办法阻止这种情况发生?或者这就是你对其余评论的看法?感谢您的回复。
  • @Cthing_Mad:可以用tcsetattr()设置控制终端的TOSTOP属性。
  • 好的,我明白你在说什么。我不需要实现 fg 功能,而且我不确定停止 bg 进程的输出。从您所指的那些函数以及使用 & 操作数时其他 shell 的行为来看,我猜我们不必担心它。无论如何,就使用 a 的混淆而言双叉,你的回答非常有帮助。非常感谢!
  • 在父级中使用带有 WNOHANG 选项的 waitpid 不会挂起父级。相反,它会在孩子完成后返回,对吗?通过使用它,我们可以将父级移回前台。这是为了如果最后有一个“&”。 @caf
  • @ajfbiw.s:如果命令末尾有一个&,则父级将留在前台。 waitpid(.., .., WNOHANG) 立即返回无论孩子是否完成,但在每种情况下都返回不同的值。
猜你喜欢
  • 2014-12-06
  • 1970-01-01
  • 2018-10-26
  • 2022-11-02
  • 1970-01-01
  • 2020-03-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多