《Linux/Unix系統編程手冊》 時間子系統


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 0 on success, or –1 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) –1 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) –1 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 = 0;
    it_new.it_interval.tv_sec = it_new.it_interval.tv_usec = 0;

    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 >= 500000)
        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, 0);
    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 = 0 };
    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 != 0) {
        /* 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 ? 0 : 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 / 1000000),
                .tv_nsec = (long int) (usec % 1000000) * 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 / 1000000);
        tv.tv_usec = (long int) (usec % 1000000);
        return select(0, 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時間。

image

 

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)--------------------------(1)
SYSCALL_DEFINE4(timerfd_settime, int, ufd, int, flags,-----------------------------(2)
        const struct itimerspec __user *, utmr,
        struct itimerspec __user *, otmr)
SYSCALL_DEFINE2(timerfd_gettime, int, ufd, struct itimerspec __user *, otmr)-------(3)

 

 (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 == 0) ? 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(60);
    printf("[%s] End %s\n", currTime("%T"), __func__);
}

static void *nanosleep_func(void *arg)
{
    struct timespec request, remain;
    int ret = 0;
    
    printf("[%s] Start %s\n", currTime("%T"), __func__);
    request.tv_sec = 60;
    request.tv_nsec = 0;
    ret = nanosleep(&request, &remain);
    if (ret != 0 && 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 = 0;
    
    
    printf("[%s] Start %s\n", currTime("%T"), __func__);
    request.tv_sec = 60;
    request.tv_nsec = 0;
    ret = clock_nanosleep(CLOCK_REALTIME, flags, &request, &remain);
    if (ret != 0 && 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 = 0;
    
    
    printf("[%s] Start %s\n", currTime("%T"), __func__);
    request.tv_sec = 90;
    request.tv_nsec = 0;
    ret = clock_nanosleep(CLOCK_REALTIME_ALARM, flags, &request, &remain);
    if (ret != 0 && 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 = 0;
    
    
    printf("[%s] Start %s\n", currTime("%T"), __func__);
    request.tv_sec = 60;
    request.tv_nsec = 0;
    ret = clock_nanosleep(CLOCK_MONOTONIC, flags, &request, &remain);
    if (ret != 0 && 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 = 0;
    
    
    printf("[%s] Start %s\n", currTime("%T"), __func__);
    request.tv_sec = 60;
    request.tv_nsec = 0;
    ret = clock_nanosleep(CLOCK_BOOTTIME, flags, &request, &remain);
    if (ret != 0 && 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 = 0;

    printf("[%s] Start %s\n", currTime("%T"), __func__);
    request.tv_sec = 60;
    request.tv_nsec = 0;
    ret = clock_nanosleep(CLOCK_BOOTTIME_ALARM, flags, &request, &remain);
    if (ret != 0 && 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) == -1)
    {
        printf("Error timer_create %d\n", which_clock);
        return -1;
    }
    if (timer_settime(timer, 0, ts, NULL) == -1)
    {
        printf("Error timer_settime %d\n", which_clock);
        return -1;
    }
}

//#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[2];

    cap_header.pid = getpid();
    cap_header.version = _LINUX_CAPABILITY_VERSION_3;
    if( capget(&cap_header, cap_data) < 0)
    {
        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[0].effective, cap_data[0].permitted, cap_data[0].inheritable);
    printf("capdata: %x  %x  %x\n", cap_data[1].effective, cap_data[1].permitted, cap_data[1].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) == -1)----------------------------------------------------------------------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 != 0)
        printf("pthread_create ret=%d\n", ret);
    
    ret = pthread_create(&pthread_nanosleep, NULL, nanosleep_func, "sleep");
    if(ret != 0)
        printf("pthread_create ret=%d\n", ret);
    
    ret = pthread_create(&pthread_clock_nanosleep_1, NULL, clock_nanosleep_CLOCK_REALTIME_func, "sleep");
    if(ret != 0)
        printf("pthread_create ret=%d\n", ret);
    
    ret = pthread_create(&pthread_clock_nanosleep_2, NULL, clock_nanosleep_CLOCK_REALTIME_ALARM_func, "sleep");
    if(ret != 0)
        printf("pthread_create ret=%d\n", ret);
    
    ret = pthread_create(&pthread_clock_nanosleep_3, NULL, clock_nanosleep_CLOCK_MONOTONIC_func, "sleep");
    if(ret != 0)
        printf("pthread_create ret=%d\n", ret);
    
    ret = pthread_create(&pthread_clock_nanosleep_4, NULL, clock_nanosleep_CLOCK_BOOTTIME_func, "sleep");
    if(ret != 0)
        printf("pthread_create ret=%d\n", ret);
    
    ret = pthread_create(&pthread_clock_nanosleep_5, NULL, clock_nanosleep_CLOCK_BOOTTIME_ALARM_func, "sleep");
    if(ret != 0)
        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 = 0;

    tidlist = calloc(5, sizeof(timer_t));
    if (tidlist == NULL)
    {
        printf("Error malloc\n");
        return -1;
    }

    itv.it_value.tv_sec = 0;
    itv.it_value.tv_usec = 0;
    itv.it_interval.tv_sec = 0;
    itv.it_interval.tv_usec = 0;

    ts.it_value.tv_sec = 0;
    ts.it_value.tv_nsec = 0;
    ts.it_interval.tv_sec = 0;
    ts.it_interval.tv_nsec = 0;

    
    printf("\n[%s] Start timer\n", currTime("%T"));

//setitimer
    itv.it_value.tv_sec = 20;
    sa.sa_flags = 0;
    sa.sa_handler = alarm_handler;
    sigemptyset(&sa.sa_mask);
    if (sigaction(SIGALRM, &sa, NULL) == -1)
    {
        printf("Error sigaction line=%d\n", __LINE__);
        return -1;
    }

    alarm(10);

    if(setitimer(ITIMER_REAL, &itv, 0) == -1)------------------------------覆蓋前面的alarm()
    {
        printf("Error setitimer line=%d\n", __LINE__);
        return -1;
    }

//POSIX Timer: CLOCK_REALTIME
    ts.it_value.tv_sec = 40;----------------------------------------------定時器時間
    sig_no = __SIGRTMIN+5;------------------------------------------------自定義信號
    sa.sa_sigaction = realtime_handler;-----------------------------------handler
    sa.sa_flags = SA_SIGINFO;
    sigemptyset(&sa.sa_mask);
    if (sigaction(sig_no, &sa, NULL) == -1)-------------------------------注冊信號處理函數
    {
        printf("Error sigaction line=%d\n", __LINE__);
        return -1;
    }

    sev.sigev_notify = SIGEV_SIGNAL;
    sev.sigev_signo = sig_no;
    sev.sigev_value.sival_ptr = &tidlist[0];

    posix_timer_start(tidlist[0], CLOCK_REALTIME, &sev, &ts);------------創建timer

//POSIX Timer: CLOCK_MONOTONIC
    ts.it_value.tv_sec = 60;
    sig_no = __SIGRTMIN+6;
    sa.sa_sigaction = monotonic_handler;
    sa.sa_flags = SA_SIGINFO;
    sigemptyset(&sa.sa_mask);
    if (sigaction(sig_no, &sa, NULL) == -1)
    {
        printf("Error sigaction line=%d\n", __LINE__);
        return -1;
    }

    sev.sigev_notify = SIGEV_SIGNAL;
    sev.sigev_signo = sig_no;
    sev.sigev_value.sival_ptr = &tidlist[1];

    posix_timer_start(tidlist[1], CLOCK_MONOTONIC, &sev, &ts);

//POSIX Timer: CLOCK_BOOTTIME
    ts.it_value.tv_sec = 80;    
    sig_no = __SIGRTMIN+7;
    sa.sa_sigaction = boottime_handler;
    sa.sa_flags = SA_SIGINFO;
    sigemptyset(&sa.sa_mask);
    if (sigaction(sig_no, &sa, NULL) == -1)
    {
        printf("Error sigaction line=%d\n", __LINE__);
        return -1;
    }

    sev.sigev_notify = SIGEV_SIGNAL;
    sev.sigev_signo = sig_no;
    sev.sigev_value.sival_ptr = &tidlist[2];

    posix_timer_start(tidlist[2], CLOCK_BOOTTIME, &sev, &ts);

//POSIX Timer: CLOCK_REALTIME_ALARM
    ts.it_value.tv_sec = 100;    
    sig_no = __SIGRTMIN+8;
    sa.sa_sigaction = realtime_alarm_handler;
    sa.sa_flags = SA_SIGINFO;
    sigemptyset(&sa.sa_mask);
    if (sigaction(sig_no, &sa, NULL) == -1)
    {
        printf("Error sigaction line=%d\n", __LINE__);
        return -1;
    }

    sev.sigev_notify = SIGEV_SIGNAL;
    sev.sigev_signo = sig_no;
    sev.sigev_value.sival_ptr = &tidlist[3];

    posix_timer_start(tidlist[3], CLOCK_REALTIME_ALARM, &sev, &ts);

//POSIX Timer: CLOCK_BOOTTIME_ALARM
    ts.it_value.tv_sec = 120;    
    sig_no = __SIGRTMIN+9;
    sa.sa_sigaction = boottime_alarm_handler;
    sa.sa_flags = SA_SIGINFO;
    sigemptyset(&sa.sa_mask);
    if (sigaction(sig_no, &sa, NULL) == -1)
    {
        printf("Error sigaction line=%d\n", __LINE__);
        return -1;
    }

    sev.sigev_notify = SIGEV_SIGNAL;
    sev.sigev_signo = sig_no;
    sev.sigev_value.sival_ptr = &tidlist[4];

    posix_timer_start(tidlist[4], 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();
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM