热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Linux之时钟中断详解-Linux

Linux之时钟中断详解是千自学中一篇关于Linux的文章简介:在Linux的0号中断是一个定时器中断。在固定的时间间隔都发生一次中断,也是说每秒发生该中断的频率都是固定的。该频率是常量HZ,该值一般是在100~1000之间。该中断的作用是为了定时更新系统日期和时间,使系统时间不断地得到跳转。另外该中断的中断处理函数除了

在Linux的0号中断是一个定时器中断。在固定的时间间隔都发生一次中断,也是说每秒发生该中断的频率都是固定的。该频率是常量HZ,该值一般是在100~1000之间。该中断的作用是为了定时更新系统日期和时间,使系统时间不断地得到跳转。另外该中断的中断处理函数除了更新系统时间外,还需要更新本地CPU统计数。指的是调用scheduler_tick递减进程的时间片,若进程的时间片递减到0,进程则被调度出去而放弃CPU使用权。

时钟中断的产生

Linux的OS时钟的物理产生原因是可编程定时/计数器产生的输出脉冲,这个脉冲送入CPU,就可以引发一个中断请求信号,我们就把它叫做时钟中断。

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

时钟中断的物理产生如图所示:

操作系统对可编程定时/计数器进行有关初始化,然后定时/计数器就对输入脉冲进行计数(分频),产生的三个输出脉冲Out0、Out1、Out2各有用途,很多接口书都介绍了这个问题,我们只看Out0上的输出脉冲,这个脉冲信号接到中断控制器8259A_1的0号管脚,触发一个周期性的中断,我们就把这个中断叫做时钟中断,时钟中断的周期,也就是脉冲信号的周期,我们叫做“滴答”或“时标”(tick)。从本质上说,时钟中断只是一个周期性的信号,完全是硬件行为,该信号触发CPU去执行一个中断服务程序,但是为了方便,我们就把这个服务程序叫做时钟中断。

Linux实现时钟中断的全过程 1.可编程定时/计数器的初始化

IBMPC中使用的是8253或8254芯片。有关该芯片的详细知识我们不再详述,只大体介绍以下它的组成和作用,如下表5.1所示:

表8253/8254的组成及作用

名称

端口地址

工作方式

产生的输出脉冲的用途

计数器0

0x40

方式3

时钟中断,也叫系统时钟

计数器1

0x41

方式2

动态存储器刷新

计数器2

0x42

方式3

扬声器发声

控制寄存器

0x43

/

用于8253的初始化,接收控制字

计数器0的输出就是图中的Out0,它的频率由操作系统的设计者确定,Linux对8253的初始化程序段如下(在/arch/i386/kernel/i8259.c的init_IRQ()函数中):

set_intr_gate(ox20,interrupt[0]); /*在IDT的第0x20个表项中插入一个中断门。这个门中的段选择符设置成内核代码段的选择符,偏移域设置成0号中断处理程序的入口地址。*/ outb_p(0x34,0x43);/*写计数器0的控制字:工作方式2*/ outb_p(LATCH0xff,0x40);/*写计数初值LSB计数初值低位字节*/ outb(LATCH8,0x40);/*写计数初值MSB计数初值高位字节*/ LATCH(英文意思为:锁存器,即其中锁存了计数器0的初值)为计数器0的计数初值,在/include/linux/timex.h中定义如下: #defineCLOCK_TICK_RATE1193180/*图5.3中的输入脉冲*/ #defineLATCH((CLOCK_TICK_RATE+HZ/2)/HZ)/*计数器0的计数初值*/

CLOCK_TICK_RATE是整个8253的输入脉冲,如图5.3中所示为1.193180MHz,是近似为1MHz的方波信号,8253内部的三个计数器都对这个时钟进行计数,进而产生不同的输出信号,用于不同的用途。

HZ表示计数器0的频率,也就是时钟中断或系统时钟的频率,在/include/asm/param.h中定义如下:

#defineHZ100

2.与时钟中断相关的函数

下面我们看时钟中断触发的服务程序,该程序代码比较复杂,分布在不同的源文件中,主要包括如下函数:

时钟中断程序:timer_interrupt();

中断服务通用例程do_timer_interrupt();

时钟函数:do_timer();

中断安装程序:setup_irq();

中断返回函数:ret_from_intr();

(1)timer_interrupt()

这个函数大约每10ms被调用一次,实际上,timer_interrupt()函数是一个封装例程,它真正做的事情并不多,但是,作为一个中断程序,它必须在关中断的情况下执行。如果只考虑单处理机的情况,该函数主要语句就是调用do_timer_interrupt()函数。

(2)do_timer_interrupt()

do_timer_interrupt()函数有两个主要任务,一个是调用do_timer(),另一个是维持实时时钟(RTC,每隔一定时间段要回写),其实现代码在/arch/i386/kernel/time.c中,为了突出主题,笔者对以下函数作了改写,以便于读者理解:

staticinlinevoiddo_timer_interrupt(intirq,void*dev_id,structpt_regs*regs) { do_timer(regs);/*调用时钟函数,将时钟函数等同于时钟中断未尝不可*/ if(xtime.tv_seclast_rtc_update+660) update_RTC(); /*每隔11分钟就更新RTC中的时间信息,以使OS时钟和RTC时钟保持同步,11分钟即660秒,xtime.tv_sec的单位是秒,last_rtc_update记录的是上次RTC更新时的值*/ }

其中,xtime是前面所提到的timeval类型,这是一个全局变量。

(3)时钟函数do_timer()(在/kernel/sched.c中)

voiddo_timer(structpt_regs*regs) { (*(unsignedlong*)jiffies)++;/*更新系统时间,这种写法保证对jiffies 操作的原子性*/ update_process_times(); ++lost_ticks; if(!user_mode(regs)) ++lost_ticks_system; mark_bh(TIMER_BH); if(tq_timer) mark_bh(TQUEUE_BH); }

其中,update_process_times()函数与进程调度有关,从函数的名子可以看出,它处理的是与当前进程与时间有关的变量,例如,要更新当前进程的时间片计数器counter,如果counter=0,则要调用调度程序,要处理进程的所有定时器:实时、虚拟、概况,另外还要做一些统计工作。

与时间有关的事情很多,不能全都让这个函数去完成,这是因为这个函数是在关中断的情况下执行,必须处理完最重要的时间信息后退出,以处理其他事情。那么,与时间相关的其他信息谁去处理,何时处理?这就是由第三章讨论的后半部分去去处理。上面timer_interrupt()(包括它所调用的函数)所做的事情就是上半部分。

在该函数中还有两个变量lost_ticks和lost_ticks_system,这是用来记录timer_bh()执行前时钟中断发生的次数。因为时钟中断发生的频率很高(每10ms一次),所以在timer_bh()执行之前,可能已经有时钟中断发生了,而timer_bh()要提供定时、记费等重要操作,所以为了保证时间计量的准确性,使用了这两个变量。lost_ticks用来记录timer_bh()执行前时钟中断发生的次数,如果时钟中断发生时当前进程运行于内核态,则lost_ticks_system用来记录timer_bh()执行前在内核态发生时钟中断的次数,这样可以对当前进程精确记费。

(4)中断安装程序

从上面的介绍可以看出,时钟中断与进程调度密不可分,因此,一旦开始有时钟中断就可能要进行调度,在系统进行初始化时,所做的大量工作之一就是对时钟进行初始化,其函数time_init()的代码在/arch/i386/kernel/time.c中,对其简写如下:

void__inittime_init(void) { xtime.tv_sec=get_cmos_time(); xtime.tv_usec=0; setup_irq(0,&irq0); }

其中的get_cmos_time()函数就是把当时的实际时间从CMOS时钟芯片读入变量xtime中,时间精度为秒。而setup_irq(0,&irq0)就是时钟中断安装函数,那么irq0指的是什么呢,它是一个结构类型irqaction,其定义及初值如下:

staticstructirqactionirq0={timer_interrupt,SA_INTERRUPT,0,"timer",NULL,NULL};

setup_irq(0,irq0)的代码在/arch/i386/kernel/irq.c中,其主要功能就是将中断程序连入相应的中断请求队列,以等待中断到来时相应的中断程序被执行。

structirqaction{ irq_handler_thandler;//中断处理函数,注册时提供 unsignedlongflags;//中断标志,注册时提供 cpumask_tmask;//中断掩码 constchar*name;//中断名称 void*dev_id;//设备id,本文后面部分介绍中断共享时会详细说明这个参数的作用 structirqaction*next;//如果有中断共享,则继续执行, intirq;//中断号,注册时提供 structproc_dir_entry*dir;//指向IRQn相关的/proc/irq/n目录的描述符 };

这个结构体包含了处理一种中断所需要的各种信息,它代表了内核接受到特定IRQ之后应该采取的操作。

1.handler:该指针所指向的函数就是在中断服务程序,当中断发生时内核便会调用这个指针指向的函数。

2.flags:该标志位可以是0,也可以是:

SA_INTERRUPT:表示此中断处理程序是一个快速中断处理程序,在2.6中默认情况下没有这个标志;设置该标志位,中断处理程序禁止任何中断运行,没有该标志,仅屏蔽正在运行的IRQ线;

SA_SAMPLE_RANDOM:表示这个中断对内核池有贡献,在中断时产生一些随机数;

SA_SHIRQ:此标志位表示允许多个中断服务程序共享一个中断号,如不设则一个程序对应一个中断线;

3.mask:在x86上不会用到。

4.name:产生中断的硬件的名字.

5.dev_id:该标志位主要在共享中断号时使用,即你设置flags=SA_SHIRQ时,有多个中断服务程序共享一个中断号时,内核就需要知道在用完中断程序后该删除那个中断服务程序。不共享时此成员为null。

6.next:如果flags=SA_SHIRQ,那么这就是指向对列中下一个structirqaction结构体的指针,否则为空。

7.irq:不用说这就是中断号了。

到现在为止,我们仅仅是把时钟中断程序挂入中断请求队列,什么时候执行,怎样执行,这是一个复杂的过程(参见第三章),为了让读者对时钟中断有一个完整的认识,我们忽略中间过程,而给出一个整体描述。我们将有关函数改写如下,体现时钟中断的大意:

do_timer_interrupt()/*这是一个伪函数*/ { SAVE_ALL/*保存处理机现场*/ intr_count+=1;/*这段操作不允许被中断*/ timer_interrupt()/*调用时钟中断程序*/ intr_count-=1; jmpret_from_intr/*中断返回函数*/ }

其中,jmpret_from_intr是一段汇编代码,也是一个较为复杂的过程,它最终要调用jmpret_from_sys_call,即系统调用返回函数,而这个函数与进程的调度又密切相关,,因此,我们重点分析jmpret_from_sys_call。

3.系统调用返回函数:

系统调用返回函数的源代码在/arch/i386/kernel/entry.S中

ENTRY(ret_from_sys_call) cli#need_reschedandsignalsatomictest cmpl$0,need_resched(%ebx) jnereschedule cmpl$0,sigpending(%ebx) jnesignal_return restore_all: RESTORE_ALL ALIGN signal_return: sti#wecangetherefromaninterrupthandler testl$(VM_MASK),EFLAGS(%esp) movl%esp,%eax jnev86_signal_return xorl%edx,%edx callSYMBOL_NAME(do_signal) jmprestore_all ALIGN v86_signal_return: callSYMBOL_NAME(save_v86_state) movl%eax,%esp xorl%edx,%edx callSYMBOL_NAME(do_signal) jmprestore_all …. reschedule: callSYMBOL_NAME(schedule)#test jmpret_from_sys_call

这一段汇编代码就是前面我们所说的“从系统调用返回函数”ret_from_sys_call,它是从中断、异常及系统调用返回时的通用接口。这段代码主体就是ret_from_sys_call函数,其执行过程中要调用其它一些函数(实际上是一段代码,不是真正的函数),在此我们列出相关的几个函数:

(1)ret_from_sys_call:主体

(2)reschedule:检测是否需要重新调度

(3)signal_return:处理当前进程接收到的信号

(4)v86_signal_return:处理虚拟86模式下当前进程接收到的信号

(5)RESTORE_ALL:我们把这个函数叫做彻底返回函数,因为执行该函数之后,就返回到当前进程的地址空间中去了。

可以看到ret_from_sys_call的主要作用有:

检测调度标志need_resched,决定是否要执行调度程序;处理当前进程的信号;恢复当前进程的环境使之继续执行。

最后我们再次从总体上浏览一下时钟中断:

每个时钟滴答,时钟中断得到执行。时钟中断执行的频率很高:100次/秒,时钟中断的主要工作是处理和时间有关的所有信息、决定是否执行调度程序以及处理下半部分。和时间有关的所有信息包括系统时间、进程的时间片、延时、使用CPU的时间、各种定时器,进程更新后的时间片为进程调度提供依据,然后在时钟中断返回时决定是否要执行调度程序。下半部分处理程序是Linux提供的一种机制,它使一部分工作推迟执行。时钟中断要绝对保证维持系统时间的准确性,而下半部分这种机制的提供不但保证了这种准确性,还大幅提高了系统性能。

总结

以上就是本文关于Linux之时钟中断详解的全部内容,希望对大家有所帮助。感兴趣的朋友可以继续参阅本站其他相关专题,如有不足之处,欢迎留言指出。感谢朋友们对本站的支持!

您可能感兴趣的文章:利用Linux中的crontab实现分布式项目定时任务功能linuxsudo密码输入时显示星号的操作方法如何在Linux下设置录音笔时间Linux定时任务Crontab的使用方法Linux/Unix关于时间和时间戳的命令行Linux定时执行任务at和crontab命令详解简单谈谈Linux内核定时器Linux中unzip解压时中文乱码的解决办法

推荐阅读
  • oracle 对硬件环境要求,Oracle 10G数据库软硬件环境的要求 ... [详细]
  • 问题描述现在,不管开发一个多大的系统(至少我现在的部门是这样的),都会带一个日志功能;在实际开发过程中 ... [详细]
  • Docker安全策略与管理
    本文探讨了Docker的安全挑战、核心安全特性及其管理策略,旨在帮助读者深入理解Docker安全机制,并提供实用的安全管理建议。 ... [详细]
  • 流处理中的计数挑战与解决方案
    本文探讨了在流处理中进行计数的各种技术和挑战,并基于作者在2016年圣何塞举行的Hadoop World大会上的演讲进行了深入分析。文章不仅介绍了传统批处理和Lambda架构的局限性,还详细探讨了流处理架构的优势及其在现代大数据应用中的重要作用。 ... [详细]
  • 协程作为一种并发设计模式,能有效简化Android平台上的异步代码处理。自Kotlin 1.3版本引入协程以来,这一特性基于其他语言的成熟理念,为开发者提供了新的工具,以增强应用的响应性和效率。 ... [详细]
  • 本文介绍了如何在 Lisp in a Box 中进行自定义设置,包括快捷键、行号显示和背景主题等。由于直接在 .emacs 文件中设置无效,需要在特定文件中进行配置。 ... [详细]
  • 本文介绍了如何使用Aspose库将Office文件(如Word、PowerPoint)转换为HTML文件,并详细说明了在转换过程中可能出现的乱码问题及其解决方案。 ... [详细]
  • Maven + Spring + MyBatis + MySQL 环境搭建与实例解析
    本文详细介绍如何使用MySQL数据库进行环境搭建,包括创建数据库表并插入示例数据。随后,逐步指导如何配置Maven项目,整合Spring框架与MyBatis,实现高效的数据访问。 ... [详细]
  • 本文详细介绍了如何在Oracle VM VirtualBox中实现主机与虚拟机之间的数据交换,包括安装Guest Additions增强功能,以及如何利用这些功能进行文件传输、屏幕调整等操作。 ... [详细]
  • CSS Border 属性:solid 边框的使用详解
    本文详细介绍了如何在CSS中使用solid边框属性,包括其基本语法、应用场景及高级技巧,适合初学者和进阶用户参考。 ... [详细]
  • 如何在Django框架中实现对象关系映射(ORM)
    本文介绍了Django框架中对象关系映射(ORM)的实现方式,通过ORM,开发者可以通过定义模型类来间接操作数据库表,从而简化数据库操作流程,提高开发效率。 ... [详细]
  • 二维码的实现与应用
    本文介绍了二维码的基本概念、分类及其优缺点,并详细描述了如何使用Java编程语言结合第三方库(如ZXing和qrcode.jar)来实现二维码的生成与解析。 ... [详细]
  • MySQL InnoDB 存储引擎索引机制详解
    本文深入探讨了MySQL InnoDB存储引擎中的索引技术,包括索引的基本概念、数据结构与算法、B+树的特性及其在数据库中的应用,以及索引优化策略。 ... [详细]
  • 深入探讨前端代码优化策略
    本文深入讨论了前端开发中代码优化的关键技术,包括JavaScript、HTML和CSS的优化方法,旨在提升网页加载速度和用户体验。 ... [详细]
  • PHP函数的工作原理与性能分析
    在编程语言中,函数是最基本的组成单元。本文将探讨PHP函数的特点、调用机制以及性能表现,并通过实际测试给出优化建议。 ... [详细]
author-avatar
wentingcool_533
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有