所有fork函数返回值之间的区别是什么?

时间:2017-10-14 03:24:54

标签: c linux fork waitpid

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/wait.h>
#define LG_LIGNE 256
int main(void){

char ligne[LG_LIGNE];
while(1){//afficher un symbole d'invite(prompt)
    fprintf(stderr, "-->");
    //lire une ligne de commandes
    if(fgets(ligne,LG_LIGNE,stdin)==NULL)
    break;
    //supprimer le retour chariot final
    ligne[strlen(ligne)-1]='\0';
    //lancer un processeur
    if(fork()==0){
    //processus fils
    //executer la commande
    execlp(ligne, ligne, NULL);
    //msg d'erreur si on echoue
    perror(ligne);
    exit(EXIT_FAILURE);
    }
    else{//process père
    //attendre la fin de son fils
    waitpid(-1,NULL,0);
    //et reprendre la boucle
    }
}
fprintf(stderr,"\n");
return EXIT_SUCCESS;
}

我理解每个返回值意味着什么,但我在这里不太明白,在孩子/父亲过程中技术上意味着什么,我看不到waitpid的使用,如果我不这样做将会发生什么用它?

2 个答案:

答案 0 :(得分:2)

Fork()创建进程内存和状态的副本,并生成一个子进程以在其中运行。子进程和父进程都在自己的进程中运行,拥有自己的地址空间和虚拟内存副本。那么你怎么知道哪个是父,哪个是孩子,因为在具有相同状态和内存的fork()调用之后,两个任务都恢复执行?

您可以根据fork()的返回代码判断哪个是父级,哪个是子级。如果返回值为零,那么您就是子进程,并且此进程通常会将返回代码测试为零,并分支以执行子进程的工作。

同时,父节点也从相同的fork()调用返回,但返回代码设置为其子节点的进程ID(非零)。父级可以选择保存此进程ID,但只有在父级创建多个子级并希望通过其进程ID跟踪它们时,它才真正有用。

在父完成之前,它通常会等待其子进程先完成。这就是waitpid调用的作用。您可以传入子进程的id,在这种情况下,{Child}进程完成之前不会返回waitpid调用。将-1传递给waitpid会让它等到你的一个子任务完成。

waitpid的返回码包含已完成的子任务的进程ID。有关详细信息,请参阅此处https://stackoverflow.com/a/21249082/6693299

有关您的课程的完整解释,请阅读H.S.的优秀详细说明。在这里回答相同的问题https://stackoverflow.com/a/46741392/6693299

答案 1 :(得分:1)

您的第一个问题 -

what being in the child/father process technically means

fork()通过复制调用进程来创建子进程。 调用fork()的流程是父流程,新创建的流程是子流程。 所以fork()将进程拆分为两个,并将子进程返回0,子进程的PID返回父进程,如果fork失败,则返回-1。

子进程和父进程在不同的内存空间中运行。在fork()时,两个内存空间都具有相同的内容。

有一个名为写作时复制的概念,了解它的好处 -

  

Copy on Write是一种优化,其中设置了页表,以便父进程和子进程开始共享所有相同的内存,并且只有在需要时才复制任一进程写入的页面。

如果进程没有修改任何内存并立即执行新进程,则完全替换地址空间。因此,在fork期间复制所有进程的内存将是浪费,而使用写入时复制技术。

例如,在您的计划中,您在execlp之后立即致电fork

if(fork()==0){
//processus fils
//executer la commande
execlp(ligne, ligne, NULL);

您的第二个问题 -

I couldn't see the use of waitpid, what will happen if I didn't use it?

为了解释这一点,我修改了你的程序并添加了语句来打印父进程和子进程pid并注释掉了这个语句 -

waitpid(-1,NULL,0);

节目输出是 -

parent process pid : 22325 -->ls child process pid : 22326 < here the output of ls command >

现在,如果我们看到带有grep'ed父进程id的ps命令的输出 -

# ps -eaf | grep 22325
root     22325 21555  0 10:39 pts/4    00:00:00 ./a.out
root     22326 22325  0 10:39 pts/4    00:00:00 [ls] <defunct>
root     22339 21644  0 10:39 pts/5    00:00:00 grep 22325

这里,在输出中第一列是UID,第二列是PID,第三列是PPID(父pid)。 您可以看到子进程(pid - 22326)被标记为&lt;已解散&gt;

defunct 进程(也称为“zombie”进程)是一个已完成执行的进程,它将具有退出状态以报告其父进程。由于这最后一点点信息,该进程将作为僵尸进程保留在操作系统的进程表中,表明它不会被安排进一步执行,但无法完全删除(并且它的进程ID不能是重新使用)直到确定不再需要退出状态。

此处使用 waitpid() -

wait()waitpid()函数应获取与其中一个调用者的子进程有关的状态信息。

waitpid()暂停调用进程,直到系统获取有关子进程的状态信息。如果系统在调用waitpid()时已有适当子项的状态信息,则waitpid()会立即返回。如果调用进程收到一个信号,其动作是执行信号处理程序或结束进程,那么waitpid()也会结束。

有关waitpid()类似语法(状态,选项)和返回值的其他详细信息,您可以查看其man页。

如果我取消注释程序中的waitpid()并编译并运行它,那么输出 -

parent process id : 23069
-->ls
child process id : 23070
<here the output of ls command>

现在,如果我们看到ps命令的输出与grep'ed父进程id -

# ps -eaf | grep 23069
root     23069 21555  0 10:51 pts/4    00:00:00 ./a.out
root     23108 21644  0 10:51 pts/5    00:00:00 grep 23069

没有僵尸进程。运行ls命令的子进程已完成,父进程读取其退出状态。

希望这能回答你们两个问题。