Linux时间子系统之一:定时器的应用.doc

上传人:白大夫 文档编号:3255371 上传时间:2019-08-06 格式:DOC 页数:7 大小:31.50KB
返回 下载 相关 举报
Linux时间子系统之一:定时器的应用.doc_第1页
第1页 / 共7页
亲,该文档总共7页,到这儿已超出免费预览范围,如果喜欢就下载吧!
资源描述

《Linux时间子系统之一:定时器的应用.doc》由会员分享,可在线阅读,更多相关《Linux时间子系统之一:定时器的应用.doc(7页珍藏版)》请在三一文库上搜索。

1、Linux时间子系统之一:定时器的应用我们知道低分辨率定时器和高精度定时器的实现原理,内核为了方便其它子系统,在时间子系统中提供了一些用于延时或调度的API,例如msleep,hrtimer_nanosleep等等,这些API基于低分辨率定时器或高精度定时器来实现,本章的内容就是讨论这些方便、好用的API是如何利用定时器系统来完成所需的功能的。1. msleepmsleep相信大家都用过,它可能是内核用使用最广泛的延时函数之一,它会使当前进程被调度并让出cpu一段时间,因为这一特性,它不能用于中断上下文,只能用于进程上下文中。要想在中断上下文中使用延时函数,请使用会阻塞cpu的无调度版本mde

2、lay。msleep的函数原型如下:cppview plaincopyvoidmsleep(unsignedintmsecs)延时的时间由参数msecs指定,单位是毫秒,事实上,msleep的实现基于低分辨率定时器,所以msleep的实际精度只能也是1/HZ级别。内核还提供了另一个比较类似的延时函数msleep_interrupTIble:cppview plaincopyunsignedlongmsleep_interrupTIble(unsignedintmsecs)延时的单位同样毫秒数,它们的区别如下:函数延时单位返回值是否可被信号中断msleep毫秒无否msleep_interrupT

3、Ible毫秒未完成的毫秒数是最主要的区别就是msleep会保证所需的延时一定会被执行完,而msleep_interrupTIble则可以在延时进行到一半时被信号打断而退出延时,剩余的延时数则通过返回值返回。两个函数最终的代码都会到达schedule_timeout函数,它们的调用序列如下图所示: 图1.1 两个延时函数的调用序列下面我们看看schedule_timeout函数的实现,函数首先处理两种特殊情况,一种是传入的延时jiffies数是个负数,则打印一句警告信息,然后马上返回,另一种是延时jiffies数是MAX_SCHEDULE_TIMEOUT,表明需要一直延时,直接执行调度即可:cp

4、pview plaincopysignedlong_schedschedule_timeout(signedlongtimeout)structtimer_listtimer;unsignedlongexpire;switch(timeout)caseMAX_SCHEDULE_TIMEOUT:schedule();gotoout;default:if(timeoutstate=TASK_RUNNING;gotoout;然后计算到期的jiffies数,并在堆栈上建立一个低分辨率定时器,把到期时间设置到该定时器中,启动定时器后,通过schedule把当前进程调度出cpu的运行队列:cppview

5、plaincopyexpire=timeout+jiffies;setup_timer_on_stack(_mod_timer(schedule();到这个时候,进程已经被调度走,那它如何返回继续执行?我们看到定时器的到期回调函数是process_timeout,参数是当前进程的task_struct指针,看看它的实现:cppview plaincopystaticvoidprocess_timeout(unsignedlong_data)wake_up_process(structtask_struct*)_data);噢,没错,定时器一旦到期,进程会被唤醒并继续执行:cppview pla

6、incopydel_singleshot_timer_sync(/*Removethetimerfromtheobjecttracker*/destroy_timer_on_stack(timeout=expire-jiffies;out:returntimeouttimeout=schedule_timeout_interruptible(timeout);returnjiffies_to_msecs(timeout);msleep_interruptible通过schedule_timeout_interruptible中转,schedule_timeout_interruptible的唯

7、一区别就是把进程的状态设置为了TASK_INTERRUPTIBLE,说明在延时期间有信号通知,while循环会马上终止,剩余的jiffies数被转换成毫秒返回。实际上,你也可以利用schedule_timeout_interruptible或schedule_timeout_uninterruptible构造自己的延时函数,同时,内核还提供了另外一个类似的函数,不用我解释,看代码就知道它的用意了:cppview plaincopysignedlong_schedschedule_timeout_killable(signedlongtimeout)_set_current_state(TASK

8、_KILLABLE);returnschedule_timeout(timeout);2. hrtimer_nanosleep第一节讨论的msleep函数基于时间轮定时系统,只能提供毫秒级的精度,实际上,它的精度取决于HZ的配置值,如果HZ小于1000,它甚至无法达到毫秒级的精度,要想得到更为精确的延时,我们自然想到的是要利用高精度定时器来实现。没错,linux为用户空间提供了一个api:nanosleep,它能提供纳秒级的延时精度,该用户空间函数对应的内核实现是sys_nanosleep,它的工作交由高精度定时器系统的hrtimer_nanosleep函数实现,最终的大部分工作则由do_na

9、nosleep完成。调用过程如下图所示:图 2.1 nanosleep的调用过程与msleep的实现相类似,hrtimer_nanosleep函数首先在堆栈中创建一个高精度定时器,设置它的到期时间,然后通过do_nanosleep完成最终的延时工作,当前进程在挂起相应的延时时间后,退出do_nanosleep函数,销毁堆栈中的定时器并返回0值表示执行成功。不过do_nanosleep可能在没有达到所需延时数量时由于其它原因退出,如果出现这种情况,hrtimer_nanosleep的最后部分把剩余的延时时间记入进程的restart_block中,并返回ERESTART_RESTARTBLOCK错

10、误代码,系统或者用户空间可以根据此返回值决定是否重新调用nanosleep以便把剩余的延时继续执行完成。下面是hrtimer_nanosleep的代码:cppview plaincopylonghrtimer_nanosleep(structtimespec*rqtp,structtimespec_user*rmtp,constenumhrtimer_modemode,constclockid_tclockid)structrestart_block*restart;structhrtimer_sleepert;intret=0;unsignedlongslack;slack=current-

11、timer_slack_ns;if(rt_task(current)slack=0;hrtimer_init_on_stack(hrtimer_set_expires_range_ns(if(do_nanosleep(gotoout;/*Absolutetimersdonotupdatethermtpvalueandrestart:*/if(mode=HRTIMER_MODE_ABS)ret=-ERESTARTNOHAND;gotoout;if(rmtp)ret=update_rmtp(if(retrestart_block;restart-fn=hrtimer_nanosleep_resta

12、rt;restart-nanosleep.clockid=t.timer.base-clockid;restart-nanosleep.rmtp=rmtp;restart-nanosleep.expires=hrtimer_get_expires_tv64(ret=-ERESTART_RESTARTBLOCK;out:destroy_hrtimer_on_stack(returnret;接着我们看看do_nanosleep的实现代码,它首先通过hrtimer_init_sleeper函数,把定时器的回调函数设置为hrtimer_wakeup,把当前进程的task_struct结构指针保存在hr

13、timer_sleeper结构的task字段中:cppview plaincopyvoidhrtimer_init_sleeper(structhrtimer_sleeper*sl,structtask_struct*task)sl-timer.function=hrtimer_wakeup;sl-task=task;EXPORT_SYMBOL_GPL(hrtimer_init_sleeper);staticint_scheddo_nanosleep(structhrtimer_sleeper*t,enumhrtimer_modemode)hrtimer_init_sleeper(t,curr

14、ent);然后,通过一个do/while循环内:启动定时器,挂起当前进程,等待定时器或其它事件唤醒进程。这里的循环体实现比较怪异,它使用hrtimer_active函数间接地判断定时器是否到期,如果hrtimer_active返回false,说明定时器已经过期,然后把hrtimer_sleeper结构的task字段设置为NULL,从而导致循环体的结束,另一个结束条件是当前进程收到了信号事件,所以,当因为是定时器到期而退出时,do_nanosleep返回true,否则返回false,上述的hrtimer_nanosleep正是利用了这一特性来决定它的返回值。以下是do_nanosleep循环体的

15、代码:cppview plaincopydoset_current_state(TASK_INTERRUPTIBLE);hrtimer_start_expires(if(!hrtimer_active(t-task=NULL;if(likely(t-task)schedule();hrtimer_cancel(mode=HRTIMER_MODE_ABS;while(t-task_set_current_state(TASK_RUNNING);returnt-task=NULL;除了hrtimer_nanosleep,高精度定时器系统还提供了几种用于延时/挂起进程的api:schedule_hr

16、timeout 使得当前进程休眠指定的时间,使用CLOCK_MONOTONIC计时系统;schedule_hrtimeout_range 使得当前进程休眠指定的时间范围,使用CLOCK_MONOTONIC计时系统;schedule_hrtimeout_range_clock 使得当前进程休眠指定的时间范围,可以自行指定计时系统;usleep_range使得当前进程休眠指定的微妙数,使用CLOCK_MONOTONIC计时系统;它们之间的调用关系如下:图 2.2 schedule_hrtimeout_xxxx系列函数最终,所有的实现都会进入到schedule_hrtimeout_range_clo

17、ck函数。需要注意的是schedule_hrtimeout_xxxx系列函数在调用前,最好利用set_current_state函数先设置进程的状态,在这些函数返回前,进城的状态会再次被设置为TASK_RUNNING。如果事先把状态设置为TASK_UNINTERRUPTIBLE,它们会保证函数返回前一定已经经过了所需的延时时间,如果事先把状态设置为TASK_INTERRUPTIBLE,则有可能在尚未到期时由其它信号唤醒进程从而导致函数返回。主要实现该功能的函数schedule_hrtimeout_range_clock和前面的do_nanosleep函数实现原理基本一致。大家可以自行参考内核的代码,它们位于:kernel/hrtimer.c。

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 其他


经营许可证编号:宁ICP备18001539号-1