有关fork调用的程序解析

#例子1(有关waitpid函数)

​#include <sys/types.h>//定义了pid_t为int
#include <sys/wait.h>//定义了waitpid函数
#include <stdio.h>//定义了printf函数
#include <unistd.h>//定义了fork函数
#include <stdlib.h>//定义了exit函数

int main()
{
    int status;//定义一个整型变量status,用于存储(返回的)子进程的状态信息
	pid_t pid;//定义一个pid_t型变量pid,用于存储fork函数的返回值
	
	printf("Hello\n");
	pid=fork();//调用fork函数,创建子进程,在子进程中返回值为0,在父进程中返回值为子进程的进程号
	printf("%d\n",!pid);//在父进程中输出0,在子进程中输出1
	
	if(pid!=0)//执行父进程时
	{
	   /*这里waitpid函数的作用是父进程被挂起,直到子进程终止。若成功回收已终止的子进程,
	   返回值为子进程的进程号(一定大于0),出错返回值为-1。入口参数status用于存放导致返回
	   的子进程的状态信息,入口参数-1表示等待集合包含父进程的所有子进程,入口参数0表示默认情   况
	   (也就是父进程被挂起,直到一个子进程终止)
	   */  
	   if(waitpid(-1,&status,0)>0)
	   {
	     if(WIFEXITED(status)!=0)//WIFEXITED函数当子进程正常终止时为真
		    printf("%d\n",WEXITSTATUS(status));//WEXITSTATUS函数用于返回正常终止的子进程的退出状态	 
	   }
	}
	printf("Bye\n");
	exit(2);//退出,即终止进程,退出状态信息为2

}​

进程图

理论上它可以有三种输出情况(遵循拓扑排序)

Hello     或          Hello        或    Hello

1                         1                      0

Bye                    0                       1

0                        Bye                  Bye

2                        2                       2

Bye                   Bye                   Bye

我的系统运行情况

系统原因,只产生一种情况。但是这一种情况也有可能是由多种途径产生的。(执行完父进程的第二个printf语句后,可能先执行waitpid导致父进程被挂起,再依次执行子进程的三条语句;也有可能先执行子进程的一条printf语句,再执行waitpid导致父进程被挂起,再继续执行子进程的另外两条语句等等)

这些情况都是由于,父进程和子进程是并发运行的独立进程。内核能够以任意方式交替执行它们的逻辑控制流中的指令。

#例子2

void fork10()
{
    pid_t pid[N];//定义一个含有5个pid_t类型元素的数组
    int i, child_status;//child_status用于记录(返回的)子进程的状态信息

    for (i = 0; i < N; i++)//循环5次
	if ((pid[i] = fork()) == 0) {//每循环一次,生成一个子进程,并以独特的状态终止(按照子进程产生的顺序,退出状态分别为100,101,102,103,104)
	    exit(100+i); /* Child */ //子进程当中pid[i]全为0,父进程当中pid[0...4]分别存储了第一个产生的子进程的pid...第五个产生的子进程的pid
	}
    for (i = 0; i < N; i++) { /* Parent *///循环5次
	pid_t wpid = wait(&child_status);//父进程被挂起,当一个子进程终止时,用wpid返回它的pid,用child_status记录它的退出状态
	if (WIFEXITED(child_status))//WIFEXITED函数当子进程正常终止时为真
	    printf("Child %d terminated with exit status %d\n",
		   wpid, WEXITSTATUS(child_status));//具体说明哪个子进程被正常终止,并输出它的退出状态	 
	else
	    printf("Child %d terminate abnormally\n", wpid);//当该子进程没有正常终止时,输出它退出异常
    }
}

运行结果

这个例子的进程图是根据子进程的终止顺序有多种情况的。(先终止的先输出)

下面是我根据运行情况画出的进程图

下面是类似的一个例子(例子2.1),只不过将wait改为waitpid。(即一定要等到某个具体子进程被终止,父进程才继续执行,并输出该子进程的相关信息。其他子进程就算已经终止了,也必须等到轮到它时才被回收)

void fork11()
{
    pid_t pid[N];
    int i;
    int child_status;

    for (i = 0; i < N; i++)
	if ((pid[i] = fork()) == 0)
	    exit(100+i); /* Child */
    for (i = N-1; i >= 0; i--) {
	pid_t wpid = waitpid(pid[i], &child_status, 0);//依次等待子进程终止(从最后一个产生的子进程开始等)
	if (WIFEXITED(child_status))
	    printf("Child %d terminated with exit status %d\n",
		   wpid, WEXITSTATUS(child_status));
	else
	    printf("Child %d terminate abnormally\n", wpid);
    }
}

运行结果(按子进程产生顺序逆序输出相关信息)

#例子3

void fork18()
{
    int i;
	for(i=0;i<2;i++)
	{
	   fork();
	   printf("*\n");//输出一个*号并换行,\n有将输出缓冲区内容输出到屏幕上,从而清空缓冲区的作用
	}
}

void fork19()
{
    int i;
	for(i=0;i<2;i++)
	{
	   fork();
	   printf("*");//少了\n,可能会有部分内容残留在缓冲区
	}
}

void fork20()
{
    int i;
	for(i=0;i<2;i++)
	{
	   fork();
	   printf("*");
	   fflush(stdout);//增加一个将输出缓冲区内容输出到屏幕上,从而清空缓冲区的函数
	}
}

为什么第三行会输出命令提示符?

根据查阅资料和画进程图,我认为先是输出两个*,结束了最初的父进程后,该进程返回到了shell进程;此时输出命令提示符的语句和

输出*的语句分别作为某个进程的当前可执行语句。两个进程是并发的,并且可能因为系统原因,总是先执行输出命令提示符的语句。

之后我们没有输入任何数据,shell进程暂时停止,CPU去转而执行程序里其他子进程。

fork20使用了fflush()函数,将输出缓冲区的内容输出到屏幕上,从而清空输出缓冲区。

这个效果和fork18类似,它们都正常输出了6颗*。

fork19就不同了。

printf语句只是将*输出到缓冲区里,并没有立即输出到屏幕上,也就是说缓冲区未被清空。由于子进程会复制父进程的缓冲区,

第二级fork后,四个进程的缓冲区全为一颗*。当它们接着执行printf语句后,缓冲区内容均变为两颗*,当它们最后终止时,

缓冲区将被清空。因此会输出8颗*。

发布了25 篇原创文章 · 获赞 6 · 访问量 2001

猜你喜欢

转载自blog.csdn.net/weixin_44711653/article/details/102875734