用户空间应用中创建一个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在用户和内核空间流程的更多相关文章

  1. Linux时间子系统之四:定时器的引擎:clock_event_device

    早期的内核版本中,进程的调度基于一个称之为tick的时钟滴答,通常使用时钟中断来定时地产生tick信号,每次tick定时中断都会进行进程的统计和调度,并对tick进行计数,记录在一个jiffies变量 ...

  2. Linux时间子系统之四:定时器的引擎:clock_event_device【转】

    本文转载自:http://blog.csdn.net/droidphone/article/details/8017604 版权声明:本文为博主原创文章,未经博主允许不得转载.   目录(?)[+] ...

  3. Linux时间子系统专题汇总

    关于Linux时间子系统有两个系列文章讲的非常好,分别是WowoTech和DroidPhone. 还有两本书分别是介绍: Linux用户空间时间子系统<Linux/UNIX系统编程手册>的 ...

  4. Linux时间子系统之六:高精度定时器(HRTIMER)的原理和实现

    转自:http://blog.csdn.net/droidphone/article/details/8074892 上一篇文章,我介绍了传统的低分辨率定时器的实现原理.而随着内核的不断演进,大牛们已 ...

  5. Linux时间子系统之五:低分辨率定时器的原理和实现

    专题文档汇总目录 Notes:低精度timer在内核中的数据结构以及API接口:低精度timer精巧高效的分组,使用cascade进行定时器移位,组内Timer FIFO:低精度Timer的初始化流程 ...

  6. Linux时间子系统之八:动态时钟框架(CONFIG_NO_HZ、tickless)

    在前面章节的讨论中,我们一直基于一个假设:Linux中的时钟事件都是由一个周期时钟提供,不管系统中的clock_event_device是工作于周期触发模式,还是工作于单触发模式,也不管定时器系统是工 ...

  7. Linux时间子系统之八:动态时钟框架(CONFIG_NO_HZ、tickless)【转】

    转自:http://blog.csdn.net/droidphone/article/details/8112948 版权声明:本文为博主原创文章,未经博主允许不得转载.   目录(?)[-] 数据结 ...

  8. Linux时间子系统之(三):用户空间接口函数

    专题文档汇总目录 Notes:用户空间时间相关接口函数: 类型 API 精度 说明 时间 time stime time_t 精度为秒级 逐渐要被淘汰.需要定义__ARCH_WANT_SYS_TIME ...

  9. Linux时间子系统之(六):POSIX timer

    专题文档汇总目录 Notes:首先讲解了POSIX timer的标识(唯一识别).POSIX Timer的组织(管理POSIX Timer).内核中如何抽象POSIX Timer:然后分析了POSIX ...

随机推荐

  1. Orientation Auto Rotation旋转屏幕crash问题(Unity3D开发之十四)

    猴子原创,欢迎转载.转载请注明: 转载自Cocos2Der-CSDN,谢谢! 原文地址: http://blog.csdn.net/cocos2der/article/details/44133127 ...

  2. Java常见运算符整理

    本文是在学习中的总结,欢迎转载但请注明出处:http://blog.csdn.net/pistolove/article/details/44724267 本文主要介绍Java中常见的运算符,重点介绍 ...

  3. DB Query Analyzer 5.05 is released, 65 articles concerned have been published

    DB Query Analyzer 5.05 is released, 65 articles concerned have been published DB Query Analyzer is p ...

  4. MaterialDesign学习项目

    概述 该项目主要用来学习Material Design Support Library和一些android其他技术,也借鉴了网上一些其他优秀的学习资源.该项目目前主要分为俩大部分(后期可能会有一些增加 ...

  5. Android的Binder的起源-android学习之旅(100)

    George Hoffman任职1991年Be公司的工程师,他启动了一个"openBinder"的项目,该项目的宗旨是研究一个高效的信号传递工具,允许多个软件相互合作,构成一个软件 ...

  6. rails中select不能响应多选的解决办法

    在rails4.2中如果你写如下代码,post的select无法传回多选内容,即使你select设置为多选: <select id='id_size' name='name_size' mult ...

  7. MOOS学习笔记——多线程

    /* * A simple example showing how to use a comms client */ #include "MOOS/libMOOS/Comms/MOOSAsy ...

  8. 使用XStream是实现XML与Java对象的转换(4)--转换器

    七.转换器(Converter) 我们程序中的POJO是千变万化的,而且需求也是千奇百怪的,所以XStream中的内置的转换器的功能不一定能够满足我们的要求,所以我们就需要自己构建转换器. 1,一个基 ...

  9. 使用nginx sticky实现基于cookie的负载均衡

    在多台后台服务器的环境下,我们为了确保一个客户只和一台服务器通信,我们势必使用长连接.使用什么方式来实现这种连接呢,常见的有使用nginx自带的ip_hash来做,我想这绝对不是一个好的办法,如果前端 ...

  10. layerX参数构建

    var defaults = { name: 'layerX', url: [ { name: 'sub', url: '', focus: true } ], parent: { name: '', ...