子进程中的Execl()和父进程中的wait()

时间:2016-12-29 11:48:12

标签: c fork exec posix wait

我不明白为什么父进程会继续执行,即使他的子进程没有终止。

这是a.c:

if (!fork()){
    execl("/bin/sh", "sh", "-c", "gnome-terminal -x ./b", (char *) 0);
}
else {

    int status;
    wait(&status);
    if ((status & 255) == 0) printf("\nProcess regularly exited ");
    printf("adios");
    return 0;
}

这是b.c:

printf("hello\n");
fflush(0);
getchar();
exit(0);

父进程打印"进程定期退出"和" adios"当子进程没有终止并且等待getchar()的输入时,我不明白为什么。

最后,如何强制父进程等待b.c的执行完成?

2 个答案:

答案 0 :(得分:3)

将评论转换为答案。

当面对一个在执行另一个程序时没有按预期工作的C程序时,一个关键问题是在shell中发生的事情'对于这个问题,这意味着: 从命令行运行sh -c "gnome-terminal …"时,在获得提示之前需要多长时间?

  

它是即刻的。

所以 - 惊喜,惊喜 - 当你从C代码运行它时会发生同样的事情。 sh -c …的shell会立即退出,因此您的父进程会发现其死亡并退出。我认为你的第二个程序是作为你分叉的过程的孙子或曾孙的运行。你有sh运行gnome-terminal,它可能是forks而父进程在孩子继续管理终端并且分叉运行第二个命令的shell时(或者可能没有使用)一个shell,只需执行你的第二个命令)。一个过程只能等待直接的孩子死去;它不能等待孩子的孩子或更远的后代。

  

现在,如果我在while(1){}之前的a.c末尾添加return 0b.c之前的exit(0)末尾添加top,{{1} }命令表示我有4个进程:2个a.c和2个b.c。难道不奇怪吗?

可能会发生多种事情。它可能是以前运行的剩余进程。您需要查看流程树,以了解这些流程是否相关以及它们之间的关系。

  

谢谢他们只是简单的剩余流程。

     

最后,如何强制父进程等待b.c的执行完成?

简洁地说,你不能让A等待B完成。你需要一种方法让A知道哪个进程是它应该等待的B。它不能直接等待它,因为B不是它自己的孩子,所以你可能最终做了一些轮询操作,看看B是否还在。这可能是使用kill()和信号0进行测试;在Linux上,您可以在/proc文件系统上使用iNotify(并避免轮询)。

答案 1 :(得分:1)

您可以使用信号。这是一个简单而不优雅的解决方案。

交流转换器

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>

void death_handler();

int main(){

sigset_t set;
sigfillset(&set);
sigprocmask(SIG_BLOCK,&set,NULL);
sigemptyset(&set);
sigaddset(&set, SIGUSR1); 
sigprocmask(SIG_UNBLOCK, &set, NULL); //unlock SIGUSR1
signal(SIGUSR1, death_handler);

printf("my pid is %d\n", getpid());

char pid[50];
snprintf(pid, 50, "gnome-terminal -x ./b %d",(int)getpid()); 

if (fork() == 0){

    execl("/bin/sh","sh", "-c", pid,(char *) 0);
}
else {

    int status;
    //nobody, apart from the other process (and anyone that sends me a SIGUSR1 signal) can tell me to exit. It has to send a SIGUSR1 signal.

    pause();

    //UNBLOCK ALL PREVIOUSLY BLOCKED SIGNALS 
    sigemptyset(&set);
    sigfillset(&set);
    sigprocmask(SIG_UNBLOCK, &set, NULL);
    //remove ex-child handler
    signal(SIGUSR1, SIG_DFL);

    printf("adios\n");
    return 0;
}   

}

void death_handler(){

printf("ex-child dead\n");    
}

这是b.c

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>

int main(int argc, char** argv){

int xmax;
xmax = atoi(argv[1]);
printf("parent pid = %s \n", argv[1]);
fflush(0);
getchar();
kill((pid_t)xmax, SIGUSR1);
printf("sendinga signal to the ex-parent to abort pause()...\n");
exit(0);

}

这基本上会暂停父母,直到有人发出10信号。它只能被发送SIGUSR1信号的人取消暂停。

另一种解决方案可能是使用信号量。您将信号量初始化为0.父级执行等待,然后,当子进程完成后,您只需执行一个帖子(在子级中),即可释放父级。