我有一个程序可以分叉进程,并确定子进程是否应该在前台和后台运行。我叫信号函数在分叉前处理子信号,以确保死子进程不会变成僵尸。
到目前为止,我的程序工作正常,它会创建一个子进程并在用户输入带有'&'的命令时在后台运行它,并在用户输入没有'&'的命令时在前台运行它。
然而,我发现了一个非常有趣的行为。我支持这个操作序列:
sleep 5 &
ls
第一个命令将正常工作,父进程不等待睡眠5完成。但是,当我运行“ls”时,它会打印该文件夹中的所有文件(这很好),但是shell卡住了,等待之前的“睡眠5&”完成...
为什么会这样?我的子进程和父进程的代码(在分叉之后)看起来非常像下面这样:
if (pid == 0)
{
// child process, execute stuff
execv();
}
else if (pid > 0)
{
// parent process: call waitpid to wait for foreground child
}
我试着做一些研究,但我找不到任何可以帮助我的东西。我尝试在子进程中使用“set session-id”,通过在execv()之前调用它,但它阻止我的子进程在终端上打印任何内容。 任何帮助将不胜感激。谢谢!
答案 0 :(得分:0)
#define _XOPEN_SOURCE 700
#include <unistd.h>
#include <signal.h>
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
void run_command(void)
{
char *cmd = NULL, *arg;
size_t n, l;
bool background;
pid_t child;
l = getline(&cmd, &n, stdin);
cmd[l-1] = 0;
l--;
if (cmd[l-1] == '&') {
background = true;
cmd[l-1] = 0;
l--;
}
else
background = false;
arg = strchr(cmd, ' ');
if (arg) {
*arg = 0;
arg++;
}
child = fork();
if (child) {
if (!background)
waitpid(child, NULL, 0);
}
else {
execlp(cmd, cmd, arg, NULL);
exit(-1);
}
free(cmd);
}
int main(void)
{
sigset_t set;
struct sigaction sig;
sigemptyset(&set);
sig.sa_handler = SIG_DFL;
sig.sa_mask = set;
sig.sa_flags = SA_NOCLDWAIT;
sigaction(SIGCHLD, &sig, NULL);
while(!feof(stdin)) run_command();
}
这可以按预期工作:
hdante@aielwaste:~/code$ ./shell
pwd
/home/hdante/code
ls /home
hdante
sleep 5
ls /
bin dev initrd.img lib32 lost+found opt run srv usr vmlinuz.old
boot etc initrd.img.old lib64 media proc sbin sys var
cdrom home lib libnss3.so mnt root selinux tmp vmlinuz
sleep 5&
xedit&
ls /
bin dev initrd.img lib32 lost+found opt run srv usr vmlinuz.old
boot etc initrd.img.old lib64 media proc sbin sys var
cdrom home lib libnss3.so mnt root selinux tmp vmlinuz
在上面的示例中,睡眠5正确阻止,而睡眠5&amp;和xedit&amp;不。
不看你的代码,就不可能知道问题出在哪里。但请注意我处理僵尸进程的方式:我在sa_flags中使用SA_NOCLDWAIT,因此我不需要跟踪pids。另外,我使用waitpid()等待前台子进程。这就是我认为问题所在。您的代码可能正在调用wait()而不是waitpid()。区别在于wait()等待所有孩子。