我正在尝试在C中创建一个简单的shell程序。我需要它做的是为用户提供一个可以运行其他本地程序的提示。我可以使用fork()来完成那一部分,其中父进程在子进程上等待(),子进程execvp()是程序。
然而,如果'&'字符被附加到用户命令的末尾,我需要他们的程序在后台运行,这意味着我需要父进程不等待子进程,而是立即将提示返回给用户,同时允许后台进程继续运行,但不允许它在屏幕上显示任何内容。我只是想通过ps命令检查它是否仍然存在。
我试图理解使用fork()创建子项的想法,然后让子fork()再次创建一个类的孙子,然后立即退出() - 子进程。即,孤儿孙子。据说,这允许父母仍然等待孩子,但由于孩子几乎立即有效地结束,它就像它不等待?关于僵尸疯狂的事情?我不知道。我遇到过的几个网站似乎都建议将其作为在后台运行流程的一种方式。
然而,当我尝试这样做时,我发现程序流程发生了很多事情,“背景”过程继续在屏幕上显示输入,我真的不知道从哪里开始。
这是我对代码的实现,我确信这是非常错误的。我只是想知道这整个孙子的事情是否是我需要采取的路线,如果是这样,我的代码有什么问题?
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 我将实现它,以便每个后续命令将确定'背景'的布尔值,对不起混淆
答案 0 :(得分:22)
你不想在shell中使用double - fork()
方法 - 用于编写特别想要逃避运行它们的shell监督的守护进程。
相反,在现有shell中复制&
行为的方法是调用fork()
,然后在子调用setpgid(0, 0);
中将子项置于新的进程中组。父母继续(可能在打印孩子的PID
之后) - 它不会调用wait()
。
每个终端只能有一个前台进程组,这是当前允许向终端发送输出的进程组。这将是您的shell所属的进程组,因此您的shell将保留在前台。
如果后台进程组尝试从终端读取,则会向其发送一个信号以阻止它。仍允许输出到终端 - 这是正常的(在常规shell中尝试ls &
)。要在shell中实现fg
命令,您需要使用tcsetpgrp()
函数来更改前台进程组。
您还需要定期使用waitpid()
选项调用WNOHANG
- 例如,在显示shell提示之前。这将允许您检测后台进程何时退出或停止(或者您可以处理SIGCHLD
信号)。