Linux下操作系统编程有两本经典APUE即《Advanced Programming in the UNIX Environment》和TLPI《The Linux Programming Interface》,中文版对应《UNIX环境高级编程(第3版)》和《Linux/UNIX系统编程》。

TLPI洋洋洒洒英文版1506页,中文版1176页;一共64章节,明显是作为工具书使用。通过目录可以了解本书的结构,以及作者的组织形式。

  • 背景知识及概念:共3章分别介绍了UNIX、C、Linux历史;Linux和UNIX基本概念;Linux和UNIX系统编程基本概念。
  • 系统编程接口基本特性:共9章分别介绍了文件I/O、进程、内存分配、用户和组、进程凭证、时间、系统限制和选项、获取系统和进程信息。
  • 系统编程接口高级特性:共11章分别介绍了文件I/O缓冲、文件系统、文件属性、扩展属性、访问控制列表、目录和链接、监控文件时间、信号、定时器。
  • 进程、程序及线程:共10章分别介绍了进程创建、终止,监控子进程,执行程序;POSIX线程。
  • 进程及程序高级主题:共9章分别介绍了进程组、会话及任务控制;进程优先级和进程调度;进程资源;守护进程;编写安全的特权程序;能力;登陆记账;共享库。
  • 进程间通信:共13章分别介绍了IPC概览;管道和FIFO;System V IPC消息队列、信号量及共享内存;内存映射;虚拟内存操作;POSIX消息队列、信号量及共享内存;文件锁定。
  • 套接字和网络编程:共6章。
  • 高级I/O主题:共3章分别介绍了中断、其他I/O模型、伪终端。

关于本书的示例有两个版本Distribution versionBook version,在这里可以找到他们的详细信息;为了便于使用将其放到https://github.com/arnoldlu/tlpi

第10章 时间

1. 日历时间

常用的获取时间的函数time和gettimeofday两种:

#include <sys/time.h>
int gettimeofday(struct timeval *tv, struct timezone *tz);
                      Returns on success, or – on error struct timeval {
__kernel_time_t tv_sec; /* seconds */
__kernel_suseconds_t tv_usec; /* microseconds */
};
#include <time.h>
time_t time(time_t *timep);
Returns number of seconds since the Epoch,or (time_t) – on error

time函数基于gettimeofday实现,time精度秒级,gettimeofday可以达到微妙。

2. 时间转换函数

下图是从内核获取时间的两种方式(time和gettimeofday),以及基于此进行的时间转换(timeval、time_t、tm)。

time对应的设置时间函数是stime,gettimeofday对应的是gettimeofday。

time_t和timeval关系,time_t是timeval的秒部分对齐,四舍五入。

time_t到tm转换:gmtime()、localtime()。

tm到time_t转换:mktime()。

time_t到文本:ctime()。

tm到文本:asctime()、strftime()。

文本到tm:strptime()。

3. 时区

/etc/localtime

4. 地区

5. 更新系统时钟

6. 软件时钟

7. 进程时间

#include <sys/times.h>
clock_t times(struct tms *buf);
Returns number of clock ticks (sysconf(_SC_CLK_TCK)) since “arbitrary” time in past on success, or (clock_t) – on error

8. 总结

第23章 定时器与休眠

1.间隔定时器setitimer和alarm

两个重要的结构体:定时器参数struct itimerval和表示时间struct timerval。

struct itimerval {
    struct timeval it_interval;     /* Interval for periodic timer */
    struct timeval it_value;        /* Current value (time until next expiration) */  为0则是一次性定时器。
};

struct timeval {
    time_t      tv_sec;             /* Seconds */
    suseconds_t tv_usec;            /* Microseconds (long int) */
};

setitimer和alarm原型:

#include <sys/time.h>
int setitimer(int which, const struct itimerval *new_value, struct itimerval *old_value);
    Returns 0 on success, or –1 on error
int getitimer(int which, struct itimerval *curr_value);
    Returns 0 on success, or –1 on error

#include <unistd.h>
unsigned int alarm(unsigned int seconds);
    Always succeeds, returning number of seconds remaining on any previously set timer, or 0 if no timer previously was set

setitimer可以指定三种不同类型定时器:ITIMER_REAL(SIGALARM)、ITIMER_VIRTUAL(SIGVTALRM)、ITIMER_PROF(SIGPROF)。

  • 进程可以拥有三种定时器,但是每种只能设置一个。
  • 只能通过发送信号的方式来通知定时器到期,也不能改变到期信号时产生的信号。
  • alarm()和setitimer()针对同一进程共享同一实时定时器。
  • 三种定时器的参考时间不同,ITIMER_REAL(真实时间)、ITIMER_VIRTUAL(进程虚拟时间,用户CPU时间)、ITIMER_PROF(进程的用户和内核时间总和)。
  • 如果这些信号不设置处理函数,则默认会终止进程。
  • 如果一个间隔式定时器到期多次,且相应信号遭到阻塞时,那么只会调用一次信号处理函数。
  • 分辨率微秒级。

Notes:简单讲alarm基于setitimer,setitimer有三种形式。

两者的系统调用如下:

asmlinkage long sys_getitimer(int which, struct itimerval __user *value);
asmlinkage long sys_setitimer(int which,
struct itimerval __user *value,
struct itimerval __user *ovalue);
asmlinkage long sys_alarm(unsigned int seconds);

对应的原型在:

kernel/itimer.c
SYSCALL_DEFINE3(setitimer, int, which, struct itimerval __user *, value,
struct itimerval __user *, ovalue)
SYSCALL_DEFINE2(getitimer, int, which, struct itimerval __user *, value) kernel/timer.c
SYSCALL_DEFINE1(alarm, unsigned int, seconds)

从代码看alarm和setitimer之间的关系:

unsigned int alarm_setitimer(unsigned int seconds)
{
struct itimerval it_new, it_old; #if BITS_PER_LONG < 64
if (seconds > INT_MAX)
seconds = INT_MAX;
#endif
it_new.it_value.tv_sec = seconds;----------------------------alarm精度是秒级,setitimer是微妙级别。
it_new.it_value.tv_usec = ;
it_new.it_interval.tv_sec = it_new.it_interval.tv_usec = ; do_setitimer(ITIMER_REAL, &it_new, &it_old);-----------------从这里可以看出alarm是setitimer的ITIMER_REAL形式,所以两者不能混用。容易造成混乱。 /*
* We can't return 0 if we have an alarm pending ... And we'd
* better return too much than too little anyway
*/
if ((!it_old.it_value.tv_sec && it_old.it_value.tv_usec) ||
it_old.it_value.tv_usec >= )
it_old.it_value.tv_sec++; return it_old.it_value.tv_sec;
}

do_setitimer/do_getitimer都对ITIMER_REAL、ITIMER_VIRTUAL、ITIMER_PROF分别进行了处理。这三个定时器都是内嵌在struct task_struct中:

ITIMER_REAL:借用task_struct->signal->real_timer,超时函数式it_real_fn。创建以真实时间倒计时的定时器,到期发送SIGALRM给进程。

ITIMER_VIRTUAL:task_struct->signal->it[CPUCLOCK_VIRT]。创建以进程虚拟时间(用户模式下CPU时间)倒计时的定时器,到期会产生信号SIGVTALRM。

ITIMER_PROF:task_struct->signal->it[CPUCLOCK_PROF]。创建一个profiling定时器,以进程时间(用户+内核CPU时间总和)倒计时。

setitimer和alarm异同?

  • alarm是setitimer的ITIMER_REAL形式
  • alarm精度为秒,setitimer为微妙级(但受受限于软件时钟的频率)
  • alarm只提供一次性实时定时器,setitimer可以提供周期性定时器。

alarm基于setitimer,那么setitimer又是基于什么呢?

  ITIMER_REAL基于hrtimer,ITIMER_VIRTUAL和ITIMER_PROF基于POSIX CPU timer。这里重点看一下hrtimer相关内容。

  task_struct->signal->real_timer是由谁创建的?do_fork-->copy_process-->copy_signal-->hrtimer_init(&sig->real_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL),超时函数是it_real_fn。

  注意这里hrtimer的两个参数时钟类型是CLOCK_MONOTONIC单调次增,防跳变;timer模式是HRTIMER_MODE_REL是一个相对时间。

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);--------发送SIFALRM给调用进程。 return HRTIMER_NORESTART;
}

测试验证

  • 不同which差异?结果:同时触发,这三者占用的时间ITIMER_REAL< ITIMER_PROF <ITIMER_VIRTUAL,可见后两者受进程调度影响较大。
  • alarm和setitimer冲突问题?结果:alarm和ITIMER_REAL存在冲突,两者是基于同样的setitimer。
  • setitimer定时器只能存在一个?结果:可以三个,同一类型只能一个。

2.定时器的调度及精度

  • 系统可能会在定时器到期的瞬间之后才去调度其所属进程,这取决于当前负载和对进程的调度。
  • setitimer()定时器虽然可能延迟一定时间,但是后续的定时器仍然按照固定间隔。比如2秒定时器,2.3超时,下一个应该在4.3秒超时。
  • 虽然setitimer精度达到微妙,但是受制于软件时钟频率。比如jiffy为4ms,间隔为19100微妙,实际间隔是20ms。
  • 高分辨率定时器需要内核CONFIG_HIGH_RES_TIMERS。

3.为阻塞操作设置超时

4.休眠一段时间

低分辨率休眠sleep()

#include <unistd.h>
unsigned int sleep(unsigned int seconds);
    如果休眠正常结束,返回0。如果被中断,返回剩余秒数。可能由于系统负载调度原因,会在sleep到时候才对进程重新加以调度。

高分辨率休眠nanosleep()

#define _POSIX_C_SOURCE 199309
#include <time.h>
int nanosleep(const struct timespec *request, struct timespec *remain);
    Returns 0 on successfully completed sleep, or –1 on error or interrupted sleep

request指定了休眠持续时间,理论上精度可以达到纳秒级,但受制于软件时钟间隔。如果间隔并非间隔值,者向上取整。

nanosleep()不基于信号实现,但是可以通过型号处理函数来中断,如SIGINT(Ctrl-C)。
再被中断之后,剩余时间可以通过remain中获取。将remain赋给request,则可以继续睡眠剩余时间。


Notes:

sleep、nanosleep、clock_nanosleep区别?

  • sleep睡眠秒级,nanosleep理论上可以到达纳秒级别(但受制于软件时钟间隔大小,会向上取整)
  • 部分系统sleep基于alram/setitimer来实现,nanosleep则不会使用信号实现函数
  • clock_nanosleep设置flags,TIMER_ABSTIME。
  • clock_nanosleep不同时钟来测量休眠间隔clockid。
  • nanosleep(HRTIMER_MODE_REL、CLOCK_MONOTONIC),clock_nanosleep(HRTIMER_MODE_ABS/ HRTIMER_MODE_REL、CLOCK_REALTIME/CLOCK_MONOTONIC/),TIMER_ABSTIME避免进程嗜睡问题。

如下sleep、usleep、nanosleep、clock_nanosleep代码可以看出区别:

libc/unistd/sleep.c
unsigned int sleep (unsigned int seconds)
{
struct timespec ts = { .tv_sec = (long int) seconds, .tv_nsec = };
sigset_t set;
struct sigaction oact;
unsigned int result;
...
/* Run nanosleep, with SIGCHLD blocked if SIGCHLD is SIG_IGNed. */
result = nanosleep (&ts, &ts);----------------------------------------基于nanosleep
if (result != ) {
/* Got EINTR. Return remaining time. */
result = (unsigned int) ts.tv_sec + (ts.tv_nsec >= 500000000L);
}
...
} unsigned int sleep (unsigned int seconds)
{
struct sigaction act, oact;
sigset_t set, oset;
unsigned int result, remaining;
time_t before, after;
int old_errno = errno;
...
remaining = alarm(seconds);--------------------------------------------在没有定义REALTIME相关内容情况下,使用alarm实现。
...
return result > seconds ? : seconds - result;
} libc/unistd/usleep.c
#if defined __UCLIBC_HAS_REALTIME__ int usleep (__useconds_t usec)
{
const struct timespec ts = {
.tv_sec = (long int) (usec / ),
.tv_nsec = (long int) (usec % ) * 1000ul
};
return nanosleep(&ts, NULL);-----------------------------------根据REALTIME是否定义,是则基于nanosleep;否则基于select实现。
}
#else /* __UCLIBC_HAS_REALTIME__ */
int usleep (__useconds_t usec)
{
struct timeval tv; tv.tv_sec = (long int) (usec / );
tv.tv_usec = (long int) (usec % );
return select(, NULL, NULL, NULL, &tv);
}
#endif /* __UCLIBC_HAS_REALTIME__ */ kernel/hrtimer.c SYSCALL_DEFINE2(nanosleep, struct timespec __user *, rqtp,
struct timespec __user *, rmtp)
{
struct timespec tu; if (copy_from_user(&tu, rqtp, sizeof(tu)))
return -EFAULT; if (!timespec_valid(&tu))
return -EINVAL; return hrtimer_nanosleep(&tu, rmtp, HRTIMER_MODE_REL, CLOCK_MONOTONIC);---基于hrtimer_nanosleep的CLOCK_MONOTONIC时钟,相对时间。
} kernel/posix-timer.c
SYSCALL_DEFINE4(clock_nanosleep, const clockid_t, which_clock, int, flags,
const struct timespec __user *, rqtp,
struct timespec __user *, rmtp)
{
struct k_clock *kc = clockid_to_kclock(which_clock);
struct timespec t; if (!kc)
return -EINVAL;
if (!kc->nsleep)
return -ENANOSLEEP_NOTSUP; if (copy_from_user(&t, rqtp, sizeof (struct timespec)))
return -EFAULT; if (!timespec_valid(&t))
return -EINVAL; return kc->nsleep(which_clock, flags, &t, rmtp);------------------------除了POSIX CPU Timer和AlarmTImer外,也是基于hrtimer_nanosleep,但是多了which_clock的选择。
}

测试验证

  • nanosleep能达到的精度?是多少?受制于hrtimer?用户空间很难精确测量精度,系统调用、进程调度都会影响到实际定时时间段。
  • sleep是否基于nanosleep?根据是否定义REALTIME功能来定。

通过strace查看sleep和nanosleep可以看出,sleep基于nanosleep实现。


5.POSIX时钟

POSIX时钟API必须以-lrt选项进行编译,从而与librt函数库链接,主要系统调用包括获取当前值的clock_gettime()、返回时钟分辨率的clock_getres()、以及更新时钟的clock_settime()。

要测定特定进程或线程所消耗的CPU时间,可以借助clock_getcpuclockid/pthread_getcpuclockid来获取时钟ID,接着再以此返回ID去调用clock_gettime(),从而获得进程或线程耗费的CPU时间。pid为0是,clock_getcpuclockid()返回调用进程的CPU时间时钟ID。

#define _POSIX_C_SOURCE 199309
#include <time.h>
int clock_gettime(clockid_t clockid, struct timespec *tp);
int clock_getres(clockid_t clockid, struct timespec *res);
    Both return 0 on success, or –1 on error

int clock_settime(clockid_t clockid, const struct timespec *tp);
    Returns 0 on success, or –1 on error

int clock_getcpuclockid(pid_t pid, clockid_t *clockid);
    Returns 0 on success, or a positive error number on error

int pthread_getcpuclockid(pthread_t thread, clockid_t *clockid);
    Returns 0 on success, or a positive error number on error

int clock_nanosleep(clockid_t clockid, int flags, const struct timespec *request, struct timespec *remain);
    Returns 0 on successfully completed sleep or a positive error number on error or interrupted sleep

CLOCK_REALTIME时钟是一种系统级时钟,用于度量真实时间。

CLOCK_MONOTONIC系统启动后就不会发生改变,适用于那些无法容忍系统时钟发生跳跃性变化的应用。Linux上这种时钟对事件的测量食欲系统启动。

CLOCK_PROCESS_CPUTIME_ID时钟测量调用进程所消耗的用户和系统CPU时间。

CLOCK_THREAD_CPUTIME_ID时钟用于测量进程中单条线程的用户和系统CPU时间。

Linux特有的clock_nanosleep()系统调用也可以暂停调用进程,知道经理一段指定时间,亦或是收到信号才恢复运行。

int clock_nanosleep(clockid_t clockid, int flags, const struct timespec *request, struct timespec *remain);
Returns 0 on successfully completed sleep,or a positive error number on error or interrupted sleep

默认情况下(flags为0),request指定休眠间隔是相对时间;如果flags设定TIMER_ABSTIME,request则表示clockid时钟所测量的绝对时间。

相对时间嗜睡问题:如果只是先获取当前时间,计算与目标时间差距,再以相对时间进行休眠,进程可能执行到一半就奔抢占了,结果实际休眠时间回避预期要久。如果被信号处理函数中断并使用循环重启休眠,则“嗜睡”问题尤其明显。如果信号频率很高,则按相对时间休眠的进程则会有较大时间误差。

如何避免嗜睡:先调用clock_gettime()获取时间,加上期望休眠的时间量,再以TIMER_ABSTIME标识调用clock_nanosleep()函数。指定TIMER_ABSTIME,不再使用参数remain。如果信号中断clock_nanosleep()调用,再次调用该函数来重启休眠时,request参数不变。clock_nanosleep()和nanosleep()另一区别在可以选择不同的时钟来测量休眠间隔时间。


Notes:

在介绍POSIX时钟和定时器之前,先介绍一下POSIX都有那些类型的时钟。

CLOCK_REALTIME:可设定的系统级实时时钟。用于度量真实时间。

CLOCK_MONOTONIC :不可设定的恒定态时钟。系统启动后就不会发生改变。

CLOCK_PROCESS_CPUTIME_ID:每进程CPU时间的时钟。测量调用进程所消耗的用户和系统CPU时间。

CLOCK_THREAD_CPUTIME_ID:每线程CPU时间的时钟。测量调用线程所消耗的用户和系统CPU时间。

CLOCK_MONOTONIC_RAW:提供了对纯基于硬件时间的访问。不受NTP时间调整的影响。

CLOCK_REALTIME_COARSE:类似于CLOCK_REALTIME,适用于希望以最小代价获取较低分辨率时间戳的程序。返回值分辨率为jiffy。

CLOCK_MONOTONIC_COARSE:类似于CLOCK_MONOTONIC,适用于希望以最小代价获取较低分辨率时间戳的程序。返回值分辨率为jiffy。

CLOCK_BOOTTIME:

CLOCK_REALTIME_ALARM:

CLOCK_BOOTTIME_ALARM:

kernel/posix-timer.c

SYSCALL_DEFINE2(clock_settime, const clockid_t, which_clock,----------------设置POSIX时间,调用k_clock->clock_set
const struct timespec __user *, tp)
SYSCALL_DEFINE2(clock_gettime, const clockid_t, which_clock,----------------获取POSIX时间,调用k_clock->clock_get
struct timespec __user *,tp)
SYSCALL_DEFINE2(clock_adjtime, const clockid_t, which_clock,----------------调整时间,调用k_clock->clock_adj
struct timex __user *, utx)
SYSCALL_DEFINE2(clock_getres, const clockid_t, which_clock,-----------------获取时钟精度,由硬件决定,调用k_clock->clock_getres
struct timespec __user *, tp)
SYSCALL_DEFINE4(clock_nanosleep, const clockid_t, which_clock, int, flags,--睡眠纳秒数,调用k_clock->nsleep
const struct timespec __user *, rqtp,
struct timespec __user *, rmtp)

clock_getcpuclockid

CLOCK_PROCESS_CPUTIME_ID类型的clock_gettime

pthread_getcpuclockid

CLOCK_THREAD_CPUTIME_ID类型的clock_gettime


6.POSIX间隔式定时器

编译时需要使用-lrt选项。

#define _POSIX_C_SOURCE 199309
#include <signal.h>
#include <time.h>
int timer_create(clockid_t clockid, struct sigevent *evp,timer_t *timerid);
    Returns 0 on success, or –1 on error

int timer_settime(timer_t timerid, int flags,const struct itimerspec *value, struct itimerspec *old_value);
    Returns 0 on success, or –1 on error

int timer_gettime(timer_t timerid, struct itimerspec *curr_value);
    Returns 0 on success, or –1 on error

int timer_delete(timer_t timerid);
    Returns 0 on success, or –1 on error

int timer_getoverrun(timer_t timerid);
    Returns timer overrun count on success, or –1 on error

POSIX定时器API生命周期阶段如下:

  • 创建定时器timer_create(),并定义到期时对进程的通知方法,函数返回参数timerid所指向的缓冲区中放置定时器句柄,供后续调用中指代该定时器之用。timer_t用于标识定时器。
  • 启动/停止定时器timer_settime()。
  • 获取定时器当前值timer_gettime(),返回由timerid指定的间隔以及剩余时间。
  • 获取定时器溢出值timer_getoverrun()。
  • 删除定时器timer_delete(),对于已启动的定时器,会在移除前自动将其停止;如果定时器因到期已经在待定(pending),那么信号会保持这一状态;如果进程终止,会自动删除所有定时器。

Notes:

kernel/posix-timers.c中:
SYSCALL_DEFINE3(timer_create, const clockid_t, which_clock,-----创建timer,时钟基准which_clock
struct sigevent __user *, timer_event_spec,
timer_t __user *, created_timer_id)
SYSCALL_DEFINE2(timer_gettime, timer_t, timer_id,---------------获取timer剩余时间
struct itimerspec __user *, setting)
SYSCALL_DEFINE1(timer_getoverrun, timer_t, timer_id)------------获取timer当前值,时间间隔以及剩余时间
SYSCALL_DEFINE4(timer_settime, timer_t, timer_id, int, flags,---启动或停止timer
const struct itimerspec __user *, new_setting,
struct itimerspec __user *, old_setting)
SYSCALL_DEFINE1(timer_delete, timer_t, timer_id)----------------删除timer

POSIX定时器分为三类,初始化路径如下:

init_posix_timers--> posix_timers_register_clock-|-> CLOCK_REALTIME
|-> CLOCK_MONOTONIC
|-> CLOCK_MONOTONIC_RAW
|-> CLOCK_REALTIME_COARSE
|-> CLOCK_MONOTONIC_COARSE
|-> CLOCK_BOOTTIME
alarmtimer_init-->posix_timers_register_clock-|->CLOCK_REALTIME_ALARM
|->CLOCK_BOOTTIME_ALARM
init_posix_cpu_timers-->posix_timers_register_clock-|->CLOCK_PROCESS_CPUTIME_ID
|->CLOCK_THREAD_CPUTIME_ID

从posix_timers_register_clock可知,每个clock都必须具备clock_get和clock_getres,然后加入posix_clocks。

void posix_timers_register_clock(const clockid_t clock_id,
struct k_clock *new_clock)
{
if ((unsigned) clock_id >= MAX_CLOCKS) {
printk(KERN_WARNING "POSIX clock register failed for clock_id %d\n",
clock_id);
return;
} if (!new_clock->clock_get) {
printk(KERN_WARNING "POSIX clock id %d lacks clock_get()\n",
clock_id);
return;
}
if (!new_clock->clock_getres) {---------------------------------------clock_get和clock_getres必备
printk(KERN_WARNING "POSIX clock id %d lacks clock_getres()\n",
clock_id);
return;
} posix_clocks[clock_id] = *new_clock;----------------------------------posix_clocks保存所有相关timer信息
}

结构体k_clock提供了关于clock的操作函数,clock_getres/clock_set/clock_get/clock_adj/nsleep/nsleep_restart对应POSIX时钟系统调用;timer_create/timer_set/timer_del/timer_get对应POSIX定时器。

struct k_clock {
int (*clock_getres) (const clockid_t which_clock, struct timespec *tp);
int (*clock_set) (const clockid_t which_clock,
const struct timespec *tp);
int (*clock_get) (const clockid_t which_clock, struct timespec * tp);
int (*clock_adj) (const clockid_t which_clock, struct timex *tx);
int (*timer_create) (struct k_itimer *timer);
int (*nsleep) (const clockid_t which_clock, int flags,
struct timespec *, struct timespec __user *);
long (*nsleep_restart) (struct restart_block *restart_block);
int (*timer_set) (struct k_itimer * timr, int flags,
struct itimerspec * new_setting,
struct itimerspec * old_setting);
int (*timer_del) (struct k_itimer * timr);
#define TIMER_RETRY 1
void (*timer_get) (struct k_itimer * timr,
struct itimerspec * cur_setting);
};

系统调用和k_clock对应关系:

syscall

k_clock

clock_getres

clock_getres

clock_settime

clock_set

clock_gettime

clock_get

clock_adjtime

clock_adj

clock_nanosleep

nsleep/nsleep_restart

timer_create

timer_create

timer_gettime

timer_get

timer_getoverrun

timer_settime

timer_set

timer_delete

timer_del

从上面的POSIX定时器初始化可以看出分为三类:POSIX定时器、POSIX Alarm定时器、POSIX CPU时间定时器。

k_itimer是POSIX定时器结构体,从其it union可以看出有real、cpu、alarm、mmtimer几种类型的定时器。

struct k_itimer {
struct list_head list; /* free/ allocate list */
spinlock_t it_lock;
clockid_t it_clock; /* which timer type */
timer_t it_id; /* timer id */
int it_overrun; /* overrun on pending signal */
int it_overrun_last; /* overrun on last delivered signal */
int it_requeue_pending; /* waiting to requeue this timer */
#define REQUEUE_PENDING 1
int it_sigev_notify; /* notify word of sigevent struct */
struct signal_struct *it_signal;
union {
struct pid *it_pid; /* pid of process to send signal to */
struct task_struct *it_process; /* for clock_nanosleep */
};
struct sigqueue *sigq; /* signal queue entry. */
union {
struct {
struct hrtimer timer;
ktime_t interval;
} real;
struct cpu_timer_list cpu;
struct {
unsigned int clock;
unsigned int node;
unsigned long incr;
unsigned long expires;
} mmtimer;
struct {
struct alarm alarmtimer;
ktime_t interval;
} alarm;
struct rcu_head rcu;
} it;
};

kernel/posix-timers.c

CLOCK_REALTIME/ CLOCK_MONOTONIC/CLOCK_BOOTTIME三种时钟不但提供时钟功能,还提供定时器功能。这三种定时器都是基于hrtimer实现的。

CLOCK_MONOTONIC_RAW/CLOCK_REALTIME_COARSE/CLOCK_MONOTONIC_COARSE三种类型的时钟只提供获取时间和获取精度接口。

kernel/posix-cpu-timers.c

CLOCK_PROCESS_CPUTIME_ID/ CLOCK_THREAD_CPUTIME_ID

只提供了timer_create接口,没有timer_set等。

    struct k_clock process = {
.clock_getres = process_cpu_clock_getres,
.clock_get = process_cpu_clock_get,
.timer_create = process_cpu_timer_create,
.nsleep = process_cpu_nsleep,
.nsleep_restart = process_cpu_nsleep_restart,
};
struct k_clock thread = {
.clock_getres = thread_cpu_clock_getres,
.clock_get = thread_cpu_clock_get,
.timer_create = thread_cpu_timer_create,
};

kernel/time/alarmtimer.c

CLOCK_REALTIME_ALARM/CLOCK_BOOTTIME_ALARM

    struct k_clock alarm_clock = {
.clock_getres = alarm_clock_getres,
.clock_get = alarm_clock_get,
.timer_create = alarm_timer_create,
.timer_set = alarm_timer_set,
.timer_del = alarm_timer_del,
.timer_get = alarm_timer_get,
.nsleep = alarm_timer_nsleep,
};

系统在suspend时,也会执行alarmtimer设备的suspend。在系统即将进入睡眠时,查找alarm_bases(包括ALARM_REALTIME和ALARM_BOOTTIME)的timerqueue,找出最近一次要超时的timer。将这个timer转换成RTC Timer,这样就会将系统从suspend状态唤醒。

/* Suspend hook structures */

static const struct dev_pm_ops alarmtimer_pm_ops = {

.suspend = alarmtimer_suspend,

};

static struct platform_driver alarmtimer_driver = {

.driver = {

.name = "alarmtimer",

.pm = &alarmtimer_pm_ops,

}

};

和传统定时器相比POSIX的优势?

  • 传统setitimer定时器只有三种定时器,每种只能设置一个。timer_create可以提供多种类型(include/linux/time.h CLOCK_REALTIME…),每种类型多个定时器共存。
  • 只能通过发送信号来通知定时器到期。timer_create的evp参数决定定时器到期通知方式,SIGEV_NONE、SIGEV_SIGNAL、SIGEV_THREAD、SIGEV_THREAD_ID。可以选择通过执行线程函数来获取定时器通知。
  • 如果一个间隔式定时器到期多次,且响应信号遭到阻塞是,只调用一次信号处理函数。无从知晓定时器是否溢出。timer_getoverrun可以获知超时定时器数目。
  • setitimer定时器分辨率只能达到微妙级。POSIX定时器提供纳秒级。

下面整理一下不同定时器ID、定时器时钟基准、获取时间函数:

定时器ID 定时器时钟 延时 时间函数 是否具备唤醒

是否计算

suspend时间

CLOCK_REALTIME HRTIMER_BASE_REALTIME common_nsleep posix_clock_realtime_get X X
CLOCK_REALTIME_COARSE X X posix_get_realtime_coarse X X
CLOCK_REALTIME_ALARM HRTIMER_BASE_REALTIME alarm_timer_nsleep alarm_clock_get   X
CLOCK_MONOTONIC HRTIMER_BASE_MONOTONIC common_nsleep posix_ktime_get_ts X X
CLOCK_MONOTONIC_RAW X X posix_get_monotonic_raw X X
CLOCK_MONOTONIC_COARSE X X posix_get_monotonic_coarse X X
CLOCK_BOOTTIME HRTIMER_BASE_BOOTTIME common_nsleep posix_get_boottime X  
CLOCK_BOOTTIME_ALARM HRTIMER_BASE_BOOTTIME alarm_timer_nsleep alarm_clock_get    
CLOCK_PROCESS_CPUTIME_ID X process_cpu_nsleep process_cpu_clock_get X X
CLOCK_THREAD_CPUTIME_ID X X thread_cpu_clock_get X X

测试验证

  • POSIX不同类型的定时器区别?归根结底还是基础时钟的不同,还有是否具备_ALARM。
  • 多个POSIX定时器共存?可以多个定时器共存。

7.文件描述符定时器:timerfd API

Linux特有的timerfd API,可以从文件描述符中读取其所创建定时器的到期通知,也可以用使用select()、poll()、epoll()监控。

#include <sys/timerfd.h>
int timerfd_create(int clockid, int flags);
clockid可以设置为CLOCK_REALTIME或者CLOCK_MONOTONIC。
相当于open创建一个句柄,可以使用close()关闭响应文件描述符。

int timerfd_settime(int fd, int flags, const struct itimerspec *new_value, struct itimerspec *old_value);
int timerfd_gettime(int fd, struct itimerspec *curr_value);

timerfd_settime()启动了定时器,可以通过read()来读取定时器到期信息。read缓冲区必须容纳uint64_t类型,读取返回值是已发生的到期次数。

执行./demo_timerfd 1:1 100,使用Ctrl-Z将其变成挂起到背景执行,fg拉倒前台,Ctrl-C终止执行。可以看出在背景执行期间有10此到期。

./demo_timerfd 1:1 100
1.000: expirations read: 1; total=1
2.000: expirations read: 1; total=2
3.000: expirations read: 1; total=3
4.000: expirations read: 1; total=4
^Z
[2]+  Stopped                 ./demo_timerfd 1:1 100
fg
./demo_timerfd 1:1 100
14.734: expirations read: 10; total=14
15.000: expirations read: 1; total=15
16.000: expirations read: 1; total=16
17.000: expirations read: 1; total=17
18.000: expirations read: 1; total=18
^Z
[2]+  Stopped                 ./demo_timerfd 1:1 100


Notes:

fs/timerfd.c
SYSCALL_DEFINE2(timerfd_create, int, clockid, int, flags)--------------------------()
SYSCALL_DEFINE4(timerfd_settime, int, ufd, int, flags,-----------------------------()
const struct itimerspec __user *, utmr,
struct itimerspec __user *, otmr)
SYSCALL_DEFINE2(timerfd_gettime, int, ufd, struct itimerspec __user *, otmr)-------()

(1)

clockid可以设置为:CLOCK_REALTIME或CLOCK_MONOTONIC。

flags可设置为:TFD_CLOEXEC(Close on Exec,fork不被关闭)或者TFD_NONBLOCK(读操作是非阻塞式的)。

timerfd使用close关闭。

(2)flags为哦,则it_value视为始于timer_settime调用时间点的相对值。TFD_TIMER_ABSTIME则将其视为一个绝对时间(从时钟0点开始测量)。

(3)

timerfd和POSIX timer异同?

  • timerfd也是基于hrtimer的
  • 可以从文件描述符中获取定时器通知,还可使用select()、poll()、epoll()来监控这些描述符。

8.总结

setitimer()和alarm()设定定时器,以便于在经历指定的一段实际时间后收到信号通知。

sleep()和nano_sleep()指定程序暂停执行一段特定间隔的实际时间。

POSIX时钟API包括clock_gettime()、clock_getres()、clock_settime()、clock_getcpuclockid()、pthread_getcpuclockid()以及高分辨率clock_nanosleep(),这些函数提供个更丰富的时钟类型。

POSIX定时器API包括timer_create()、timer_settime()、timer_gettime、timer_getoverrun()、timer_delete,这些函数客服了setitimer()一系列缺陷,可以创建多个timer、获取timer溢出情况、设置不同触发方式。

timerfd_create()、timerfd_settime()、timerfd_gettime()提供一组创建定时器的接口,允许从文件秒速附中读取特定定时器通知。可以使用read()、select()、poll()、epoll()、close()来操作这些描述符。


和时间相关的库:

libc

librt

void getnstimeofday(struct timespec *ts)
{
unsigned long seq;
s64 nsecs; WARN_ON(timekeeping_suspended); do {
seq = read_seqbegin(&timekeeper.lock); *ts = timekeeper.xtime;
nsecs = timekeeping_get_ns();
printk("%s ts->tv_sec=%ld, ts->tv_nsec=%9ld, nsecs=%lld", ts->tv_sec, ts->tv_nsec, nsecs); /* If arch requires, add in gettimeoffset() */
nsecs += arch_gettimeoffset();
printk("%s ts->tv_sec=%ld, ts->tv_nsec=%9ld, nsecs=%lld", ts->tv_sec, ts->tv_nsec, nsecs);
} while (read_seqretry(&timekeeper.lock, seq)); timespec_add_ns(ts, nsecs);
}
libc/librt函数名 系统调用 hrtimer_clock_base/mode 是否计入suspend 是否具备唤醒 RUN/IDLE/Suspend 注释

延                                                                                      时

sleep nanosleep

CLOCK_MONOTONIC

HRTIMER_MODE_REL

X X    
usleep X X    
nanosleep X X    
clock_nanosleep CLOCK_REALTIME  X X    
CLOCK_MONOTONIC  X X    
CLOCK_BOOTTIME  √ X
   
CLOCK_REALTIME_ALARM    
CLOCK_BOOTTIME_ALARM
   

定                                                时                                                器

alarm

setitimer

ITIMER_REAL

(ITIMER_VIRTUAL、

ITIMER_PROF)

ITIMER_REAL基于hrtimer的:

CLOCK_MONOTONIC/HRTIMER_MODE_REL

  X   X    由do_fork-->copy_process-->copy_signal创建hrtimer
...        
...        
timer_create/timer_settime/timer_gettime  CLOCK_REALTIME X X    
 CLOCK_MONOTONIC X X    
 CLOCK_BOOTTIME X    
 CLOCK_REALTIME_ALARM    
 CLOCK_BOOTTIME_ALARM    
             
获                                取                                时                                间
time  gettimeofday do_gettimeofday-->getnstimeofday

void getnstimeofday(struct timespec *ts)
{
unsigned long seq;
s64 nsecs; WARN_ON(timekeeping_suspended); do {
seq = read_seqbegin(&timekeeper.lock); *ts = timekeeper.xtime;
nsecs = timekeeping_get_ns();
/* If arch requires, add in gettimeoffset() */
nsecs += arch_gettimeoffset();
} while (read_seqretry(&timekeeper.lock, seq)); timespec_add_ns(ts, nsecs);
}

timekeeper.xtime

timekeeping_get_ns

arch_gettimeoffset()

   
gettimeofday    
clock_gettime/clock_settime   CLOCK_REALTIME ktime_get_real_ts-->getnstimeofday    
CLOCK_MONOTONIC ktime_get_ts

void ktime_get_ts(struct timespec *ts)
{
struct timespec tomono;
unsigned int seq;
s64 nsecs; WARN_ON(timekeeping_suspended); do {
seq = read_seqbegin(&timekeeper.lock);
*ts = timekeeper.xtime;
tomono = timekeeper.wall_to_monotonic;
nsecs = timekeeping_get_ns();
/* If arch requires, add in gettimeoffset() */
nsecs += arch_gettimeoffset(); } while (read_seqretry(&timekeeper.lock, seq)); set_normalized_timespec(ts, ts->tv_sec + tomono.tv_sec,
ts->tv_nsec + tomono.tv_nsec + nsecs);
}

timekeeper.xtime

timekeeper.wall_to_monotonic

timekeeping_get_ns()

arch_gettimeoffset()

   
CLOCK_BOOTTIME get_monotonic_boottime

void get_monotonic_boottime(struct timespec *ts)
{
struct timespec tomono, sleep;
unsigned int seq;
s64 nsecs; WARN_ON(timekeeping_suspended); do {
seq = read_seqbegin(&timekeeper.lock);
*ts = timekeeper.xtime;
tomono = timekeeper.wall_to_monotonic;
sleep = timekeeper.total_sleep_time;
nsecs = timekeeping_get_ns(); } while (read_seqretry(&timekeeper.lock, seq)); set_normalized_timespec(ts, ts->tv_sec + tomono.tv_sec + sleep.tv_sec,
(s64)ts->tv_nsec + tomono.tv_nsec + sleep.tv_nsec + nsecs);
}

timekeeper.xtime
timekeeper.wall_to_monotonic
timekeeper.total_sleep_time
timekeeping_get_ns()

   
CLOCK_MONOTONIC_RAW getrawmonotonic

void getrawmonotonic(struct timespec *ts)
{
unsigned long seq;
s64 nsecs; do {
seq = read_seqbegin(&timekeeper.lock);
nsecs = timekeeping_get_ns_raw();
*ts = timekeeper.raw_time; } while (read_seqretry(&timekeeper.lock, seq)); timespec_add_ns(ts, nsecs);
}

timekeeper.raw_time

timekeeping_get_ns_raw()

   
CLOCK_REALTIME_COARSE current_kernel_time

struct timespec current_kernel_time(void)
{
struct timespec now;
unsigned long seq; do {
seq = read_seqbegin(&timekeeper.lock); now = timekeeper.xtime;
} while (read_seqretry(&timekeeper.lock, seq)); return now;
}

timekeeper.xtime
   
 CLOCK_MONOTONIC_COARSE get_monotonic_coarse

struct timespec get_monotonic_coarse(void)
{
struct timespec now, mono;
unsigned long seq; do {
seq = read_seqbegin(&timekeeper.lock); now = timekeeper.xtime;
mono = timekeeper.wall_to_monotonic;
} while (read_seqretry(&timekeeper.lock, seq)); set_normalized_timespec(&now, now.tv_sec + mono.tv_sec,
now.tv_nsec + mono.tv_nsec);
return now;
}

timekeeper.xtime
timekeeper.wall_to_monotonic

   
CLOCK_REALTIME_ALARM ktime_get_real-->getnstimeofday  同CLOCK_REALTIME      
CLOCK_BOOTTIME_ALARM ktime_get_boottime-->get_monotonic_boottime  同CLOCK_BOOTTIME      
  /proc/uptime

uptime:do_posix_clock_monotonic_gettime(ktime_get_ts)+monotonic_to_bootbased

idletime:kernel_cpustat.cpustat[CPUTIMEIDLE]

uptime:

idletime:CPU idle时间总和

timekeeper.xtime

timekeeper.wall_to_monotonic

timekeeper.total_sleep_time

timekeeping_get_ns()

arch_gettimeoffset()

   

总结:

1.上述关于时间获取的函数

timekeeper.xtime------------------------------------------墙上时间,全局变量struct timekeeper timerkeeper的成员。记录从Epoch到当前的时间差。

timekeeper.wall_to_monotonic-------------------------monotonic到xtime的偏移量,wall_to_monotonic+xtime等与monotonic时间

timekeeper.total_sleep_time----------------------------记录系统suspend时间

timekeeper.raw_time-------------------------------------

timekeeping_get_ns()------------------------------------clock->cycle_last记录了最近一次时钟读取的cycle数,timekeeping_get_ns返回的是上次读取到本次读取之间的时间差值。

timekeeping_get_ns_raw()-----------------------------和timekeeping_get_ns类似,只是mult/shift基准不同。

arch_gettimeoffset()--------------------------------------架构相关调用system_timer->offset(),如未实现则返回0.

  xtime wall_to_monotonic total_sleep_time raw_time timekeeping_get_ns timekeeping_get_ns_raw arch_gettimeoffset  
time  √        √    √  
gettimeofday          
CLOCK_REALTIME          
CLOCK_REALTIME_ALARM          
CLOCK_MONOTONIC        
CLOCK_MONOTONIC_RAW            
CLOCK_REALTIME_COARSE              
CLOCK_MONOTONIC_COARSE            
CLOCK_BOOTTIME        
CLOCK_BOOTTIME_ALARM        
/proc/uptime      

1.1 time、gettimeofday、CLOCK_REALTIME*都包含了睡眠时间,不过精度不同,分别是秒、微妙、纳秒。时间起点是Epoch。

1.2 CLOCK_BOOTTIME*也包含了睡眠时间,精度为纳秒。时间起点是内核开机。

1.3 CLOCK_MONOTONIC时间起点也是内核开机,只不过不记录睡眠时间。

下面是时间一次测试的结果:

2. 关于延时函数

2.1 sleep精度为秒,nanosleep精度为纳秒。sleep是使用nanosleep还是alarm依赖于库的实现,使用需谨慎。两周都不计入suspend时间,也不能唤醒系统。

2.2 clock_nanosleep的CLOCK_BOOTTIME计入suspend时间,但是不具备唤醒功能。在超时后,如果系统唤醒立即执行。

2.3 clock_nanosleep的CLOCK_REALTIME_ALARM和CLOCK_BOOTTIME_ALARM计入suspend时间,并且在超时后具备唤醒系统的能力。需要root权限。

2.4 sleep和nanosleep再被中断频繁中断,然后重新sleep都无法避免嗜睡问题。clock_nanosleep可以通过设置TIMER_ABSTIME来避免嗜睡问题。

3. 关于定时器

3.1 alarm()和setitimer()精度分别为秒和微秒。但是由于alarm是调用setitimer,所以两者不能混用。后调用者会覆盖前者。

3.2 POSIX定时器精度为纳秒,可以多定时器、多种类型定时器共存。

3.3 POSIX的CLOCK_BOOTTIME将suspend 时间计入,但不具备唤醒功能,超时后只能在系统唤醒后立即执行。

3.4 POSIX的CLOCK_REALTIME_ALARM和CLOCK_BOOTTIME_ALARM都将suspend时间计入,并且能唤醒系统。需要root。


测试代码:

#define _POSIX_C_SOURCE 199309
#include <signal.h>
#include <sys/time.h>
#include <time.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <linux/capability.h>
#include <errno.h> #define CLOCK_REALTIME 0
#define CLOCK_MONOTONIC 1
#define CLOCK_PROCESS_CPUTIME_ID 2
#define CLOCK_THREAD_CPUTIME_ID 3
#define CLOCK_MONOTONIC_RAW 4
#define CLOCK_REALTIME_COARSE 5
#define CLOCK_MONOTONIC_COARSE 6
#define CLOCK_BOOTTIME 7
#define CLOCK_REALTIME_ALARM 8
#define CLOCK_BOOTTIME_ALARM 9 #define TIMER_SIG SIGRTMAX /* Our timer notification signal */
#define BUF_SIZE 256 char *
currTime(const char *format)
{
char buf_local[BUF_SIZE]; /* Nonreentrant */
time_t t;
size_t s;
struct tm *tm; t = time(NULL);
tm = localtime(&t);
if (tm == NULL)
return NULL; s = strftime(buf_local, BUF_SIZE, (format != NULL) ? format : "%c", tm); return (s == ) ? NULL : buf_local;
} void print_buf(char *buf)
{
time_t t;
size_t s;
struct tm *tm; t = time(NULL);
tm = localtime(&t);
if (tm == NULL)
return; s = strftime(buf, sizeof(buf), "%T", tm);
} void print_time_t(time_t *t)
{
printf("time_t=%ld\n", *t);
} void print_timeval(struct timeval *tv)
{
printf("timeval=%ld.%6ld\n", tv->tv_sec, tv->tv_usec);
} void print_timespec(char * head, struct timespec *ts)
{
printf("%s timespec=%ld.%9ld\n", head, ts->tv_sec, ts->tv_nsec);
} static void *sleep_func(void *arg)
{
printf("[%s] Start %s\n", currTime("%T"), __func__);
sleep();
printf("[%s] End %s\n", currTime("%T"), __func__);
} static void *nanosleep_func(void *arg)
{
struct timespec request, remain;
int ret = ; printf("[%s] Start %s\n", currTime("%T"), __func__);
request.tv_sec = ;
request.tv_nsec = ;
ret = nanosleep(&request, &remain);
if (ret != && ret != EINTR)
printf("Error %s\n", __func__);
printf("[%s] End %s\n", currTime("%T"), __func__);
} static void *clock_nanosleep_CLOCK_REALTIME_func(void *arg)
{
struct timespec request, remain;
int ret, flags = ; printf("[%s] Start %s\n", currTime("%T"), __func__);
request.tv_sec = ;
request.tv_nsec = ;
ret = clock_nanosleep(CLOCK_REALTIME, flags, &request, &remain);
if (ret != && ret != EINTR)
printf("Error %s\n", __func__);
printf("[%s] End %s\n", currTime("%T"), __func__);
} static void *clock_nanosleep_CLOCK_REALTIME_ALARM_func(void *arg)
{
struct timespec request, remain;
int ret, flags = ; printf("[%s] Start %s\n", currTime("%T"), __func__);
request.tv_sec = ;
request.tv_nsec = ;
ret = clock_nanosleep(CLOCK_REALTIME_ALARM, flags, &request, &remain);
if (ret != && ret != EINTR)
printf("Error %s\n", __func__);
printf("[%s] End %s\n", currTime("%T"), __func__);
} static void *clock_nanosleep_CLOCK_MONOTONIC_func(void *arg)
{
struct timespec request, remain;
int ret, flags = ; printf("[%s] Start %s\n", currTime("%T"), __func__);
request.tv_sec = ;
request.tv_nsec = ;
ret = clock_nanosleep(CLOCK_MONOTONIC, flags, &request, &remain);
if (ret != && ret != EINTR)
printf("Error %s\n", __func__);
printf("[%s] End %s\n", currTime("%T"), __func__);
} static void *clock_nanosleep_CLOCK_BOOTTIME_func(void *arg)
{
struct timespec request, remain;
int ret, flags = ; printf("[%s] Start %s\n", currTime("%T"), __func__);
request.tv_sec = ;
request.tv_nsec = ;
ret = clock_nanosleep(CLOCK_BOOTTIME, flags, &request, &remain);
if (ret != && ret != EINTR)
printf("Error %s\n", __func__);
printf("[%s] End %s\n", currTime("%T"), __func__);
} static void *clock_nanosleep_CLOCK_BOOTTIME_ALARM_func(void *arg)
{
struct timespec request, remain;
int ret, flags = ; printf("[%s] Start %s\n", currTime("%T"), __func__);
request.tv_sec = ;
request.tv_nsec = ;
ret = clock_nanosleep(CLOCK_BOOTTIME_ALARM, flags, &request, &remain);
if (ret != && ret != EINTR)
printf("Error %s\n", __func__);
printf("[%s] End %s\n", currTime("%T"), __func__);
} static void alarm_handler(int sig)
{
printf("\n[%s] %s sig=%d\n", currTime("%T"), __func__, sig);
} static void realtime_handler(int sig, siginfo_t *si, void *uc)
{
timer_t *tidptr; tidptr = si->si_value.sival_ptr;
printf("\n[%s] %s sig=%d\n", currTime("%T"), __func__, sig);
} static void monotonic_handler(int sig, siginfo_t *si, void *uc)
{
timer_t *tidptr; tidptr = si->si_value.sival_ptr;
printf("\n[%s] %s sig=%d\n", currTime("%T"), __func__, sig);
} static void boottime_handler(int sig, siginfo_t *si, void *uc)
{
timer_t *tidptr; tidptr = si->si_value.sival_ptr;
printf("\n[%s] %s sig=%d\n", currTime("%T"), __func__, sig);
} static void realtime_alarm_handler(int sig, siginfo_t *si, void *uc)
{
timer_t *tidptr; tidptr = si->si_value.sival_ptr;
printf("\n[%s] %s sig=%d\n", currTime("%T"), __func__, sig);
} static void boottime_alarm_handler(int sig, siginfo_t *si, void *uc)
{
timer_t *tidptr; tidptr = si->si_value.sival_ptr;
printf("\n[%s] %s sig=%d\n", currTime("%T"), __func__, sig);
} int posix_timer_start(timer_t timer, clockid_t which_clock, struct sigevent *sev, struct itimerspec *ts)
{
if (timer_create(which_clock, sev, &timer) == -)
{
printf("Error timer_create %d\n", which_clock);
return -;
}
if (timer_settime(timer, , ts, NULL) == -)
{
printf("Error timer_settime %d\n", which_clock);
return -;
}
} //#define TEST_TIME
//#define TEST_SLEEP
#define TEST_TIMER int
main(int argc, char *argv[])
{ #if 0---------------------------------------------------------------------------------------------------------------读取进程的capabilities,_ALARM需要WAKEUP能力。
struct __user_cap_header_struct cap_header;
struct __user_cap_data_struct cap_data[]; cap_header.pid = getpid();
cap_header.version = _LINUX_CAPABILITY_VERSION_3;
if( capget(&cap_header, cap_data) < )
{
printf("%s\n", strerror(errno));
exit(EXIT_FAILURE);
} printf("capheader: %x %d\n", cap_header.version, cap_header.pid);
printf("capdata: %x %x %x\n", cap_data[].effective, cap_data[].permitted, cap_data[].inheritable);
printf("capdata: %x %x %x\n", cap_data[].effective, cap_data[].permitted, cap_data[].inheritable);
#endif //**********************************************Get time Test********************************************************
#ifdef TEST_TIME
struct timespec clock_ts;
struct timeval tv;
time_t t; t = time(NULL); ---------------------------------------------------------------------------------------time
print_time_t(&t); if(gettimeofday(&tv, NULL) == -)----------------------------------------------------------------------gettimeofday
printf("Error gettimeofday.\n");
print_timeval(&tv); clock_gettime(CLOCK_REALTIME, &clock_ts); print_timespec("CLOCK_REALTIME", &clock_ts);----------------clock_gettime...
clock_gettime(CLOCK_MONOTONIC, &clock_ts); print_timespec("CLOCK_MONOTONIC", &clock_ts);
clock_gettime(CLOCK_MONOTONIC_RAW, &clock_ts); print_timespec("CLOCK_MONOTONIC_RAW", &clock_ts);
clock_gettime(CLOCK_REALTIME_COARSE, &clock_ts); print_timespec("CLOCK_REALTIME_COARSE", &clock_ts);
clock_gettime(CLOCK_MONOTONIC_COARSE, &clock_ts); print_timespec("CLOCK_MONOTONIC_COARSE", &clock_ts);
clock_gettime(CLOCK_BOOTTIME, &clock_ts); print_timespec("CLOCK_BOOTTIME", &clock_ts);
clock_gettime(CLOCK_REALTIME_ALARM, &clock_ts); print_timespec("CLOCK_REALTIME_ALARM", &clock_ts);
clock_gettime(CLOCK_BOOTTIME_ALARM, &clock_ts); print_timespec("CLOCK_BOOTTIME_ALARM", &clock_ts);
#endif
//******************************************************************************************************************* //***********************************************Delay Test**********************************************************
#ifdef TEST_SLEEP
pthread_t pthread_sleep, pthread_nanosleep, pthread_clock_nanosleep_1, pthread_clock_nanosleep_2, pthread_clock_nanosleep_3, pthread_clock_nanosleep_4, pthread_clock_nanosleep_5;
int ret; ret = pthread_create(&pthread_sleep, NULL, sleep_func, "sleep");----------------------------------------------在不同线程中睡眠
if(ret != )
printf("pthread_create ret=%d\n", ret); ret = pthread_create(&pthread_nanosleep, NULL, nanosleep_func, "sleep");
if(ret != )
printf("pthread_create ret=%d\n", ret); ret = pthread_create(&pthread_clock_nanosleep_1, NULL, clock_nanosleep_CLOCK_REALTIME_func, "sleep");
if(ret != )
printf("pthread_create ret=%d\n", ret); ret = pthread_create(&pthread_clock_nanosleep_2, NULL, clock_nanosleep_CLOCK_REALTIME_ALARM_func, "sleep");
if(ret != )
printf("pthread_create ret=%d\n", ret); ret = pthread_create(&pthread_clock_nanosleep_3, NULL, clock_nanosleep_CLOCK_MONOTONIC_func, "sleep");
if(ret != )
printf("pthread_create ret=%d\n", ret); ret = pthread_create(&pthread_clock_nanosleep_4, NULL, clock_nanosleep_CLOCK_BOOTTIME_func, "sleep");
if(ret != )
printf("pthread_create ret=%d\n", ret); ret = pthread_create(&pthread_clock_nanosleep_5, NULL, clock_nanosleep_CLOCK_BOOTTIME_ALARM_func, "sleep");
if(ret != )
printf("pthread_create ret=%d\n", ret);
#endif
//******************************************************************************************************************* //**********************************************Timer Test********************************************************
#ifdef TEST_TIMER
struct itimerval itv;
struct itimerspec ts;
struct sigaction sa;
struct sigevent sev;
timer_t *tidlist;
int sig_no = ; tidlist = calloc(, sizeof(timer_t));
if (tidlist == NULL)
{
printf("Error malloc\n");
return -;
} itv.it_value.tv_sec = ;
itv.it_value.tv_usec = ;
itv.it_interval.tv_sec = ;
itv.it_interval.tv_usec = ; ts.it_value.tv_sec = ;
ts.it_value.tv_nsec = ;
ts.it_interval.tv_sec = ;
ts.it_interval.tv_nsec = ; printf("\n[%s] Start timer\n", currTime("%T")); //setitimer
itv.it_value.tv_sec = ;
sa.sa_flags = ;
sa.sa_handler = alarm_handler;
sigemptyset(&sa.sa_mask);
if (sigaction(SIGALRM, &sa, NULL) == -)
{
printf("Error sigaction line=%d\n", __LINE__);
return -;
} alarm(); if(setitimer(ITIMER_REAL, &itv, ) == -)------------------------------覆盖前面的alarm()
{
printf("Error setitimer line=%d\n", __LINE__);
return -;
} //POSIX Timer: CLOCK_REALTIME
ts.it_value.tv_sec = ;----------------------------------------------定时器时间
sig_no = __SIGRTMIN+;------------------------------------------------自定义信号
sa.sa_sigaction = realtime_handler;-----------------------------------handler
sa.sa_flags = SA_SIGINFO;
sigemptyset(&sa.sa_mask);
if (sigaction(sig_no, &sa, NULL) == -)-------------------------------注册信号处理函数
{
printf("Error sigaction line=%d\n", __LINE__);
return -;
} sev.sigev_notify = SIGEV_SIGNAL;
sev.sigev_signo = sig_no;
sev.sigev_value.sival_ptr = &tidlist[]; posix_timer_start(tidlist[], CLOCK_REALTIME, &sev, &ts);------------创建timer //POSIX Timer: CLOCK_MONOTONIC
ts.it_value.tv_sec = ;
sig_no = __SIGRTMIN+;
sa.sa_sigaction = monotonic_handler;
sa.sa_flags = SA_SIGINFO;
sigemptyset(&sa.sa_mask);
if (sigaction(sig_no, &sa, NULL) == -)
{
printf("Error sigaction line=%d\n", __LINE__);
return -;
} sev.sigev_notify = SIGEV_SIGNAL;
sev.sigev_signo = sig_no;
sev.sigev_value.sival_ptr = &tidlist[]; posix_timer_start(tidlist[], CLOCK_MONOTONIC, &sev, &ts); //POSIX Timer: CLOCK_BOOTTIME
ts.it_value.tv_sec = ;
sig_no = __SIGRTMIN+;
sa.sa_sigaction = boottime_handler;
sa.sa_flags = SA_SIGINFO;
sigemptyset(&sa.sa_mask);
if (sigaction(sig_no, &sa, NULL) == -)
{
printf("Error sigaction line=%d\n", __LINE__);
return -;
} sev.sigev_notify = SIGEV_SIGNAL;
sev.sigev_signo = sig_no;
sev.sigev_value.sival_ptr = &tidlist[]; posix_timer_start(tidlist[], CLOCK_BOOTTIME, &sev, &ts); //POSIX Timer: CLOCK_REALTIME_ALARM
ts.it_value.tv_sec = ;
sig_no = __SIGRTMIN+;
sa.sa_sigaction = realtime_alarm_handler;
sa.sa_flags = SA_SIGINFO;
sigemptyset(&sa.sa_mask);
if (sigaction(sig_no, &sa, NULL) == -)
{
printf("Error sigaction line=%d\n", __LINE__);
return -;
} sev.sigev_notify = SIGEV_SIGNAL;
sev.sigev_signo = sig_no;
sev.sigev_value.sival_ptr = &tidlist[]; posix_timer_start(tidlist[], CLOCK_REALTIME_ALARM, &sev, &ts); //POSIX Timer: CLOCK_BOOTTIME_ALARM
ts.it_value.tv_sec = ;
sig_no = __SIGRTMIN+;
sa.sa_sigaction = boottime_alarm_handler;
sa.sa_flags = SA_SIGINFO;
sigemptyset(&sa.sa_mask);
if (sigaction(sig_no, &sa, NULL) == -)
{
printf("Error sigaction line=%d\n", __LINE__);
return -;
} sev.sigev_notify = SIGEV_SIGNAL;
sev.sigev_signo = sig_no;
sev.sigev_value.sival_ptr = &tidlist[]; posix_timer_start(tidlist[], CLOCK_BOOTTIME_ALARM, &sev, &ts);
#endif
//*****************************************************************************************************************
// sleep(250);
// printf("[%s] Enter sleep\n", currTime("%T"));
// system("echo mem > /sys/power/state"); for (;;) /* Wait for incoming timer signals */
pause();
}

《Linux/Unix系统编程手册》 时间子系统的更多相关文章

  1. 《Linux/Unix系统编程手册》读书笔记9(文件属性)

    <Linux/Unix系统编程手册>读书笔记 目录 在Linux里,万物皆文件.所以文件系统在Linux系统占有重要的地位.本文主要介绍的是文件的属性,只是稍微提及一下文件系统,日后如果有 ...

  2. 《Linux/Unix系统编程手册》读书笔记8 (文件I/O缓冲)

    <Linux/Unix系统编程手册>读书笔记 目录 第13章 这章主要将了关于文件I/O的缓冲. 系统I/O调用(即内核)和C语言标准库I/O函数(即stdio函数)在对磁盘进行操作的时候 ...

  3. 《Linux/Unix系统编程手册》读书笔记6

    <Linux/Unix系统编程手册>读书笔记 目录 第9章 这章主要讲了一堆关于进程的ID.实际用户(组)ID.有效用户(组)ID.保存设置用户(组)ID.文件系统用户(组)ID.和辅助组 ...

  4. 《Linux/UNIX系统编程手册》第63章 IO多路复用、信号驱动IO以及epoll

    关键词:fasync_helper.kill_async.sigsuspend.sigaction.fcntl.F_SETOWN_EX.F_SETSIG.select().poll().poll_wa ...

  5. 《Linux/Unix系统编程手册》读书笔记 目录

    <Linux/Unix系统编程手册>读书笔记1  (创建于4月3日,最后更新4月7日) <Linux/Unix系统编程手册>读书笔记2  (创建于4月9日,最后更新4月10日) ...

  6. 《Linux/Unix系统编程手册》读书笔记7 (/proc文件的简介和运用)

    <Linux/Unix系统编程手册>读书笔记 目录 第11章 这章主要讲了关于Linux和UNIX的系统资源的限制. 关于限制都存在一个最小值,这些最小值为<limits.h> ...

  7. 《Linux/Unix系统编程手册》读书笔记5

    <Linux/Unix系统编程手册>读书笔记 目录 第8章 本章讲了用户和组,还有记录用户的密码文件/etc/passwd,shadow密码文件/etc/shadow还有组文件/etc/g ...

  8. 《Linux/Unix系统编程手册》读书笔记4

    <Linux/Unix系统编程手册>读书笔记 目录 第7章: 内存分配 通过增加堆的大小分配内存,通过提升program break位置的高度来分配内存. 基本学过C语言的都用过mallo ...

  9. 《Linux/Unix系统编程手册》读书笔记3

    <Linux/Unix系统编程手册>读书笔记 目录 第6章 这章讲进程.虚拟内存和环境变量等. 进程是一个可执行程序的实例.一个程序可以创建很多进程. 进程是由内核定义的抽象实体,内核为此 ...

  10. 《Linux/Unix系统编程手册》读书笔记1

    <Linux/Unix系统编程手册>读书笔记 目录 最近这一个月在看<Linux/Unix系统编程手册>,在学习关于Linux的系统编程.之前学习Linux的时候就打算写关于L ...

随机推荐

  1. java5后的并发池

    本文可作为传智播客<张孝祥-Java多线程与并发库高级应用>视频的学习记录. 为什么需要并发池 之前写并发的时候 new Thread(new Runnable(){ public voi ...

  2. 新版MATERIAL DESIGN 官方动效指南(三)

    运动 Material design 的动效会被类似真实世界中的力的影响,类似重力. 物体在屏幕内的运动 屏幕内物体在两点之间的运动,是沿着一条自然.凹陷的弧线.屏幕上所有的运动都可以使用标准曲线. ...

  3. LeetCode之“数学”:Reverse Integer && Reverse Bits

    1. Reverse Integer 题目链接 题目要求: Reverse digits of an integer. Example1: x = 123, return 321 Example2:  ...

  4. Android NFC开发(二)——Android世界里的NFC所具备的条件以及使用方法

    Android NFC开发(二)--Android世界里的NFC所具备的条件以及使用方法 NFC的应用比较广泛,而且知识面也是比较广的,所以就多啰嗦了几句,我还还是得跟着官方文档:http://dev ...

  5. 链路层 - SLIP,PPP,

    最常使用的封装格式是RFC 894定义的格式.图2 - 1显示了两种不同形式的封装格式.图中每个方框下面的数字是它们的字节长度. 两种帧格式都采用48 bit(6字节)的目的地址和源地址( 8 0 2 ...

  6. 开源项目AndroidReview学习小结(1)

    多看多学涨姿势 最近学习了一个开源项目,感觉收获颇多,这里做下简要的记录,首先感谢作者的开源.先看个大概图 感觉框架非常简单,界面也很一般,不过底层的处理的一些处理还是有很多可圈可点之处,代码的处理一 ...

  7. linux下的系统垃圾清理

    sudo apt-get autoclean --清理旧版本的软件缓存 sudo apt-get clean--清理所有软件缓存 sudo apt-get autoremove--删除系统不再使用的孤 ...

  8. 如何成为一个优秀的DBA

    今天在群上跟技术友们聊天.发现有几个大学生,他们很担心自己找不到工作.我问他们理由,他们说我们没有经验,公司不要怎么办?这些孩子们的担心,让我感觉到奇怪. 如果我是一个公司的老总,我会有这两种考虑:要 ...

  9. Java编程语言下Selenium 鼠标悬停以及右击操作

    // 基于Actions类创建一个对象 Actions action = new Actions(driver); // 鼠标悬停在药渡公司全称字段上 action.moveToElement(Yao ...

  10. 《你必须掌握的Entity Framework 6.x与Core 2.0》书籍出版

    前言 到目前为止写过刚好两百来篇博客,看过我博客的读者应该大概知道我每一篇博客都沿袭着一贯的套路,从前言到话题最终到总结,本文依然是一如既往的套路,但是不是介绍技术,也可说是介绍技术,不过是介绍书中的 ...