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. web容器的会话机制

    基本所有web应用开发的朋友都很熟悉session会话这个概念,在某个特定时间内,我们说可以在一个会话中存储某些状态,需要的时候又可以把状态取出来,这整个过程的时间空间可以抽象成"会话&qu ...

  2. 求剁手的分享,如何简单开发js图表

    前段时间做的一个项目里需要用到js图表,在网上找了下,大概找到了highcharts.fusioncharts这些国外产品. 因为都收费,虽然有盗版,我也不敢用,万一被找上们来就砸锅卖铁了要.自己写j ...

  3. linux服务搭建---yum源服务搭建

    yum源服务 1.本地yum源 2.yum源不在本地          1>  ftp服务器     2>  nfs服务器 1.本地yum源 前提:    linux系统   找到一个相应 ...

  4. 销售行业ERP数据统计分析都有哪些维度?

    场景描述 当前的企业信息化建设主要包括ERP系统.OA系统等.企业希望实现信息系统数据的整合,对企业资源进行分析汇总,方便对企业相关数据的掌控从而便于对业务流程进行及时调整监控. 但是由于系统间数据的 ...

  5. Hadoop基本知识,(以及MR编程原理)

     hadoop核心是:MapReduce和HDFS (对应着job执行(程序)和文件存储系统(数据的输入和输出)) CRC32作数据交验:在文件Block写入的时候除了写入数据还会写入交验信息,在读取 ...

  6. The 13th tip of DB Query Analyzer, powerful processing EXCEL file

    The 13thtip of DB Query Analyzer, powerful processing EXCEL file MA Genfeng (Guangdong UnitollServic ...

  7. 恶补web之八:jQuery(3)

    jquery和其他js框架.jQuery使用$作为jQuery的简写,但是还有很多js框架,比如: MooTools,Backbone,Sammy,Cappuccino,Knockout,JavaSc ...

  8. Web服务cxf框架发布2

    原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本人声明.否则将追究法律责任. 作者:永恒の_☆ 地址:http://blog.csdn.net/chenghui0317/ ...

  9. [坑况]——windows升级node最新版本报错【npm install -g n】

    我本来是下载一个vue-cli的,然后技术日新月异,告知我要先把我的node升级到8以上(目前是v6.1.13) 升级就升级,升级就报错 尝试第一种方法,网上最多的一种方法,估计也是成功最多的一种吧( ...

  10. Odoo 学习【一】http & rpc

    HTTP Odoo 中http类中的Root是wsgi应用的入口主程序. 入口,wsgi_server调用如下: def application(environ, start_response): i ...