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如何操作了。