时间片轮转调度算法的提及和关于fork函数执行父,子进程先后顺序的理解

时间:2021-03-19 21:15:06

时间片轮转调度算法的提及和关于fork函数执行父,子进程先后顺序的理解


        fork函数是用来创建进程的,命令行下输入man2 fork 看到他的函数声明:

   #include <unistd.h>

pid_t fork(void);

    fork函数调用一次会返回两次值,在成功调用fork函数后,当前进程会分裂为两个进程,一个是当前进程,返回值是子进程的ID;另一个是当前创建的子进程,返回值是0;调用失败只返回一个值-1.  想象不来,可以把他类比于一个函数被执行了两次,就会返回两次值。实际上是进程分裂为两个进程后,子进程会将父进程地址空间里的资源复制过来执行,他和父进程都在执行fork函数后的程序代码。
       以一个例子(1)说明一下:

            
  #include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
int main(void)
{
pid_t pid;
pid=fork();
printf("hello!\n");
printf("world!\n");
return 0;
}


       运行结果如下图:

时间片轮转调度算法的提及和关于fork函数执行父,子进程先后顺序的理解

             可以看到fork后的hello world被输出了两次,第一次是调用fork函数后父进程执行的;第二次输出是父进程创建的子进程复制了父进程地址空间的资源,同样也执行了fork后面的两条语句。

   那fork 之后是父进程先执行还是子进程先执行呢?其实这也不是确定的,先执行哪个取决于内核所使用的调度算法。有的操作系统最先开始执行的是子进程,有的是最先执行父进程,但是整体思想都是一样的,无论先执行谁,一般情况下,一个进程不会一口气执行到结束。
   举个例子(2)看一下父、子进程是如何执行的:

            
#include<stdio.h>
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
int main(void)
{
pid_t pid;
char * msg;
int k;
printf("Process Creation Study\n");
pid=fork();
switch(pid)
{
case 0:
msg="Child process is running";
k=3;
break;
case -1:
perror("Process creation failed\n");
break;
default:
msg="Parent process is rinning";
k=5;
break;
}
while(k>0)
{
puts(msg);
sleep(1);
k--;
}
exit(0);
}


结果:

时间片轮转调度算法的提及和关于fork函数执行父,子进程先后顺序的理解

       从例子(1)看到我的内核的调度算法是最开始先执行的父进程,然后看第二个例子,先执行了父进程,但是情况出现了,如果执行父进程会遇到一个5次的while循环,应该输出5次“Parent process is rinning”的,但是屏幕只输出了一次又开始输出“Child processisrunning”,说明此时执行的是子进程了。我们看到屏幕父子进程是交替打印字符串的,一直到子进程循环结束,父进程又继续打印了两次后循环也结束了。从这个例子我们看到,无论父进程还是子进程都不是一次性执行完毕而是交替执行的。
   这是因为操作系统一般让所有进程都享有同等执行权,除非某进程的优先级比其他的高。
   别激动,再看,我把例(2)while循环体里面的 sleep(1)注释掉,再运行例(3)看看:

int main(void)
{
pid_t pid;
char * msg;
int k;
printf("Process Creation Study\n");
pid=fork();
switch(pid)
{
case 0:
msg="Child process is running";
k=3;
break;
case -1:
perror("Process creation failed\n");
break;
default:
msg="Parent process is rinning";
k=5;
break;
}
while(k>0)
{
puts(msg);
//sleep(1); 注释掉---------------------
k--;
}
exit(0);
}


结果:

时间片轮转调度算法的提及和关于fork函数执行父,子进程先后顺序的理解


        嘿,看到了什么,执行结果和刚才不一样了,这符合了我们最开始的想法-----------先执行父进程,循环5次结束;然后执行子进程,循环3次结束。
不禁有点不爽了,不是父、子进程交替执行的么,这回怎么又不交替了。淡定!我们慢慢捣鼓。
    为了看的更清楚,我把例(1)程序改一下,把sleep 的时间间隔加大看看,结果是这样的:

            Process Creation Study
            Parent process is rinning
            Child process is running

等待大约5秒
            Parent process is rinning
            Child process is running

等待大约5秒
            Parent process is rinning
            Child process is running

等待大约5秒
            Parent process is rinning
等待大约5秒
            Parentprocess is rinning


对照程序分析一下:
      先执行父进程,遇到sleep,执行子进程,等待5秒。。。
      再执行父进程,遇到sleep,执行子进程,等待5秒。。。
。。。。。。

父进程遇到sleep并未暂停,而是直接执行子进程,子进程遇到sleep,暂停5秒。共遇到两个sleep(5),却只停了5秒。再次轮到父进程执行时,也并没有停5秒,而是直接输出字符串,说明sleep并不是等待父进程再次抢到CPU时执行的,猜测:

      a.父进程和子进程有一个没有执行sleep;
      b.父进程遇到sleep,执行了,系统记录了他的状态,执行子进程遇到sleep,等待了5秒,此时父进程的5秒时间也够了(可以看作执行子进程时,sleep时间也是在走的),所以子进程等待的时候覆盖了父进程的时间。
显然,程序语句怎么可能不执行,应该是情况b 合理了。

      

       看起来,进程在执行时,父进程遇到sleep,为了保证CPU资源的利用,不会傻傻的等待父进程sleep,而是直接切换子进程,在这个暂停时间里,可以让子进程做很多事情,这个过程花费的时间也是算在父进程sleep的等待时间里的。

再看看下面例(4):

int main(void)
{
pid_t pid;
char * msg;
int k;
printf("Process Creation Study\n");
pid=fork();
switch(pid)
{
case 0:
msg="Child process is running";
k=3;
break;
case -1:
perror("Process creation failed\n");
break;
default:
msg="Parent process is rinning";
k=5;
break;
}
while(k>0)
{
puts(msg);
if(pid==0)
sleep(5);
else
sleep(10);
k--;
}
exit(0);
}


注:让子进程等待5秒,让父进程等待10秒,
结果:
           Process Creation Study
           Parent process is rinning
           Child process is running

等待约5秒
           Child process is running
等待约5秒
           Parent process is rinning
           Child process is running

等待约10秒
           Parent process is rinning
等待约10秒
           Parent process is rinning
等待约10秒
           Parent process isrinning


        以上结果我们看到父、子进程并不是单纯一一交替执行的,而是受到了sleep 暂停时间的影响:先执行父进程,sleep 10秒,直接切换子进程,sleep 5秒,此时父进程的10秒还未完,再次执行子进程,父进程10秒已到,执行父进程,遇到sleep10秒,切换到子进程。。。。。。

        看起来,CPU 确实合理分配了时间来确定父、子进程的执行顺序。


            我们还有例(1)疑惑没有解决,为什么父、子没有交替执行?

是因为父进程没有遇到sleep,然后执行每条语句所用的时间都很小很小,C P U就没有必要切换到子进程了,所以父进程就一口气执行完了么?


下面我简单介绍一种算法,大家就明白了,以上问题就能可以迎刃而解了。

 

时间片轮转调度算法

 

     时间片轮转调度是一种最古老,最简单,最公平且使用最广的算法。每个进程被分配一个时间段,称作它的时间片,即该进程允许运行的时间。如果在时间片结束时进程还在运行,则CPU将被剥夺并分配给另一个进程。如果进程在时间片结束前阻塞或结束,则CPU当即进行切换。调度程序所要做的就是维护一张就绪进程列表,当进程用完它的时间片后,它被移到队列的末尾。

(以上是搜狗百科给出的定义)
 
   看了这个说明大概就明白多个进程是如何被执行得了。基于“时间片轮转调度算法”,每个进程都会被分给一个运行的时间片,就拿上面例(4)程序中的父,子进程的运行来说,比如时间片允许的时间段是100毫秒,这个时间是很短很短的,先执行父进程,遇到了sleep( sleep休眠,插入阻塞队列,进程阻塞状态就不会被执行,休眠时间到后,就会插入就绪队列,等待调度程序执行),保存父进程的状态信息,执行子进程,子进程也遇到sleep阻塞,再看父进程还是阻塞状态,此时终端用“ps-aux”命令查看进程状态,可以看到父子进程都是处于中断等待状态

时间片轮转调度算法的提及和关于fork函数执行父,子进程先后顺序的理解  
,5秒后,子进程先休眠完进入就绪队列,所以先执行子进程,子进程又遇到sleep阻塞,此时父进程还是阻塞,两个进程又都进入中断等待状态,大约5秒后,父进程先就绪,执行父进程,父进程又遇到sleep,子进程已就绪调度子进程。。。。。。
   再看看例(2)的问题就很好解释了,先执行父进程,父进程的程序执行完所需的时间不足一个时间片,所以父进程执行完后当即切换到子进程,在一瞬间子进程也执行完毕。
   所以,例(2)其实也是交替执行的,只不过没有sleep,在当前进程所允许的一个时间片内,程序很快被执行完了,没来得及中途切换到子进程,以至于我们没看到交替执行的效果。

(以上均属个人理解,欢迎交流,不喜勿喷。。。)