PM 时钟机制
PM 时钟机制
10.1 Minix3 PM 时钟机制概述在 MINIX3 中,除了前面所讲到的 CLOCK 时钟,在 pm 中也是维持了一个时钟, 我们暂且不分析为啥要这么做,我就分析是怎么实现这个 PM 时钟监视器。我可 以这么肯定的说,这个时钟监视器只是一个虚幻的时钟监视器,最终还是得内核 时钟来完成这个工作,但是这个时钟监视器还是很有用的。我分析下 PM 时钟监 视器的工作过程:
如果用户需要用到时钟的相关功能,用户进程就会告诉 pm 我需要设定一个时钟
警告器来告诉我在未来的多长时间给我响一次警报,我想完成相关的内容,pm
就会维持一个时钟队列,就像内核时钟任务一样,每当一个 pm 时钟对头快要消
耗完成时,把下个即将成为队头得时钟警报器发送给内核时钟,内核时钟队列同
时也维持一个时钟队列,这个时钟队列是内核的时钟队列,在前面有详细介绍,
如果内核时钟监视器发现这个时钟警报器是 PM 发送并且已经耗尽时间时,它将
会发送一个消息给 PM,PM 先更新新的时钟队列,之后将相关信息通知这个需
要通知的进程。以上过程基本上整个 PM 时钟所完成的任务。下面用图详细表示
出来
10.2 MINIX3PM时钟机制源码导读:
155
先看在servers/pm/timers.c,这个几个函数主要是为了PM机制提供一种服务。我们所以先看 这几个函数:这里的几个函数都是被其他函数而调用的。
/* PM watchdog timer management. These functions in this file provide
* a convenient interface to the timers library that manages a list of
* watchdog timers. All details of scheduling an alarm at the CLOCK task
* are hidden behind this interface.
* Only system processes are allowed to set an alarm timer at the kernel. * Therefore, the PM maintains a local list of timers for user processes * that requested an alarm signal.
* PM看门狗计时器。在这个文件的这些函数提供给时钟库一个便利的接口,这
个时钟警报器 主要是管理一系列看门狗计时器。所有的调度内核时钟警报器任
务的细节都隐藏在这个接口上。只有系统进程被允许在内核上设定时钟警报器,
因此,PM维持一个本地用户时钟计时器用来想请求一个时钟信号的进程
* The entry points into this file are:
* pm_set_timer: reset and existing or set a new watchdog timer
重新设置一个或者设置一个新的看门狗计时器
* pm_expire_timers: check for expired timers and run Watchdog Functions。 检查一个一个消耗计时器或者运行看门狗函数
* pm_cancel_timer: remove a time from the list of timers
从一个时间计时器中去除一个时钟
*
*/
#include "pm.h"
#include <timers.h>
#include <minix/syslib.h>
#include <minix/com.h>
//这个是计时器时钟指针,是这个文件的全局变量
PRIVATE timer_t *pm_timers = NULL; 先看这个函数的执行图:
156
/*=================================================================== ========*
* pm_set_timer 重新设置一个或者设置一个新的看门狗计
时器 *
*==================================================================== =======*/
PUBLIC void pm_set_timer(timer_t *tp, int ticks, tmr_func_t watchdog, int
arg)
{
int r;
clock_t now, prev_time = 0, next_time;
if ((r = getuptime(&now)) != OK)
157
panic(__FILE__, "PM couldn't get uptime", NO_NUM);
/* Set timer argument and add timer to the list. */
tmr_arg(tp)->ta_int = arg;
prev_time =
tmrs_settimer(&pm_timers,tp,now+ticks,watchdog,&next_time);
//这个函数就是设置一个看门狗计时器。将pm_timers时钟队列中安装一个tp时 钟队列,将其消耗时间设置成为now+ticks,看门狗程序设置成为watchdog函数 首地址,next_time是返回变量。这个变量就是耗时时间(绝对时间)
/* Reschedule our synchronous alarm if necessary. */ if (! prev_time || prev_time > next_time) {
if (sys_setalarm(next_time, 1) != OK)
panic(__FILE__, "PM set timer couldn't set alarm.", NO_NUM);
}
return;
}
现在我们来看下一个函数,同样,这个函数还是非常的重要。先给出示意图:
158
/*=================================================================== ========*
* pm_expire_timers 检查一个一个消耗计时器或者运行看门
狗函数 *
**=================================================================== ========*/
PUBLIC void pm_expire_timers(clock_t now)
{
clock_t next_time;
/* Check for expired timers and possibly reschedule an alarm. */ //检查时钟警报器并且可能重新调度一个时钟警报
//事实这个函数在前面的内核时钟已经讲到了,我们现在在此做一个简要的参数
分析:pm_timers是一个时钟队列,我们将pm_timers中的队头消耗时间和now相
比较,如果发现这个消耗时间小于或者等于现在这个时间now.就执行队列队头的
159
看门狗函数。并且重新设置队列对头,并且将新的队头消耗时间传递给Next_time
变量。留给下面重新设置一个系统警报。当然如果消耗时间大于now,则什么都不
会做。
tmrs_exptimers(&pm_timers, now, &next_time); if (next_time > 0) {
if (sys_setalarm(next_time, 1) != OK)
panic(__FILE__, "PM expire timer couldn't set alarm.",
NO_NUM);
}
}
/*=================================================================== ========*
* pm_cancel_timer 这个函数的主要功能就是取消时钟警报
器 *
*==================================================================== =======*/
PUBLIC void pm_cancel_timer(timer_t *tp)
{
//函数实现去除tp指向的时钟警报器。函数主体就是调用一个tmrs_clrtimer() 函数,这个函数就是把从pm_timers时钟队列中去除掉tp所指向的函数。
clock_t next_time, prev_time;
prev_time = tmrs_clrtimer(&pm_timers, tp, &next_time);
/* If the earliest timer has been removed, we have to set the alarm
to
* the next timer, or cancel the alarm altogether if the last timer
has
* been cancelled (next_time will be 0 then).
*/
if (prev_time < next_time || ! next_time) {
if (sys_setalarm(next_time, 1) != OK)
panic(__FILE__, "PM expire timer couldn't set alarm.",
NO_NUM);
}
}
160
MINIX3PM时钟和内核时钟的交互
这种交互其实就是一种信号量的实现,信号量的具体实现参考MINIX3信号量实现 机制,在这这里我们主要看是怎么实现相关的交互,执行到信号量问题时,我们 默认信号量原来的定义,原来的作用。这里选取的几个函数都是处理PM时钟机制 准备工作。这几个函数是来自于/servers/pm/sigal.c里。在讨论这几个函数之 前,我想讨论的工作就是一种大体的宏观执行过程:
先看用户进程是怎么注册一个时钟警报器:
我们现在来看看PM时钟看门狗响起时的大体执行过程
161
从上面2个图中可以看到,pm_exipre_timers和pm_set_timers函数都调用了,这
就是为什么刚才先分析那2个函数。在看这2副图注意和前面的2个函数执行图想
联系
/*=================================================================== ========*
*do_alarm做时钟警报工作。主体其实就是设定set_alarm(who,m_in.seconds)
函数这个函数在下面会有详细的介绍 *
*==================================================================== =======*/
PUBLIC int do_alarm()
{
/* Perform the alarm(seconds) system call. */
return(set_alarm(who, m_in.seconds));
}
/*=================================================================== ========*
* set_alarm *
*====================================================================
162
=======*/
PUBLIC int set_alarm(proc_nr, sec)
int proc_nr; //想要警报器的进程号 /* process that wants the alarm
*/
int sec; //在信号产生之前,许多多少秒的延迟/* how many seconds delay
before the signal */
{
/* This routine is used by do_alarm() to set the alarm timer. It is also
used
* to turn the timer off when a process exits with the timer still on. 这个例程被do_alarm()来调用,目的是为了设置时间警报器。当一个进程存在 且时钟仍然在上面,
它仍然被用来关闭一个时钟
*/
clock_t ticks; /* number of ticks for alarm */
//警报器的时钟节拍数
clock_t exptime; /* needed for remaining time on previous alarm */
//前一个时钟警报器遗留的时间
clock_t uptime; /* current system time */
//当前系统时间
int remaining; /* previous time left in seconds */
//前一个时钟警报器还有多少秒
int s;
/* First determine remaining time of previous alarm, if set. */
//首先查看进程表是否是标志ALARM_ON标志,如果是ALARM_ON标志,就表明 //该进程的警报器是打开的
if (mproc[proc_nr].mp_flags & ALARM_ON) { if ( (s=getuptime(&uptime)) != OK)
panic(__FILE__,"set_alarm couldn't get uptime", s); //这个函数前面分析了,主要是看看警报器还有多少时间剩余
exptime = *tmr_exp_time(&mproc[proc_nr].mp_timer);
//将remaining转变成秒做单位
remaining = (int) ((exptime - uptime + (HZ-1))/HZ); //计算剩余时间,如果剩余时间小于把其置
if (remaining < 0) remaining = 0; } else {
remaining = 0;
}
/* Tell the clock task to provide a signal message when the time comes.
* 当时钟器来临的时候告诉这个时钟任务来提供一个信号消息
* Large delays cause a lot of problems. First, the alarm system call
* takes an unsigned seconds count and the library has cast it to an
163
int. That probably works, but on return the library will convert
"negative" unsigneds to errors. Presumably no one checks for these
errors, so force this call through. Second, If unsigned and long have the same size, converting from seconds to ticks can easily overflow. Finally, the kernel has similar overflow bugs adding ticks.
大的延迟可能会引起一些问题,首先就是时钟系统调用
* Fixing this requires a lot of ugly casts to fit the wrong interface
* types and to avoid overflow traps. ALRM_EXP_TIME has the right type
* (clock_t) although it is declared as long. How can variables like
* this be declared properly without combinatorial explosion of message
* types?
*/
ticks = (clock_t) (HZ * (unsigned long) (unsigned) sec); //算成节拍数
if ( (unsigned long) ticks / HZ != (unsigned) sec)
ticks = LONG_MAX; /* eternity (really TMR_NEVER) */
if (ticks != 0) {
//如果节拍不是为0的话,就表明时钟警报器有效,pm_set_timer()函数在前面
有讲过,这里简要分析执行完这个函数之后,将mp_timer时钟警报器安装在
pm_timers时钟队列中,应该是安装到队尾,并且设置mp_timer时钟警报器的各
个参数,这里需要注意的一点就是将看门狗函数设置成为cause_sigalrm。
pm_set_timer(&mproc[proc_nr].mp_timer, ticks, cause_sigalrm,
proc_nr);
//设置完成之后,将进程的标志位的ALARM_ON置位
mproc[proc_nr].mp_flags |= ALARM_ON;
} //如果ticks计算出来结果还是0并且其警报器标志位ALARM_ON还是开启状 态,则应该取消这个函数的时钟警报器,并且将ALARM_ON位关闭,也就是复位 else if (mproc[proc_nr].mp_flags & ALARM_ON) {
pm_cancel_timer(&mproc[proc_nr].mp_timer);
mproc[proc_nr].mp_flags &= ~ALARM_ON;
}
return(remaining);
}
/*=================================================================== ========*
* cause_sigalrm 这个函数由前面的分析我们可以看出来这
个函数其实就是一个看门狗函数,我们不想对这个过深入的分析,这个函数的分
析事实上涉及到信号量机制,我们暂且不做相关的分析 *
*==================================================================== =======*/
PRIVATE void cause_sigalrm(tp)
164
struct timer *tp;
{
int proc_nr;
register struct mproc *rmp;
proc_nr = tmr_arg(tp)->ta_int; /* get process from timer */
//计算是哪个进程号
rmp = &mproc[proc_nr];
//对本进程号做出一些基本检测
if ((rmp->mp_flags & (IN_USE | ZOMBIE)) != IN_USE) return; //检测本进程的标志位是否开启ALARM_ON,如果没有开启,就返回
if ((rmp->mp_flags & ALARM_ON) == 0) return;
//上面2种检测完成之后,我们首先就将ALARM_ON位关闭,之后执行check_sig() 函数,这个函数主要目的就是执行相关的看门狗函数。这里不做深入分析,因为 后面信号量机制会详细的分析这个函数的用途。
rmp->mp_flags &= ~ALARM_ON;
check_sig(rmp->mp_pid, SIGALRM);
}
/*===================================================================
========*
* sys_setalarm 主要是在内核态设置一
个警报器。这点不做详细分析 *
*==================================================================== =======*/
PUBLIC int sys_setalarm(exp_time, abs_time)
clock_t exp_time; /* expiration time for the alarm */
//这个警报器的消耗时间,从上面一个函数传来的也就是next_time
int abs_time; /* use absolute or relative expiration time */
//是使用绝对时间还是相对时间
{
/* Ask the SYSTEM schedule a synchronous alarm for the caller. The process
* number can be SELF if the caller doesn't know its process number.
*/
//设置消息内容。准备进入内核来完成这个SYS_SETALARM任务。
message m;
m.ALRM_EXP_TIME = exp_time; /* the expiration time */
m.ALRM_ABS_TIME = abs_time; /* time is absolute? */
return _kernel_call(SYS_SETALARM, &m);
}
165
PM时钟的最后一个部分是PM时钟库。这里拿出来做出简要的分析: 下面是这个函数源码:
/* This file takes care of those system calls that deal with time. *这个文件注意了那些与时钟相关的系统调用
* The entry points into this file are 指向这个文件的进口有:
* do_time:
执行TIME调用
* do_stime: 执行STME调用
* do_times:
执行TIMES系统调用
*/
#include "pm.h"
perform the TIME system call
perform the STIME system call
perform the TIMES system call
#include <minix/callnr.h>
#include <minix/com.h>
#include <signal.h>
#include "mproc.h"
#include "param.h"
PRIVATE time_t boottime;
/*=================================================================== ========*
* do_time 获得当前的实时时钟和运行时间
*
*==================================================================== =======*/
PUBLIC int do_time()
{
/* Perform the time(tp) system call. This returns the time in seconds since
* 1.1.1970. MINIX is an astrophysically naive system that assumes the
earth
* rotates at a constant rate and that such things as leap seconds do not * exist.
*/
clock_t uptime;
int s;
if ( (s=getuptime(&uptime)) != OK)
panic(__FILE__,"do_time couldn't get uptime", s);
166
//上面的getuptime(&uptime)函数调用,将结果存在uptime中。存储之后将mp? //的相关域设置好。
mp->mp_reply.reply_time = (time_t) (boottime + (uptime/HZ)); mp->mp_reply.reply_utime = (uptime%HZ)*1000000/HZ;
return(OK);
}
/*=================================================================== ========*
* do_stime 设置实时时钟 *
*==================================================================== =======*/
PUBLIC int do_stime()
{
/* Perform the stime(tp) system call. Retrieve the system's uptime (ticks * since boot) and store the time in seconds at system boot in the global * variable 'boottime'.
*/
clock_t uptime;
int s;
if (mp->mp_effuid != SUPER_USER) {
return(EPERM);
}
if ( (s=getuptime(&uptime)) != OK)
panic(__FILE__,"do_stime couldn't get uptime", s); //主要是更改boottime这个时间
boottime = (long) m_in.stime - (uptime/HZ);
/* Also inform FS about the new system time. */
tell_fs(STIME, boottime, 0, 0);
return(OK);
}
/*=================================================================== ========*
* do_times 获得进程的统计时间 *
*==================================================================== =======*/
PUBLIC int do_times()
{
167
/* Perform the times(buffer) system call. */
register struct mproc *rmp = mp;
clock_t t[5];
int s;
if (OK != (s=sys_times(who, t)))
panic(__FILE__,"do_times couldn't get times", s);
//这个调用主要是获取系统时间,通过前面的sys_times(who,t),将需要的参数 //全部存储在t[5]数组中。之后将rmp进程表项相关域设置好。
rmp->mp_reply.reply_t1 = t[0]; /* user time */
rmp->mp_reply.reply_t2 = t[1]; /* system time */
rmp->mp_reply.reply_t3 = rmp->mp_child_utime; /* child user time */
rmp->mp_reply.reply_t4 = rmp->mp_child_stime; /* child system time
*/
rmp->mp_reply.reply_t5 = t[4]; /* uptime since boot */
return(OK);
}
PM时钟机制基本分析完毕。跟信号量相关的处理在MINIX3信号量分析那个章节里 有详细的分析!
PM 时钟机制的更多相关文章
- linux新内核的时钟机制代码
http://blog.chinaunix.net/uid-22810130-id-384173.html 如果说cfs是linux的一个很有创意的机制的话,那么linux中另一个创意就是nohz,我 ...
- ARM Linux系统的时钟机制
1. Linux下有两类时钟: 1.1 实时钟RTC 它由板上电池驱动的“Real Time Clock”也叫做RTC或者叫CMOS时钟,硬件时钟.当操作系统关机的时候,用这个来记录时间,但是对于运行 ...
- MINIX3 内核时钟分析
MINIX3 内核时钟分析 4.1 内核时钟概要 先想想为什么 OS 需要时钟?时钟是异步的一个非常重要的标志,设想一下,如 果我们的应用程序需要在多少秒后将触发某个程序或者进程,我们该怎么做到? ...
- android即时消息处理机制
在android端做即时消息的时候.遇到的坑点是怎么保证消息即时性,又不耗电.为什么这么说呢? 原因是假设要保证消息即时性.通常有两种机制pull或者push. pull定时轮询机制,比較浪 ...
- Linux驱动设计—— 中断与时钟
中断和时钟技术可以提升驱动程序的效率 中断 中断在Linux中的实现 通常情况下,一个驱动程序只需要申请中断,并添加中断处理函数就可以了,中断的到达和中断函数的调用都是内核实现框架完成的.所以程序员只 ...
- Oracle的SCN与检查点机制
Oracle的SCN与检查点机制 SCN在Oracle的文档上以多种形式出现,一种是System Change Number,另一种是System Commit Number,在大多数情况下,Syst ...
- MINIX3 内核整体架构回顾及内核定 性分析
MINIX3 内核整体架构回顾及内核定 性分析 12.1 注意事项 由于本文档不对 I/O 文件系统做出分析,所以在此不对 MINIX3 整体做出一个分 析,本章主要是针对内核进程分析.并且这里的模 ...
- MINIX3 系统任务分析
MINIX3 系统任务分析 7.1 MINIX3 系统任务概要 MINIX3 怎么来给用户提供丰富的服务呢?除了中断,异常处理,除了时钟服务. 程序员总是希望一个操作系统给他提供足够的服务,使得他 ...
- linux驱动程序之电源管理之linux的电源管理架构(3)
设备电源管理 Copyright (c) 2010 Rafael J. Wysocki<rjw@sisk.pl>, Novell Inc. Copyright (c) 2010 Alan ...
随机推荐
- Objective-C:@class和#import
@class和#import是OC中引用一个类的两种方式,其区别在于: #import相当于把被引用文件的内容拷贝到目标文件,这会包含被引用类的所有信息,包括被引用类的变量和方法(会降低编译性能 ): ...
- Android selector选择器的使用
通常按钮在点击前和后有两种状态,比如点击前为蓝色,点击后为灰色,且不再响应点击事件. 如果不使用selector选择器,点击后,就需要在程序中进行以下的类似操作 button1.setBackgrou ...
- JDE报表开发笔记(数据选择及继承)
在Section的Event中, Do Custom Section("sectionxxx")自定义加载下一个Section Set Selection Append Flag( ...
- js私有共有成员
在小项目中对于JavaScript使用,只要写几个function就行了.但在大型项目中,尤其是在开发追求 良好的用户体验的网站中,如SNS,就会 用到大量的JavaScrpt,有时JavaScr ...
- 原创:整理编辑jQuery全部思维导图【附下载地址】
主图 全部图已经打包:下载地址 2. 3. 4. 5. 6. 附上一点简单说明 Dom对象和jquer对象之间的转化 如何将一个jquery对象转换为DOM对象? test是一个span元素 var ...
- vbox内部linux :centos5.5与外部ping通(相互),而且域名访问
1 相互ping通:不能使用nat,nat只能单向通,虚拟机不能ping通主机,选择桥接: 如图: 2然后设置 ip:最好设置静态ip这样下次不用再改,这里我们只演示使用eth0网卡,=> vi ...
- 监听TelephonyManager的通话状态来监听手机的所有的来电
import java.io.FileNotFoundException;import java.io.OutputStream;import java.io.PrintStream;import j ...
- hduacm 2888 ----二维rmq
http://acm.hdu.edu.cn/showproblem.php?pid=2888 模板题 直接用二维rmq 读入数据时比较坑爹 cin 会超时 #include <cstdio& ...
- 闭包 (循环事件获取不到i) 和 各种解决循环获取不到i的解决方法
for(var i in fav){ (function(){ var p=i; var obj=$S.getId(fav[i]); ...
- 优化MYSQL数据库的方法
1.选取最适用的字段属性,尽可能减少定义字段长度,尽量把字段设置NOT NULL,例如'省份,性别',最好设置为ENUM2.使用连接(JOIN)来代替子查询: a.删除没有任何订单客户:DELETE ...