体育资讯网

您现在的位置是:首页 > 分类10 > 正文

分类10

linux时钟中断源码(linux 定时中断)

hacker2022-07-14 03:57:25分类1061
本文目录一览:1、linux时钟中断哪个定时器2、

本文目录一览:

linux 时钟中断 哪个定时器

一. Linux的硬件时间

PC机中的时间有三种硬件时钟实现,这三种都是基于晶振产生的方波信号输入。这三种时钟为:(1)实时时钟RTC ( Real Time Clock) (2)可编程间隔器PIT(Programmable Interval Timer )(3)时间戳计数器TSC(Time Stamp Clock)

1. 实时时钟 RTC

用于长时间存放系统时间的设备,即时关机后也可依靠主板CMOS电池继续保持系统的计时,原理图如下:

Note: Linux与RTC的关系是,当Linux启动时从RTC读取时间和日期的基准值,然后在Kernel运行期间便抛开RTC,以软件的形式维护系统的时间日期,并在适当时机由Kernel将时间写回RTC Register.

1.1 RTC Register

(1). 时钟与日历Register

共10个,地址:0x00-0x09,分别用于保存时间日历的具体信息,详情如下:

00 Current Second for RTC

01 Alarm Second

02 Current Minute

03 Alarm Minute

04 Current Hour

05 Alarm Hour

06 Current Day of Week(1=Sunday)

07 Current Date of Month

08 Current Month

09 Current Year

(2).状态和控制Register

共四个,地址:0x0a-0x0d,控制RTC芯片的工作方式,并表示当前状态。

l 状态RegisterA , 0x0A 格式如下:

bit[7]——UIP标志(Update in Progress),为1表示RTC正在更新日历寄存器组中的值,此时日历寄存器组是不可访问的(此时访问它们将得到一个无意义的渐变值)。

bit[6:4]——这三位是用来定义RTC的操作频率。各种可能的值如下:

DV2 DV1 DV0

0 0 0 4.194304 MHZ

0 0 1 1.048576 MHZ

0 1 0 32.769 KHZ

1 1 0/1 任何

PC机通常设置成“010”。

bit[3:0]——速率选择位(Rate Selection bits),用于周期性或方波信号输出。

RS3 RS2 RS1 RS0 周期性中断 方波 周期性中断 方波

0 0 0 0 None None None None

0 0 0 1 30.517μs 32.768 KHZ 3.90625ms 256 HZ

0 0 1 0 61.035μs 16.384 KHZ

0 0 1 1 122.070μs 8.192KHZ

0 1 0 0 244.141μs 4.096KHZ

0 1 0 1 488.281μs 2.048KHZ

0 1 1 0 976.562μs 1.024KHZ

0 1 1 1 1.953125ms 512HZ

1 0 0 0 3.90625ms 256HZ

1 0 0 1 7.8125ms 128HZ

1 0 1 0 15.625ms 64HZ

1 0 1 1 31.25ms 32HZ

1 1 0 0 62.5ms 16HZ

1 1 0 1 125ms 8HZ

1 1 1 0 250ms 4HZ

1 1 1 1 500ms 2HZ

PC机BIOS对其默认的设置值是“0110”

l 状态Register B , 0x0B 格式如下:

bit[7]——SET标志。为1表示RTC的所有更新过程都将终止,用户程序随后马上对日历寄存器组中的值进行初始化设置。为0表示将允许更新过程继续。

bit[6]——PIE标志,周期性中断enable标志。

bit[5]——AIE标志,告警中断enable标志。

bit[4]——UIE标志,更新结束中断enable标志。

bit[3]——SQWE标志,方波信号enable标志。

bit[2]——DM标志,用来控制日历寄存器组的数据模式,0=BCD,1=BINARY。BIOS总是将它设置为0。

bit[1]——24/12标志,用来控制hour寄存器,0表示12小时制,1表示24小时制。PC机BIOS总是将它设置为1。

bit[0]——DSE标志。BIOS总是将它设置为0。

l 状态Register C,0x0C 格式如下:

bit[7]——IRQF标志,中断请求标志,当该位为1时,说明寄存器B中断请求 发生。

bit[6]——PF标志,周期性中断标志,为1表示发生周期性中断请求。

bit[5]——AF标志,告警中断标志,为1表示发生告警中断请求。

bit[4]——UF标志,更新结束中断标志,为1表示发生更新结束中断请求。

l 状态Register D,0x0D 格式如下:

bit[7]——VRT标志(Valid RAM and Time),为1表示OK,为0表示RTC 已经掉电。

bit[6:0]——总是为0,未定义。

2.可编程间隔定时器 PIT

每个PC机中都有一个PIT,以通过IRQ0产生周期性的时钟中断信号,作为系统定时器 system timer。当前使用最普遍的是Intel 8254 PIT芯片,它的I/O端口地址是0x40~0x43。

Intel 8254 PIT有3个计时通道,每个通道都有其不同的用途:

(1) 通道0用来负责更新系统时钟。每当一个时钟滴答过去时,它就会通过IRQ0向 系统 产生一次时钟中断。

(2) 通道1通常用于控制DMAC对RAM的刷新。

(3) 通道2被连接到PC机的扬声器,以产生方波信号。

每 个通道都有一个向下减小的计数器,8254 PIT的输入时钟信号的频率是1.193181MHZ,也即一秒钟输入1193181个clock-cycle。每输入一个clock-cycle其时间 通道的计数器就向下减1,一直减到0值。因此对于通道0而言,当他的计数器减到0时,PIT就向系统产生一次时钟中断,表示一个时钟滴答已经过去了。计数 器为16bit,因此所能表示的最大值是65536,一秒内发生的滴答数是:1193181/65536=18.206482.

PIT的I/O端口:

0x40 通道0 计数器 Read/Write

0X41 通道1计数器 Read/Write

0X42 通道2计数器 Read/Write

0X43 控制字 Write Only

Note: 因PIT I/O端口是8位,而PIT相应计数器是16位,因此必须对PIT计数器进行两次读写。

8254 PIT的控制寄存器(0X43)的格式如下:

bit[7:6] — 通道选择位:00 ,通道0;01,通道1;10,通道2;11,read-back command,仅8254。

bit[5:4] – Read/Write/Latch锁定位,00,锁定当前计数器以便读取计数值;01,只读高字节;10,只读低字节;11,先高后低。

bit[3:1] – 设定各通道的工作模式。

000 mode0 当通道处于count out 时产生中断信号,可用于系统定时

001 mode1 Hardware retriggerable one-shot

010 mode2 Rate Generator。产生实时时钟中断,通道0通常工作在这个模式下

011 mode3 方波信号发生器

100 mode4 Software triggered strobe

101 mode5 Hardware triggered strobe

3. 时间戳计数器 TSC

从Pentium开始,所有的Intel 80x86 CPU就都包含一个64位的时间戳记数器(TSC)的寄存器。该寄存器实际上是一个不断增加的计数器,它在CPU的每个时钟信号到来时加1(也即每一个clock-cycle输入CPU时,该计数器的值就加1)。

汇编指令rdtsc可以用于读取TSC的值。利用CPU的TSC,操作系统通常可以得到更为精准的时间度量。假如clock-cycle的频率是400MHZ,那么TSC就将每2.5纳秒增加一次。

二. Linux时钟中断处理程序

1. 几个概念

(1)时钟周期(clock cycle)的频率:8253/8254 PIT的本质就是对由晶体振荡器产生的时钟周期进行计数,晶体振荡器在1秒时间内产生的时钟脉冲个数就是时钟周期的频率。Linux用宏 CLOCK_TICK_RATE来表示8254 PIT的输入时钟脉冲的频率(在PC机中这个值通常是1193180HZ),该宏定义在include/asm-i386/timex.h头文件中

#define CLOCK_TICK_RATE 1193180 kernel=2.4 2.6

(2)时钟滴答(clock tick):当PIT通道0的计数器减到0值时,它就在IRQ0上产生一次时钟中断,也即一次时钟滴答。PIT通道0的计数器的初始值决定了要过多少时钟周期才产生一次时钟中断,因此也就决定了一次时钟滴答的时间间隔长度。

(3)时钟滴答的频率(HZ):1秒时间内PIT所产生的时钟滴答次数。 这个值也由PIT通道0的计数器初值决定的.Linux内核用宏HZ来表示时钟滴答的频率,而且在不同的平台上HZ有不同的定义值。对于ALPHA和 IA62平台HZ的值是1024,对于SPARC、MIPS、ARM和i386等平台HZ的值都是100。该宏在i386平台上的定义如下 (include/asm-i386/param.h):

#define HZ 100 kernel=2.4

#define HZ CONFIG_HZ kernel=2.6

(4)宏LATCH:定义要写到PIT通道0的计数器中的值,它表示PIT将隔多少个时钟周期产生一次时钟中断。公式计算:

LATCH=(1秒之内的时钟周期个数)÷(1秒之内的时钟中断次数)=(CLOCK_TICK_RATE)÷(HZ)

定义在include/linux/timex.h

#define LATCH ((CLOCK_TICK_RATE + HZ/2) / HZ)

(5)全局变量jiffies:用于记录系统自启动以来产生的滴答总数。启动时,kernel将该变量初始为0,每次时钟中断处理程序timer_interrupt()将该变量加1。因为一秒钟内增加的时钟中断次数等于Hz,所以jiffies一秒内增加的值也是Hz。由此可得系统运行时间是jiffies/Hz 秒。

jiffies定义于linux/jiffies.h中:

extern unsigned long volatile jiffies;

Note:在kernel 2.4,jiffies是32位无符号数;kernel 2.6,jiffies是64位无符号数。

(6)全局变量xtime: 结构类型变量,用于表示当前时间距UNIX基准时间1970-01-01 00:00:00的相对秒数值。当系统启动时,Kernel通过读取RTC Register中的数据来初始化系统时间(wall_time),该时间存放在xtime中。

void __init time_init (void) {

... ...

xtime.tv_sec = get_cmos_time ();

xtime.tv_usec = 0;

... ... }

Note:实时时钟RTC的最主要作用便是在系统启动时用来初始化xtime变量。

2.Linux的时钟中断处理程序

Linux下时钟中断处理由time_interrupt() 函数实现,主要完成以下任务:

l 获得xtime_lock锁,以便对访问的jiffies_64 (kernel2.6)和 xtime进行保护

l 需要时应答或重新设置系统时钟。

l 周期性的使用系统时间(wall_time)更新实时时钟RTC

l 调用体系结构无关的时钟例程:do_timer()。

do_timer()主要完成以下任务:

l 更新jiffies;

l 更新系统时间(wall_time),该时间存放在xtime变量中

l 执行已经到期的动态定时器

l 计算平均负载值

void do_timer(unsigned long ticks)

{

jiffies_64 += ticks;

update_process_times(user_mode(regs));

update_times (ticks);

}

static inline void update_times(unsigned long ticks)

{

update_wall_time ();

calc_load (ticks);

}

time_interrupt ():

static void timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) {

int count;

write_lock (xtime_lock); //获得xtime_lock锁

if(use_cyclone)

mark_timeoffset_cyclone();

else if (use_tsc) {

rdtscl(last_tsc_low); //读TSC register到last_tsc_low

spin_lock (i8253_lock); //对自旋锁i8253_lock加锁,对8254PIT访问

outb_p (0x00, 0x43);

count = inb_p(0x40);

count |= inb(0x40) 8;

if (count LATCH) {

printk (KERN_WARNING "i8253 count too high! resetting../n");

outb_p (0x34, 0x43);

outb_p (LATCH 0xff, 0x40);

outb(LATCH 8, 0x40);

count = LATCH - 1;

}

spin_unlock (i8253_lock);

if (count = = LATCH) {

count- -;

}

count = ((LATCH-1) - count) * TICK_SIZE;

delay_at_last_interrupt = (count + LATCH/2) / LATCH;

} //end use_tsc

do_timer_interrupt (irq, NULL, regs);

write_unlock(xtime_lock);

}//end time_interrupt

do_timer_interrupt():

static inline void do_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)

{

……

do_timer(regs);

if((time_status STA_UNSYNC)= =0xtime.tv_sec last_rtc_update + 660 xtime.tv_usec = 500000 - ((unsigned) tick) / 2 xtime.tv_usec = 500000 + ((unsigned) tick) / 2) {

if (set_rtc_mmss(xtime.tv_sec) == 0)

last_rtc_update = xtime.tv_sec;

else

last_rtc_update = xtime.tv_sec - 600;

……

}

do_timer_interrupt()主要完成:调用do_timer()和判断是否需要更新CMOS时钟。更新CMOS时钟的条件如下:三个须同时成立

1.系统全局时间状态变量time_status中没有设置STA_UNSYNC标志,即Linux没有设置外部同步时钟(如NTP)

2.自从上次CMOS时钟更新已经过去11分钟。全局变量last_rtc_update保存上次更新CMOS时钟的时间.

3.由于RTC存在Update Cycle,因此应在一秒钟间隔的中间500ms左右调用set_rtc_mmss()函数,将当前时间xtime.tv_sec写回RTC中。

Note. Linux kernel 中定义了一个类似jiffies的变量wall_jiffies,用于记录kernel上一次更新xtime时,jiffies的值。

Summary: Linux kernel在启动时,通过读取RTC里的时间日期初始化xtime,此后由kernel通过初始PIT来提供软时钟。

时钟中断处理过程可归纳为:系统时钟system timer在IRQ0上产生中断;kernel调用time_interrupt();time_interrupt()判断系统是否使用TSC,若使用 则读取TSC register;然后读取PIT 通道0的计数值;调用do_time_interrupt(),实现系统时间更新.

linux下的几种时钟和定时器机制

1. RTC(Real Time Clock)

所有PC都有RTC. 它和CPU和其他芯片独立。它在电脑关机之后还可以正常运行。RTC可以在IRQ8上产生周期性中断. 频率在2Hz--8192HZ.

Linux只是把RTC用来获取时间和日期. 当然它允许进程通过对/dev/rtc设备来对它进行编程。Kernel通过0x70和0x71 I/O端口来访问RTC。

 

2. TSC(Time Stamp Counter)

80x86上的微处理器都有CLK输入针脚. 从奔腾系列开始. 微处理器支持一个计数器. 每当一个时钟信号来的时候. 计数器加1. 可以通过汇编指令rdtsc来得到计数器的值。通过calibrate_tsc可以获得CPU的频率. 它是通过计算大约5毫秒里tsc寄存器里面的增加值来确认的。或者可以通过cat /proc/cpuinfo来获取cpu频率。tsc可以提供比PIT更精确的时间度量。

 

3. PIT(Programmable internval timer)

除了RTC和TSC. IBM兼容机提供了PIT。PIT类似微波炉的闹钟机制. 当时间到的时候. 提供铃声. PIT不是产生铃声. 而是产生一种特殊中断. 叫定时器中断或者时钟中断。它用来告诉内核一个间隔过去了。这个时间间隔也叫做一个滴答数。可以通过编译内核是选择内核频率来确定。如内核频率设为1000HZ,则时间间隔或滴答为1/1000=1微秒。滴答月短. 定时精度更高. 但是用户模式的时间更短. 也就是说用户模式下程序执行会越慢。滴答的长度以纳秒形式存在tick_nsec变量里面。PIT通过8254的0x40--0x43端口来访问。它产生中断号为IRQ 0.

下面是关于pIT里面的一些宏定义:

HZ:每秒中断数。

CLOCK_TICK_RATE:值是1,193,182. 它是8254芯片内部振荡器频率。

LATCH:代表CLOCK_TICK_RATE和HZ的比率. 被用来编程PIT。

setup_pit_timer()如下:

spin_lock_irqsave(i8253_lock, flags);

outb_p(0x34,0x43);

udelay(10);

outb_p(LATCH 0xff, 0x40);

udelay(10);

outb (LATCH 8, 0x40);

spin_unlock_irqrestore(i8253_lock, flags);

 

 

4. CPU Local Timer

最近的80x86架构的微处理器上的local apic提供了cpu local timer.他和pit区别在于它提供了one-shot和periodic中断。它可以使中断发送到特定cpu。one-shot中断常用在实时系统里面。

linux 当前进程是时钟中断怎么更新的进程调度时间

时钟中断对于调度系统来说就是一种驱动力。在时钟中断中,调度相关的一些时间计数量会被更新。同时会检查一下目前运行的进程运行时间是不是超过了一个slice,如果超过了这个间隔,就会设置重新调度标记。会在schedule函数中完成调度并完成进程切换。如果没有小于这个slice量,那就不会触发重新调度。为什么这么做呢?因为时钟中断触发频率是很高的,每秒有1000个tick,如果一个tick就重新调度一次,那么cpu将忙于进程切换,将大量的cpu白白浪费掉了,所以进程切换的频率必须掌握一个度。

LINUX 的调度和时钟中断处理代码的分析

我要是比尔就好了。可惜不是啊!!!!!!!!!兄弟,我估计你得自己想办法了。呵呵

linux0.11版本中,关于函数do_timer的疑问

void do_timer (long cpl){

...

fn = next_timer-fn;

next_timer-fn = NULL;

next_timer = next_timer-next;

(fn)();

...

}

这个函数中的函数指针是在函数void add_timer(long jiffies, void (*fn)(void))中添加的。这个函数是供内核使用的,用户空间的函数是不能调用add_timer的,也就是说 do_timer (long cpl)函数中的函数指针只会指向内核里面的函数。

(我去看了linux0.11的源码,用的地方确实只有

Floppy.c (kernel\blk_drv): add_timer(2,transfer);

Floppy.c (kernel\blk_drv): add_timer(ticks_to_floppy_on(current_drive),floppy_on_interrupt);

Sched.c (kernel):void add_timer(long jiffies, void (*fn)(void))

Sched.h (include\linux):extern void add_timer(long jiffies, void (*fn)(void));

所以的你的“内核不能直接访问用户空间函数问题”是不存在的。

我也是看到了这个帖子才想到的。

刚开始学这个,可能说的不对,多包涵。

如何截取linux时钟中断

Linux下时钟中断处理由time_interrupt() 函数实现,主要完成以下任务:

l 获得xtime_lock锁,以便对访问的jiffies_64 (kernel2.6)和 xtime进行保护

l 需要时应答或重新设置系统时钟。

l 周期性的使用系统时间(wall_time)更新实时时钟RTC

l 调用体系结构无关的时钟例程:do_timer()。

do_timer()主要完成以下任务:

l 更新jiffies;

l 更新系统时间(wall_time),该时间存放在xtime变量中

l 执行已经到期的动态定时器

l 计算平均负载值

发表评论

评论列表

  • 嘻友卮留(2022-07-14 08:36:03)回复取消回复

    时钟中断中,调度相关的一些时间计数量会被更新。同时会检查一下目前运行的进程运行时间是不是超过了一个slice,如果超过了这个间隔,就会设置重新调度标记。会在schedule函数中完成调度并完成进程切换。如果没有小于这个slice量,那就不会触发重

  • 嘻友诗呓(2022-07-14 05:29:17)回复取消回复

    spin_unlock (i8253_lock); if (count = = LATCH) {

  • 性许离鸢(2022-07-14 04:46:04)回复取消回复

    叫定时器中断或者时钟中断。它用来告诉内核一个间隔过去了。这个时间间隔也叫做一个滴答数。可以通过编译内核是选择内核频率来确定。如内核频率设为1000HZ,则时间间隔或滴答为1/1000=1微秒。滴答月短. 定时精度更高.