08进程学习之fork函数练习-循环创建N个子进程并用循环因子对各个子进程区分,并分析终端结束符先打印的原因,并使各个子进程按序输出

08进程学习之fork函数练习-循环创建N个子进程并用循环因子对各个子进程区分,并分析终端结束符先打印的原因,并使各个子进程按序输出

1 常见的错误思想
很多人刚开始接触以为就是循环五次调用fork函数即可,即下面的例子代码,以循环创建N=5为例。
错误例子:

#include<stdio.h>
#include<unistd.h>

int main(){

	pid_t pid;
	int i;
	for(i=0;i<5;i++){	
	   pid = fork();
	   if(pid > 0){
		printf("I am Father.\n");
	   }
	   else if(pid == 0){
		printf("I am Children.\n");
	   }else{
		printf("Fork Failed.\n");
	   }
	 }

	return 0;
}

结果:
在这里插入图片描述

上面结果可见,这fork出来的子进程肯定不止5次了。我们分析上面的程序,当第一次fork的时候,父进程创建了一个子进程;此时,由于i=0刚刚创建的子进程无法退出for循环,那么就导致刚刚fork出的子进程也会自己fork子进程,也就是说第二次for循环的时候,上面的两个进程都会执行fork函数,那么就翻倍变成4个,以此类推。即:
i=0 =》 2个进程;
i=1 =》 4个进程;
i=2 =》 8个进程;
i=3 =》 16个进程;
i=4 =》 32个进程;
当i=5时,父进程也就是主进程退出for循环,此时共用32个进程,规律为2的n次方减1。
所以经过分析后我们每一个fork后必须要让fork出的子进程关闭掉,好让它无法进行下一次的for循环,这样就达到循环创建N个进程。

2 改进后可以循环创建N个进程的例子

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

int main(int argc, char *argv[])
{
	int i;
	
	//循环创建5个子进程
	for (i = 0; i < 5; i++){	
		if (fork() == 0){
			break;//子进程直接退出for循环,防止参与下一次的fork
		}
	}
	
	//i=5说明父进程执行完for循环而退出
	if(5 == i){
		printf("I am parent.\n");
	}else{
		//子进程的执行逻辑
		printf("I am %d children\n", i+1);
	}

	return 0;
}

结果:
在这里插入图片描述
加上父进程刚好是1+5创建了5个子进程。

3 分析上面终端结束符先打印的原因
看到上面的结果,终端结束符gec@ubuntu比5,2,1共三个进程更先打印,你再执行的话更有可能它是第一个打印或者其它打印结果,为什么会这样呢?
答案是因为各个进程对CPU资源抢夺的原因。因为parent的父进程是bash即终端,所以它也会与这6个进程一起抢夺CPU资源,当bash的抢夺能力比其它进程快的时候,就会优先打印。
更详细就是:
终端打印信息是根据程序释放结束即父进程结束而打印的,那么当父进程先结束,然后其余5个进程抢不过终端的话,那么终端就优先比其余5个进程先打印。
解决办法看下面解释和例子。

4 解析终端打印信息比某些进程快的方法
要解决其实也是非常简单的,既然终端看程序即父进程是否结束而打印,那么我们使父进程最后退出即可。使用的函数就是sleep使它睡眠。

代码例子,实际上就是在上一个程序的基础上增加一条睡眠语句:即父进程i=5的时候使其睡眠。

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

int main(int argc, char *argv[])
{
	int i;
	
	//循环创建5个子进程
	for (i = 0; i < 5; i++){	
		if (fork() == 0){
			break;//子进程直接退出for循环,防止参与下一次的fork
		}
	}
	
	//i=5说明父进程执行完for循环而退出
	if(5 == i){
		sleep(1); //使程序即父进程最后结束,从而解决终端先打印
		printf("I am parent.\n");
	}else{
		//子进程的执行逻辑
		printf("I am %d children\n", i+1);
	}

	return 0;
}

结果:
在这里插入图片描述

可以看到,无论你怎么执行,终端都是最后输出,因为我们让父进程睡眠不抢夺CPU最后结束。

5 在4的基础上使各个子进程按序输出
其实也是非常简单的,既然上面使用睡眠可以让终端进程和其余进程按序输出,那么同样也可以让各个子进程按序输出,使用循环因为睡眠对应的秒数即可。

按序输出的代码例子:

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

int main(int argc, char *argv[])
{
	int i;
	int n = 5;//可以将5改成n,方便操作,这里为了更直白没改

	//循环创建5个子进程
	for (i = 0; i < 5; i++){	
		if (fork() == 0){
			break;//子进程直接退出for循环,防止参与下一次的fork
		}
	}
	
	//i=5说明父进程执行完for循环而退出
	if(5 == i){
		sleep(i); //使程序即父进程最后结束,从而解决终端先打印
		printf("I am parent.\n");
	}

	//使子进程按序输出
	if(i < 5){
		sleep(i);
		printf("I am %d children\n", i+1);
	}
	

	return 0;
}

结果:
在这里插入图片描述

6 总结
好了,对于fork函数的习题我们已经学完,相信大家可以非常熟悉fork如何操作了。

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值