天天看點

Linux下的時間

版權聲明:本文為部落客原創文章,未經部落客允許不得轉載。 https://blog.csdn.net/feilengcui008/article/details/51427031

時鐘

  • 硬體時鐘
    • RTC(real time clock),記錄wall clock time,硬體對應到/dev/rtc裝置檔案,讀取裝置檔案可得到硬體時間
    • 讀取方式
      • 通過ioctl

        #include <linux/rtc.h> int ioctl(fd, RTC_request, param);

      • hwclock指令
    • 通常核心在boot以及從低電量中恢複時,會讀取RTC更新system time
  • 軟體時鐘
    • HZ and jiffies, 由核心維護,對于PC通常HZ配置為 1s / 10ms = 100
    • 精度影響select等依賴timeout的系統調用
    • HRT(high-resolution timers). Linux 2.6.21開始,核心支援高精度定時器,不受核心jiffy限制,可以達到硬體時鐘的精度。
  • 外部時鐘
    • 從網絡ntp,原子鐘等同步

時間

  • 時間類别
    • wall clock time => 硬體時間
    • real time => 從某個時間點(比如Epoch)開始的系統時間
    • sys and user time => 通常指程式在核心态和使用者态花的時間
  • 時間的表示
    • time_t 從Epoch開始的秒數
    • calendar time 字元串
    • 拆分時間 struct tm

      struct tm { int tm_sec; /* seconds */ int tm_min; /* minutes */ int tm_hour; /* hours */ int tm_mday; /* day of the month */ int tm_mon; /* month */ int tm_year; /* year */ int tm_wday; /* day of the week */ int tm_yday; /* day in the year */ int tm_isdst; /* daylight saving time */ };

    • struct timeval/struct timespec
      struct timeval {
        time_t seconds;
        suseconds_t useconds;
      }
      
      struct timespec {
        time_t   tv_sec;        /* seconds */
        long     tv_nsec;       /* nanoseconds */
      };           

系統時間的操作

#include <time.h>
#include <sys/time.h>

// number of seconds since epoch
time_t time(time_t *t) 

//參數time_t*
char *ctime(const time_t *timep);
char *ctime_r(const time_t *timep, char *buf);

struct tm *gmtime(const time_t *timep);
struct tm *gmtime_r(const time_t *timep, struct tm *result);

struct tm *localtime(const time_t *timep);
struct tm *localtime_r(const time_t *timep, struct tm *result);

//參數struct tm*
char *asctime(const struct tm *tm);
char *asctime_r(const struct tm *tm, char *buf);
time_t mktime(struct tm *tm);


int gettimeofday(struct timeval *tv, struct timezone *tz);//如果系統時間調整了會影響
int clock_gettime(clockid_t clk_id, struct timespec *tp);

//将tm按照format處理後放到s
size_t strftime(char *s, size_t max, const char *format, const struct tm *tm);

//将字元串時間s按照format格式化後放入tm
char *strptime(const char *s, const char *format, struct tm *tm);
           

定時器

  • sleep
unsigned int sleep(unsigned int seconds);           
  • usleep
int usleep(useconds_t usec);           
  • nanosleep
int nanosleep(const struct timespec *req, struct timespec *rem);           
  • alarm
// SIGALARM after seconds
unsigned int alarm(unsigned int seconds);           
  • timer_create
int timer_create(clockid_t clockid, struct sigevent *sevp,
                        timer_t *timerid);           
  • setitimer
  • timerfd_create + select/poll/epoll
int timerfd_create(int clockid, int flags);           
  • select
// struct timeval可以精确到微秒(如果硬體有高精度時鐘支援)
int select(int nfds, fd_set *readfds, fd_set *writefds,
                  fd_set *exceptfds, struct timeval *timeout);
// struct timespec可以精确到納秒,但是pselect下次無法修改timeout 
int pselect(int nfds, fd_set *readfds, fd_set *writefds,
                   fd_set *exceptfds, const struct timespec *timeout,
                   const sigset_t *sigmask);

// 一般能提供周期,延時,時間點觸發,但核心還是時間點觸發的timer
// 1.call_period => 觸發一次重新注冊call_at
// 2.call_later => 轉換為call_at 
// 3.call_at => 時間點觸發的timer可以用一個優先級隊列儲存
           
  • poll
// timeout最小機關ms,并且rounded up to系統時鐘的精度
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
// 注意timespec會被轉換成ms
int ppoll(struct pollfd *fds, nfds_t nfds,
               const struct timespec *timeout_ts, const sigset_t *sigmask);
           
  • epoll
// timeout最小機關ms,并且rounded up to系統時鐘的精度
int epoll_wait(int epfd, struct epoll_event *events,
                      int maxevents, int timeout);
int epoll_pwait(int epfd, struct epoll_event *events,
                      int maxevents, int timeout,
                      const sigset_t *sigmask);           
  • eventfd + select/poll/epoll

    一個fd可同時負責讀接受事件通知和寫觸發事件通知

  • signaled + select/poll/epoll

    借助alarm/setitimer/timer_create等觸發的SIGALARM,通過signalfd傳遞到多路複用中

  • pipe + select/poll/epoll

    一端另起線程定時觸發,另一端放到多路複用中

分布式系統的時間

扯點其他的東西:-)。時間是個複雜而有意思的東西,在單機上不同處理器不同程序不同線程可以讀到同一個系統時鐘CLOCK_REALTIME,而且在一定時間範圍内t1~t2發生的事件,即使在t1之前,t2之後系統時間與真實時間發生了一定偏移,隻要時間戳的相對順序沒亂,那麼我們就可以完全确定t1~t2時間戳之間不同線程發生事件的順序。但是不同機器之間的系統時間總會互相漂移(ntp區域網路0.1ms左右,網際網路1-50ms左右),導緻我們沒法直接使用系統時間(google的原子鐘也是将一個時間段作為時間點的,隻要這個時間段比較小,那麼性能應該可以接收),是以需要logic clock以及衍生出來的vector clock或者version number等。

沒有全局時鐘是分布式系統需要一緻性算法的一個重要原因,因為我們沒辦法根據單機的系統時間戳來判斷多台機器之間事件的先後順序,那麼對于一個新的節點,我們要把之前所有的時間atomic broadcast到這個新節點就會出現問題,是以這也是分布式一緻性算法(Paxos/Raft/Viewstamp Replication/Zab)解決的一個問題,當然再加上網絡的異步,以及無法獲知各個節點的全局狀态,以及機器crash等各種問題,這些算法往往加入了容錯性。

繼續閱讀