在Linux虚拟机上编写如下代码:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(int argc, char *argv[])
{
int i=0;
printf("i son/pa ppid pid fpid\n");
//ppid指当前进程的父进程pid
//pid指当前进程的pid,
//fpid指fork返回给当前进程的值
for(i=0;i<4;i++)
{
pid_t fpid=fork();
if(i<2)
{
if(fpid==0)
{
printf("%d child %4d %4d %4d\n",i,getppid(),getpid(),fpid);
sleep(3);
}
else
{
printf("%d parent %4d %4d %4d\n",i,getppid(),getpid(),fpid);
sleep(3);
}
}
if(i==2)
{
if( fpid < 0 ){ // 出错
perror("fork");
exit(0);
}
if( fpid == 0 )
{
// 子进程
{
printf("%d son %4d %4d %4d\n",i,getppid(),getpid(),fpid);
sleep(3);
}
_exit(2); // 子进程退出,数字 2 为子进程退出的状态
}
else if( fpid > 0)
{
printf("%d papa %4d %4d %4d\n",i,getppid(),getpid(),fpid);
sleep(3);
// 父进程
int status = 0;
wait(&status);
if(WIFEXITED(status) != 0)
{ // 子进程是否正常终止
printf("son process return %d\n", WEXITSTATUS(status));
printf("%d son %4d %4d %4d\n",i,getppid(),getpid(),fpid);
sleep(3);
}
printf("this is father process\n");
printf("%d papa %4d %4d %4d\n",i,getppid(),getpid(),fpid);
sleep(3);
}
}
if(i==3)
{
if(fpid==0)
{
printf("%d child %4d %4d %4d\n",i,getppid(),getpid(),fpid);
sleep(3);
}
else
{
printf("%d parent %4d %4d %4d\n",i,getppid(),getpid(),fpid);
sleep(3);
}
printf("*****before exec******\n\n");
execl("/bin/ls", "ls", "-a", "-l", "-h", NULL);
perror("execl");
printf("*****after exec*******\n\n");
}
}
return 0;
}
运行结果图:
该程序的循环中,i<2时,一共建立了四个进程,打印六次。其中i代表循环次数,son/pa代表是父进程或是子进程,ppid代表父进程的pid,pid代表当前进程的pid,fpid指fork返回给当前进程的值。第一步:在父进程中,指令执行到for循环中,i=0,接着执行fork,fork执行完后,系统中出现两个进程,分别是3737和3738.可以看到父进程3737的父进程是3700,子进程3738的父进程正好是3737。我们用一个链表来表示这个关系:3700->4150->4151,第二次fork()后与第一次相类似,链表关系是:4150->4152, 4150->4151,4151->4153
其对应代码如下:
具体结果如下所示:
第三次循环中即i=2时,若是子进程就打印进程pid及其父进程,子进程,若是父进程,打印相关信息,并 调使用wait()函数,等子进程结束,回收子进程的资源,该函数会阻塞。
status 某个字段保存子进程调用 _exit(2) 的 2,需要用宏定义取出
waitpid(-1, &status, 0); // 和 wait() 没区别,0:阻塞
waitpid(pid, &status, 0); // 指定等待进程号为 pid 的子进程, 0 阻塞
waitpid(pid, &status, WNOHANG); // WNOHANG:不阻塞
如果子进程是正常终止的,取出的字段值非零,就执行以下命令
执行结果如下图所示
可以看出i==2时共有八个进程,其中子进程四个,父进程四个,回收了此次循环创建的子进程
第四次循环即i=3时,由于第二次循环已经收回了四个子进程,因此,在第三次,此前的四个进程重新创建了四个子进程,在这些进程中调用execl,其中
/bin/ls:外部程序,这里是/bin目录的 ls 可执行程序,必须带上路径(相对或绝对)
ls:没有意义,如果需要给这个外部程序传参,这里必须要写上字符串,至于字符串内容任意。
-a,-l,-h:给外部程序 ls 传的参数
NULL:这个必须写上,代表给外部程序 ls 传参结束
所用代码如下:
如果 execl() 执行成功,以下代码执行不到,因为当前进程已经被执行的 ls 替换了
运行结果如下所示,由于存在八个进程,因此调用了八次ls命令。