0

我有一个分叉进程的程序,并确定子进程是否应该在前台和后台运行。我在分叉之前调用信号函数来处理子信号,以确保死去的子进程不会变成僵尸。

到目前为止,我的程序运行良好,它创建一个子进程并在用户输入带有“&”的命令时在后台运行它,并在用户输入不带“&”的命令时在前台运行它。

但是,我发现了一个非常有趣的行为。假设我调用了这个操作序列:

sleep 5 &
ls

第一个命令可以正常工作,父进程不会等待 sleep 5 完成。但是,当我运行“ls”时,它会打印该文件夹中的所有文件(这很好),但是外壳卡住了,等待之前的“sleep 5 &”完成......

为什么会出现这种情况?我的子进程和父进程代码(分叉后)如下所示:

 if (pid == 0)
 {
      // child process, execute stuff
      execv();
 } 
 else if (pid > 0) 
 {
      // parent process: call waitpid to wait for foreground child
 }

我试图做一些研究,但我找不到任何可以帮助我的东西。我尝试通过在 execv() 之前调用它来为子进程使用“set session-id”,但它会阻止我的子进程在终端上打印任何内容。任何帮助将不胜感激。谢谢!

4

1 回答 1

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

在上面的示例中, sleep 5 正确阻塞,而 sleep 5& 和 xedit& 不会。

如果不查看您的代码,就不可能知道问题出在哪里。但是请注意我处理僵尸进程的方式:我在 sa_flags 中使用 SA_NOCLDWAIT,这样我就不需要跟踪 pid。另外,我使用waitpid() 等待前台子进程。这就是我认为问题所在。您的代码可能正在调用 wait() 而不是 waitpid()。不同之处在于 wait() 等待所有孩子。

于 2013-02-26T00:57:14.027 回答