time时钟内核学习和进程调度关系

来源:互联网 发布:久住小春知乎 编辑:程序博客网 时间:2024/06/06 04:34
linux内核必须完成两种主要的定时测量。
1.一个是计时,保存当前的时间和日期,以便能通过time(),gettimeofday()等系统调用,返回给用户程序,
2.另一个是定时,这种机制主要是告诉内核或者应用程序,某一时间间隔已经过去了,触发回调函数,然后做一些事情。

对于硬件主要有实时时钟RTC,CPU本地timer。
RTC:
是cpu外部的其他芯片,或者内部的模块,不过都是需要外部单独提供类似纽扣电池供电。
linux只用RTC来获取时间和日期。

CPUtimer:
CPU本地timer其实就是用CPU内部的Timer模块,CPU的timer模块可以计数,当然是通过什么主频计算实现。

linux具有Dynamic Ticks和High Resolution Timer。

其中CONFIG_NO_HZ用来控制Dynamic Ticks,CONFIG_HIGH_REST_TIMERS控制High Resolution Timer。
    若是没有选择上述的高精度timer和动态tick的话,那么就用linux系统的低精度timer了


时间系统主要任务是维持系统时间并且防止某个进程独占CPU及其他资源,也就是驱动进程的调度
RTC(实时时钟、硬件时钟)和OS时钟(系统时钟、软时钟)
OS时钟是由可编程定时/计数器产生的输出脉冲触发中断而产生的。输出脉冲的周期叫做一个“时钟滴答”。
计算机中的时间是以时钟滴答为单位的,每一次时钟滴答,系统时间就会加1。操作系统根据当前时钟滴答的数目就可以得到以秒或毫秒等为单位的其他时间格式。
具体怎么算:
OS时钟记录的时间也就是通常所说的系统时间。系统时间是以“时钟滴答”为单位的,
而时钟中断的频率决定了一个时钟滴答的长短,例如每秒有100次时钟中断,
那么一个时钟滴答的就是10毫秒(记为10ms),相应地,系统时间就会每10ms增1。
当然后计算也需要你一个基准,例如DOS的时间基准是1980年1月1日,
Unix的时间基准是1970年1月1日上午12点,
Linux的时间基准是1970年1月1日凌晨0点。

Linux中用全局变量jiffies表示系统自启动以来的时钟滴答数目
在jiffies基础上,Linux提供了如下适合人们习惯的时间格式,在/include/linux/time.h中定义如下:
struct timespec {               /* 这是精度很高的表示*/
       long tv_sec;             /* 秒 (second) */
       long tv_nsec;           /* 纳秒:十亿分之一秒( nanosecond)*/
};
struct timeval {               /* 普通精度   */
       int    tv_sec;             /* 秒   */
       int    tv_usec;           /* 微秒:百万分之一秒(microsecond)*/
};
struct timezone {                     /* 时区 */
       int    tz_minuteswest;            /* 格林尼治时间往西方的时差 */
       int    tz_dsttime;                  /* 时间修正方式 */
};
tv_sec表示秒(second),tv_usec表示微秒(microsecond),tv_nsec表示纳秒(nanosecond)。
定义tb_usec和tv_nsec的目的是为了适用不同的使用要求,不同的场合根据对时间精度的要求选用这两种表示。
另外,Linux还定义了用于表示更加符合大众习惯的时间表示:年、月、日。但是万变不离其宗,所有的时间应用都是建立在jiffies基础之上的。
简而言之,jiffies产生于时钟中断!

脉冲信号:
一次脉冲信号就会出发一次时钟终端,完全由硬件产生,并且该硬件有频率,例如每秒有100次时钟中断(其实就是一次脉冲),
那么一个时钟滴答的就是10毫秒(记为10ms),相应地,系统时间就会每10ms增1。

时钟中断:
“时钟中断”是特别重要的一个中断,因为整个操作系统的活动都受到它的激励。
系统利用时钟中断维持系统时间、促使环境的切换,以保证所有进程共享CPU;
利用时钟中断进行记帐、监督系统工作以及确定未来的调度优先级等工作。可以说,“时钟中断”是整个操作系统的脉搏。
该时钟中断信号触发CPU去执行一个中断服务程序,我们就把这个服务程序叫做时钟中断。

时钟滴答tick
也就是指juffies,其实也就是每一次脉冲信号的周期,假设(10毫秒)


Linux实现时钟中断的全过程:
timer_interrupt( )//时钟中断程序,主要就是调用do_timer_interrupt()
    do_timer_interrupt()//中断服务通用例程,1.调用do_timer( )。2.维持实时时钟(RTC)和os时钟同步
               do_timer( )//时钟函数,1.更新系统时间。2.调用update_process_times
            update_process_times()//函数和进程有关,例如更新当前进程的时间片计数器counter,
                        //如果counter<=0,则要调用调度程序,意思就是说这个进程的时间片完了,换一个进程
所以看出时钟中断与进程调度密不可分,因此,一旦开始有时钟中断就可能要进行调度。

进程调度算法:
1.时间片轮转调度算法
2.非抢占式优先权算法
3.抢占式优先权算法
4.多级反馈队列调度,其实就是时间片轮转调度算法和式优先权算法的结合
5.实时调度,即有求必应。


Linux调度时机主要有:
1、进程状态转换的时刻:进程终止、进程睡眠;
//进程要调用sleep()或exit()等函数进行状态转换,这些函数会主动调用调度程序进行进程调度;
2、当前进程的时间片用完时(current->counter=0);
3、设备驱动程序主动调用schedule;
4、进程从中断、异常及系统调用返回到用户态时;

进程调度的依据:
每个进程的task_struct结构中有这么五项:
need_resched、nice、counter、policy 及rt_priority
need_resched: 在调度时机到来时,检测这个域的值,如果为1,则调用schedule() 。
counter: 进程处于运行状态时所剩余的时钟滴答数,每次时钟中断到来时,这个值就减1。
当这个域的值变得越来越小,直至为0时,就把need_resched 域置1,因此,也把这个域叫做进程的“动态优先级”。
nice: 进程的“静态优先级”,这个域决定counter 的初值。
只有通过nice(), sched_setparam()系统调用才能改变进程的静态优先级。
rt_priority: 实时进程的优先级
policy: 从整体上区分实时进程和普通进程,因为实时进程和普通进程的调度是不同的,
它们两者之间,实时进程应该先于普通进程而运行,可以通过系统调用sched_setscheduler( )来改变调度的策略。
对于同一类型的不同进程,采用不同的标准来选择进程。对于普通进程,选择进程的主要依据为counter和nice 。
对于实时进程,Linux采用了两种调度策略,即FIFO(先来先服务调度)和RR(时间片轮转调度)。
因为实时进程具有一定程度的紧迫性,所以衡量一个实时进程是否应该运行,Linux采用了一个比较固定的标准。
实时进程的counter只是用来表示该进程的剩余滴答数,并不作为衡量它是否值得运行的标准,这和普通进程是有区别的。

进程可运行程度的衡量
函数goodness()就是用来衡量一个处于可运行状态的进程值得运行的程度。
该函数综合使用了上面我们提到的五项,给每个处于可运行状态的进程赋予一个权值(weight),
调度程序以这个权值作为选择进程的唯一依据。


最后总结:
时钟中断====时钟滴答===系统时间===jiffies次数(其实也就是滴答次数,当然是以基准时间1970年什么)===时间10ms(当然是通过中断频率计算的)

CPU竞争有很多种策略
linux : 时间片,一个进程1分钟执行时间
windows:抢占式,通过饥饿度,优先级等计算CPU占有优先级,然后进程可以使用CPU个痛快(一直霸占,当然也有机制不会让它一直霸占)

Thread.Sleep 的作用,就是可以提前结束进程占用CPU,并且在这段时间内不把它放入队列。
注意,不是Sleep结束就马上可以得到CPU,这个只是说在那段时间内不抢占CPU。包括Thread有个Resume函数
仅仅是表名该进程要开始抢占CPU了。

另外Thread.Sleep(0)的作用就是说当前进程放弃CPU,然后重新计算所有进程的优先级。

注意,上述的进程其实指线程。CPU只操作线程,进程只是一个容器。
原创粉丝点击