Linux时间子系统之四:Timer在用户和内核空间流程
用户空间应用中创建一个Timer(alarm/setitimer/POSIX Timer等等),然后程序继续执行;
内核进入创建/设置Timer系统调用,开始计时,在超时后通过何种方式通知用户空间;
用户空间又是如何执行回调函数的。
下面就着重这个流程,梳理一下Timer周期中用户空间和内核空间涉及到的相关模块。
1. 总体框架
关注的Timer(alarm/setitimer/POSIX Timer),都在libc/librt/libphtread中定义。librt是POSIX.1b Realtime扩展的实现,这其中就包括POSIX Timer。
其中alarm/setitimer都调用libc,POSIX Timer调用librt/libpthread。
总体框架如下:
❶应用调用库通过系统调用创建Timer,同时自身注册信号处理函数。
❷库提供通用接口,转换成系统调用。
❸内核Timer相关系统调用(setitimer/timer_create),通过hrtimer创建相应的定时器,在超时后调用hrtimer超时函数发送signal给用户空间进程。
❹用户空间进程在收到信号之后,执行对应的信号处理函数。
至此,Timer一个闭环完成。
下面分alarm/setitimer和POSIX Timer两种类型的Timer,来介绍其流程。
2. alarm/setitimer流程
linux/common/alarm.c中实现了alarm,可以看到和setitimer相同的接口。
- #ifdef __NR_alarm
- _syscall1(unsigned int, alarm, unsigned int, seconds)
- #else
- #include <sys/time.h>
- unsigned int alarm(unsigned int seconds)
- {
- ...
- if (setitimer(ITIMER_REAL, &new, &old) < ) {
- return ;
- }
- ...
- }
- #endif
即使定义了alarm系统调用,在内核中alarm和setitimer也是调用相同的do_setitimer。所以这两个API在内核的实现是一致的。
- SYSCALL_DEFINE1(alarm, unsigned int, seconds)
- {
- return alarm_setitimer(seconds);
- }
- unsigned int alarm_setitimer(unsigned int seconds)
- {
- ...
- do_setitimer(ITIMER_REAL, &it_new, &it_old);
- ...
- }
- SYSCALL_DEFINE3(setitimer, int, which, struct itimerval __user *, value,
- struct itimerval __user *, ovalue)
- {
- ...
error = do_setitimer(which, &set_buffer, ovalue ? &get_buffer : NULL);- ...
}
所以研究do_setitimer就可以分析这两个API的内核实现。
- int do_setitimer(int which, struct itimerval *value, struct itimerval *ovalue)
- {
- ...
- switch (which) {
- case ITIMER_REAL:
- again:
- spin_lock_irq(&tsk->sighand->siglock);
- timer = &tsk->signal->real_timer;---------------------------------这里是task_struct结构体中的real_timer这个hrtimer。所以alarm/setitimer一个进程/线程空间中只能存在一个。
- if (ovalue) {
- ovalue->it_value = itimer_get_remtime(timer);
- ovalue->it_interval
- = ktime_to_timeval(tsk->signal->it_real_incr);
- }
- /* We are sharing ->siglock with it_real_fn() */
- if (hrtimer_try_to_cancel(timer) < ) {
- spin_unlock_irq(&tsk->sighand->siglock);
- goto again;
- }
- expires = timeval_to_ktime(value->it_value);
- if (expires.tv64 != ) {
- tsk->signal->it_real_incr =
- timeval_to_ktime(value->it_interval);
- hrtimer_start(timer, expires, HRTIMER_MODE_REL);--------------启动alarm/setitimer对应的hrtimer。
- } else
- tsk->signal->it_real_incr.tv64 = ;
- trace_itimer_state(ITIMER_REAL, value, );
- spin_unlock_irq(&tsk->sighand->siglock);
- break;
- ...
- }
- return ;
- }
那么real_timer这个hrtimer的处理函数在何时初始化的呢?
可以看出在进程创建的时候,已经初始化了real_timer。对应的超时函数是it_real_fn,发送SIGALRM信号给对应的进程。然后用户空间执行SIGALRM处理函数。
- do_fork-->
- copy_process-->
- copy_signal-->
- static int copy_signal(unsigned long clone_flags, struct task_struct *tsk)
- {
- ...
- hrtimer_init(&sig->real_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);--------初始化real_timer定时器。
- sig->real_timer.function = it_real_fn;
- ...
- }
- /*
- * The timer is automagically restarted, when interval != 0
- */
- enum hrtimer_restart it_real_fn(struct hrtimer *timer)
- {
- struct signal_struct *sig =
- container_of(timer, struct signal_struct, real_timer);
- trace_itimer_expire(ITIMER_REAL, sig->leader_pid, );
- kill_pid_info(SIGALRM, SEND_SIG_PRIV, sig->leader_pid);--------------------发送SIGALRM信号。
- return HRTIMER_NORESTART;
- }
3. POSIX Timer流程
POSIX Timer在libpthread\nptl\sysdeps\unix\sysv\linux\timer_create.c中创建。
- int
- timer_create (
- clockid_t clock_id,
- struct sigevent *evp,
- timer_t *timerid)
- {
- # undef timer_create
- # ifndef __ASSUME_POSIX_TIMERS
- if (__no_posix_timers >= )
- # endif
- {
- ...
- kernel_timer_t ktimerid;
- int retval = INLINE_SYSCALL (timer_create, , syscall_clockid, evp,-------------通过timer_create系统调用创建Timer。
- &ktimerid);
- ...
- }
- else
- {
- # ifndef __ASSUME_POSIX_TIMERS
- ...
- if (__no_posix_timers > )
- # endif
- {
- /* Create the helper thread. */
- pthread_once (&__helper_once, __start_helper_thread);----------------------单独创建线程来处理,一次初始化。
- if (__helper_tid == )
- {
- /* No resources to start the helper thread. */
- __set_errno (EAGAIN);
- return -;
- }
- ...
- res = INTERNAL_SYSCALL (timer_create, err, ,------------------------------通过timer_create系统调用来创建Timer。
- syscall_clockid, &sev, &newp->ktimerid);
- ...
- }
- }
- }
- # ifndef __ASSUME_POSIX_TIMERS
- /* Compatibility code. */
- return compat_timer_create (clock_id, evp, timerid);
- # endif
- }
- __start_helper_thread-->
timer_helper_thread-->
timer_sigev_thread-----------------------------------------------------------此线程在定时器超时后,才会创建。
thrfunc------------------------------------------------------------------调用用户提供的超时回调函数。
再来看看内核中的实现,这里主要看common_timer_create和alarm_timer_create两种类型。
- SYSCALL_DEFINE3(timer_create, const clockid_t, which_clock,
- struct sigevent __user *, timer_event_spec,
- timer_t __user *, created_timer_id)
- {
- ...
if (timer_event_spec) {- if (copy_from_user(&event, timer_event_spec, sizeof (event))) {
- error = -EFAULT;
- goto out;
- }
- rcu_read_lock();
- new_timer->it_pid = get_pid(good_sigevent(&event));------------如果用户提供了sigevent,获取pid。
- rcu_read_unlock();
- if (!new_timer->it_pid) {
- error = -EINVAL;
- goto out;
- }
- } else {
- memset(&event.sigev_value, , sizeof(event.sigev_value));
- event.sigev_notify = SIGEV_SIGNAL;
- event.sigev_signo = SIGALRM;
- event.sigev_value.sival_int = new_timer->it_id;
- new_timer->it_pid = get_pid(task_tgid(current));--------------如果没有提供sigevent,使用默认的SIGALRM。
- }
- new_timer->it_sigev_notify = event.sigev_notify;
- new_timer->sigq->info.si_signo = event.sigev_signo;
- new_timer->sigq->info.si_value = event.sigev_value;
- new_timer->sigq->info.si_tid = new_timer->it_id;
- new_timer->sigq->info.si_code = SI_TIMER;
- if (copy_to_user(created_timer_id,--------------------------------返回timer_id给用户空间
- &new_timer_id, sizeof (new_timer_id))) {
- error = -EFAULT;
- goto out;
- }
- error = kc->timer_create(new_timer);-----------------------------------------调用common_timer_create或者alarm_timer_create创建定时器
- if (error)
- goto out;
- ...
}- static int common_timer_create(struct k_itimer *new_timer)
- {
- hrtimer_init(&new_timer->it.real.timer, new_timer->it_clock, );-------------初始化hrtimer
- return ;
- }
那么超时函数在哪里设置的呢?
- static int
- common_timer_set(struct k_itimer *timr, int flags,
- struct itimerspec *new_setting, struct itimerspec *old_setting)
- {
- ...
mode = flags & TIMER_ABSTIME ? HRTIMER_MODE_ABS : HRTIMER_MODE_REL;- hrtimer_init(&timr->it.real.timer, timr->it_clock, mode);-------------------重新初始化hrtimer
- timr->it.real.timer.function = posix_timer_fn;------------------------------hrtimer回调函数
- hrtimer_set_expires(timer, timespec_to_ktime(new_setting->it_value));
- ...
- }
在回调函数中,进行了超时处理。
- static enum hrtimer_restart posix_timer_fn(struct hrtimer *timer)
- {
- ...
- if (posix_timer_event(timr, si_private)) {----------------------------------发送信号
- ...
- }
- }
- ...
- }
- int posix_timer_event(struct k_itimer *timr, int si_private)
- {
- ...
- timr->sigq->info.si_sys_private = si_private;
- rcu_read_lock();
- task = pid_task(timr->it_pid, PIDTYPE_PID);-------------------------------根据pid获取task实体
- if (task) {
- shared = !(timr->it_sigev_notify & SIGEV_THREAD_ID);
- ret = send_sigqueue(timr->sigq, task, shared);------------------------将当前信号队列发送到对应的用户空间对应的task,进程在收到信号后进行相应处理
- }
- rcu_read_unlock();
- /* If we failed to send the signal the timer stops. */
- return ret > ;
- }
那么如果是Alarm类型的Timer,情况如何呢?
- static int alarm_timer_create(struct k_itimer *new_timer)
- {
- ...
- alarm_init(&new_timer->it.alarm.alarmtimer, type, alarm_handle_timer);-----------初始化alarmtimer,回调函数是alarm_handle_timer
- ...
- }
- static enum alarmtimer_restart alarm_handle_timer(struct alarm *alarm,
- ktime_t now)
- {
- ...
- if ((ptr->it_sigev_notify & ~SIGEV_THREAD_ID) != SIGEV_NONE) {
- if (posix_timer_event(ptr, ) != )------------------------------------------和其他POSIX Timer一样发送signal信号
- ptr->it_overrun++;
- }
- ...
- }
- static int alarm_timer_set(struct k_itimer *timr, int flags,
- struct itimerspec *new_setting,
- struct itimerspec *old_setting)
- {
- ...
- alarm_start(&timr->it.alarm.alarmtimer, exp);-------------------------------------启动AlarmTimer
- ...
- }
4. 总结
所以无论是alarm/setitimer,还是POSIX Timer都是通过发送signal来通知用户应用。只是用户空间处理消息的方式有所不同。
由于Timer经过库的封装,不光要看内核,还需要研究库对API进行了何种封装。才能更好的了解其行为。
也由于库的种类(lig/glib/ulib等)和版本千差万别,所以也需要引起重视。
一个关于libpthread引起的POSIX Timer执行异常情况。
描述:在一个进程中创建三个SIGEV_THREAD类型POSIX Timer,但是超时只执行一个回调函数。其他两个没有被调用。
问题分析:SIGCANCEL这个信号导致,timer_create创建helper thread失败。Timer超时后,回调函数也不会被执行。
解决方法:
- void
- attribute_hidden
- __start_helper_thread (void)
- {
- ...
- sigset_t ss;
- sigset_t oss;
- sigfillset (&ss);
- /*__sigaddset (&ss, SIGCANCEL); - already done by sigfillset */
- __sigaddset (&ss, SIGCANCEL);--------------------------------修改方法
- ...
}
Linux时间子系统之四:Timer在用户和内核空间流程的更多相关文章
- Linux时间子系统之四:定时器的引擎:clock_event_device
早期的内核版本中,进程的调度基于一个称之为tick的时钟滴答,通常使用时钟中断来定时地产生tick信号,每次tick定时中断都会进行进程的统计和调度,并对tick进行计数,记录在一个jiffies变量 ...
- Linux时间子系统之四:定时器的引擎:clock_event_device【转】
本文转载自:http://blog.csdn.net/droidphone/article/details/8017604 版权声明:本文为博主原创文章,未经博主允许不得转载. 目录(?)[+] ...
- Linux时间子系统专题汇总
关于Linux时间子系统有两个系列文章讲的非常好,分别是WowoTech和DroidPhone. 还有两本书分别是介绍: Linux用户空间时间子系统<Linux/UNIX系统编程手册>的 ...
- Linux时间子系统之六:高精度定时器(HRTIMER)的原理和实现
转自:http://blog.csdn.net/droidphone/article/details/8074892 上一篇文章,我介绍了传统的低分辨率定时器的实现原理.而随着内核的不断演进,大牛们已 ...
- Linux时间子系统之五:低分辨率定时器的原理和实现
专题文档汇总目录 Notes:低精度timer在内核中的数据结构以及API接口:低精度timer精巧高效的分组,使用cascade进行定时器移位,组内Timer FIFO:低精度Timer的初始化流程 ...
- Linux时间子系统之八:动态时钟框架(CONFIG_NO_HZ、tickless)
在前面章节的讨论中,我们一直基于一个假设:Linux中的时钟事件都是由一个周期时钟提供,不管系统中的clock_event_device是工作于周期触发模式,还是工作于单触发模式,也不管定时器系统是工 ...
- Linux时间子系统之八:动态时钟框架(CONFIG_NO_HZ、tickless)【转】
转自:http://blog.csdn.net/droidphone/article/details/8112948 版权声明:本文为博主原创文章,未经博主允许不得转载. 目录(?)[-] 数据结 ...
- Linux时间子系统之(三):用户空间接口函数
专题文档汇总目录 Notes:用户空间时间相关接口函数: 类型 API 精度 说明 时间 time stime time_t 精度为秒级 逐渐要被淘汰.需要定义__ARCH_WANT_SYS_TIME ...
- Linux时间子系统之(六):POSIX timer
专题文档汇总目录 Notes:首先讲解了POSIX timer的标识(唯一识别).POSIX Timer的组织(管理POSIX Timer).内核中如何抽象POSIX Timer:然后分析了POSIX ...
随机推荐
- 根据分析查看相关知识点分析iOS 三种录制视频方式
这篇文章讨论了关于如何配置视频捕获管线 (pipeline) 和最大限度地利用硬件性能的一些不同选择. 这里有个使用了不同管线的样例 app,可以在 GitHub 查看. 第一种:UIImagePic ...
- 第一个Polymer应用 - (0)准备工作
原文链接: Getting Started - Your first Polymer application翻译时间: 2014年7月5日翻译人员: 铁锚 关于Polymer 的简介,请参考 CSD ...
- PS 滤镜算法原理——照亮边缘
这个算法原理很简单,对彩色图像的R,G,B 三个通道,分别求梯度,然后将梯度值作为三个通道的值. clc; clear all;Image=imread('4.jpg');Image=double(I ...
- 和菜鸟一起学产品之用户体验设计UED
ps:参考产品经理深入浅出ppt
- IIS服务器如何抗住高并发的客户端访问
今天被问到一个问题,如果你在阿里云上部署了一个IIS服务器,此时如果有成千上万的客户端来访问,你将如何设计?我东扯扯西谈谈,说加个线程池来处理,在加个请求队列.当时觉得说的没有问题,现在想想,服务器自 ...
- async & await 异步编程的一点巧方法
await 关键字不会创建新的线程,而是由Task任务或是FCL中的xxxAsync等方法创建的线程,而且这里创建的线程都是基于线程池创建的工作线程,属于后台线程. await关键字会阻塞/暂停调用它 ...
- 布局display属性(一)--【Flex】
一.Flex 布局是什么? Flex 是 Flexible Box 的缩写,意为"弹性布局",用来为盒状模型提供最大的灵活性. 任何一个容器都可以指定为 Flex 布局. .box ...
- 用nodejs实现简单爬虫
前言 本喵最近工作中需要使用node,并也想晋升为全栈工程师,所以开始了node学习之旅,在学习过程中, 我会总结一些实用的例子,做成博文和视频教程,以实例形式来理解体会node的用法,所以跟小猫 ...
- HTMLConverter使用实例(转)
---- 本来,Applet的概念相当简单——只要在Web页面中加入一个< APPLET >标记就可以了.浏览器一遇到这个标记,就会下载对应的 Applet类文件,并启动自己的解释器运行这 ...
- 【计算机视觉】深度相机(一)--TOF总结
http://www.voidcn.com/blog/lg1259156776/article/p-6302915.html 1.1 TOF初探 TOF是Time of flight的简写,直译为飞行 ...