您的位置:首页 > 运维架构 > Linux

基于Linux0.11内核分析:系统时间和定时

2019-05-22 17:20 204 查看

系统时间

linux系统有两个时钟:一个是由主板电池驱动的“Real Time Clock”也叫做RTC或者叫CMOS时钟,
硬件时钟。当操作系统关机的时候,用这个来记录时间,但是对于运行的系统是不用这个时间的。
另一个时间是 “System clock”也叫内核时钟或者软件时钟,是由软件根据时间中断来进行计数的,
内核时钟在系统关机的情况下是不存在的,所以,当操作系统启动的时候,内核时钟是要读取RTC时间
来进行时间同步,并且在系统关机的时候将系统时间写回RTC中进行同步。
所以:

  • 内核在启动时从RTC中读取启动时的时间和日期
  • 内核在需要时将时间与日期写回到RTC中

下面介绍0.11版的初始化读取RTC时间:

static _inline _syscall1(int,setup,void *,BIOS)// int setup(void * BIOS)系统调用,仅用于
// linux 初始化
// 这段宏读取CMOS 实时时钟信息。
// 0x70 是写端口号,0x80|addr 是要读取的CMOS 内存地址。
// 0x71 是读端口号。
/*
#define CMOS_READ(addr) ({ \
outb_p(0x80|addr,0x70); \
inb_p(0x71); \
})*/
#define BCD_TO_BIN(val) ((val)=((val)&15) + ((val)>>4)*10)
// 该子程序取CMOS 时钟,并设置开机时间 startup_time(为从1970-1-1-0 时起到开机时的秒数)
static void time_init(void)
{
struct tm time;

do {// 参见后面CMOS 内存列表。
time.tm_sec = CMOS_READ(0);
time.tm_min = CMOS_READ(2);
time.tm_hour = CMOS_READ(4);
time.tm_mday = CMOS_READ(7);
time.tm_mon = CMOS_READ(8);
time.tm_year = CMOS_READ(9);
} while (time.tm_sec != CMOS_READ(0));
BCD_TO_BIN(time.tm_sec);
BCD_TO_BIN(time.tm_min);
BCD_TO_BIN(time.tm_hour);
BCD_TO_BIN(time.tm_mday);
BCD_TO_BIN(time.tm_mon);
BCD_TO_BIN(time.tm_year);
time.tm_mon--;
startup_time = kernel_mktime(&time);
}
time_init();	// 设置开机启动时间 -> startup_time
long
kernel_mktime (struct tm *tm)
{
long res;
int year;

year = tm->tm_year - 70;	// 从70 年到现在经过的年数(2 位表示方式),
// 因此会有2000 年问题。
/* magic offsets (y+1) needed to get leapyears right. */
/* 为了获得正确的闰年数,这里需要这样一个魔幻偏值(y+1) */
res = YEAR * year + DAY * ((year + 1) / 4);	// 这些年经过的秒数时间 + 每个闰年时多1 天
res += month[tm->tm_mon];	// 的秒数时间,在加上当年到当月时的秒数。
/* and (y+2) here. If it wasn't a leap-year, we have to adjust */
/* 以及(y+2)。如果(y+2)不是闰年,那么我们就必须进行调整(减去一天的秒数时间)。 */
if (tm->tm_mon > 1 && ((year + 2) % 4))
res -= DAY;
res += DAY * (tm->tm_mday - 1);	// 再加上本月过去的天数的秒数时间。
res += HOUR * tm->tm_hour;	// 再加上当天过去的小时数的秒数时间。
res += MINUTE * tm->tm_min;	// 再加上1 小时内过去的分钟数的秒数时间。
res += tm->tm_sec;		// 再加上1 分钟内已过的秒数。
return res;			// 即等于从1970 年以来经过的秒数时间。
}

系统定时

系统每个一个时间段就发出一个时钟中断请求(IRQ0)信号。这个时间爱你节拍就是操作系统的运行脉搏,称为1个系统滴答或一个系统时钟周期。
每经过1个滴答时间,系统就会调用一次时钟中断处理程序(timer_interrupt)。
每发生一次时钟中断 jiffies 变量的值就增1,然后调用C函数 do_timer()函数,调用是所带的参数CPL是从被中断程序的段选择符(保存在堆栈中的CS段寄存器)中取得的当钱代码特权级CPL。do_timer()函数根据特权级对当前进程运行时间作累计,CPL=0:表示运行在内核态,内核态运行时间统计值stime增1,否则用户态时间统计值增1.
时间片
时间片是一个进程在被切换掉之前所能持续运行的CPU时间,单位是滴答数。
如果进程时间片值递减后大于0,表示其时间片未用完,于是就退出 do_timer()继续运行当前进程,如果此时时间片已经递减为0,程序就会根据被中断程序的级别来确定进一步的处理方法。

若被中断的当前进程是工作在用户态的,则do_timer()就会调用调度程序 schedule() 切换到其它进程取运行。
如果被中断的当前进程工作在内核态,也即在内核态中运行的程序被中断,则 do_timer() 会立即退出。
因此这样的处理方式决定了linux系统的进程在内核态运行时不会被调度程序切换,也即进程在内核态中不可抢占,但在用户态中可以被抢占。
(linux 2.4 内核起,内核空间低优先级的进程也能被高优先级的进程抢占,从而使系统响应性能最大提高200%)

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: