#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的使用,如果我不这样做将会发生什么用它?
答案 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
命令的子进程已完成,父进程读取其退出状态。
希望这能回答你们两个问题。