青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

隨筆-167  評論-8  文章-0  trackbacks-0

轉自:

http://blog.chinaunix.net/space.php?uid=317451&do=blog&id=92667

簡介

這篇文章主要記錄我在試圖解決如何盡可能精確地在某個特定的時間間隔執行某項具體任務時的思路歷程,并在后期對相關的API進行的歸納和總結,以備參考。

問題引出

很多時候,我們會有類似“每隔多長時間執行某項任務”的需求,乍看這個問題并不難解決,實則并不容易,有很多隱含條件需要考慮,諸如:時間精度是多少?時間是否允許出現偏差,允許的偏差是多少,偏差之后如何處理?系統的負載如何?這個程序允許占用的系統資源是否有限制?這個程序運行的硬件平臺如何?

為了便于分析,我們鎖定題目為“每隔2妙打印當前的系統時間(距離UNIX紀元的秒數)”。

基于sleep的樸素解法

看到這個題目,我想大家的想法和我一樣,都是首先想到類似這樣的解法:


#include <stdio.h>

int main(int argc, char *argv[])
{
        while (1) {
                printf("%d\n", time(NULL));
                sleep(2);
        }

        return 0;
}


如果對時間精度要求不高,以上代碼確實能工作的很好。因為sleep的時間精度只能到1s:

       #include <unistd.h>

       unsigned int sleep(unsigned int seconds);


所以對于更高的時間精度(比如說毫秒)來說,sleep就不能奏效了。如果沿著這個思路走下去,還分別有精確到微妙和納秒的函數usleep和nanosleep可用:

      #include <unistd.h>

       int usleep(useconds_t usec);

   Feature Test Macro Requirements for glibc (see feature_test_macros(7)):

       usleep(): _BSD_SOURCE || _XOPEN_SOURCE >= 500



      #include <time.h>

       int nanosleep(const struct timespec *req, struct timespec *rem);

   Feature Test Macro Requirements for glibc (see feature_test_macros(7)):

       nanosleep(): _POSIX_C_SOURCE >= 199309L


既然有了能精確到納秒的nanosleep可用,上面的較低精度的函數也就可以休息了。實際上在Linux系統下,sleep和usleep就是通過一個系統調用nanosleep實現的。

用帶有超時功能的API變相實現睡眠

如果開發者不知道有usleep和nanosleep,這個時候他可能會聯想到select類的系統調用:

       According to POSIX.1-2001 */
       #include <sys/select.h>

       /* According to earlier standards */
       #include <sys/time.h>
       #include <sys/types.h>
       #include <unistd.h>

       int select(int nfds, fd_set *readfds, fd_set *writefds,
                  fd_set *exceptfds, struct timeval *timeout);


      #include <poll.h>

       int poll(struct pollfd *fds, nfds_t nfds, int timeout);


       #include <sys/epoll.h>

       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);


從函數原型和相關手冊來看,poll和epoll_wait能提供的時間精度為毫秒,select比他們兩個略勝一籌,為微秒,和前述的usleep相當。但是,果真如此么?這需要我們深入到Linux的具體實現,在內核里,這幾個系統調用的超時功能都是通過內核中的動態定時器實現的,而動態定時器的時間精度是由當前內核的HZ數決定的。如果內核的HZ是100,那么動態定時器的時間精度就是1/HZ=1/100=10毫秒。目前,X86系統的HZ最大可以定義為1000,也就是說X86系統的動態定時器的時間精度最高只能到1毫秒。由此來看,select用來指示超時的timeval數據結構,只是看起來很美,實際上精度和poll/epoll_wait相當。

基于定時器的實現

除了基于sleep的實現外,還有基于能用信號進行異步提醒的定時器實現:

#include <stdio.h>
#include <signal.h>

int main(int argc, char *argv[])
{
        sigset_t block;

        sigemptyset(&block);
        sigaddset(&block, SIGALRM);
        sigprocmask(SIG_BLOCK, &block, NULL);

        while (1) {
                printf("%d\n", time(NULL));
                alarm(2);
                sigwaitinfo(&block, NULL);
        }

        return 0;
}


顯然,上面的代碼并沒有利用信號進行異步提醒,而是通過先阻塞信號的傳遞,然后用sigwaitinfo等待并將信號取出的方法將異步化同步。這樣做的目的是為了盡可能減少非必要的信號調用消耗,因為這個程序只需要執行這個簡單的單一任務,所以異步除了帶來消耗外,并無任何好處。

讀者可能已經發現上面的代碼無非是把最初的代碼中的sleep換成了alarm和sigwaitinfo兩個調用,除了復雜了代碼之外,好像并沒有什么額外的好處。alarm的時間精度只能到1s,并且alarm和sigwaitinfo的確也可以看成是sleep的一種實現,實際上有的sleep確實是透過alarm來實現的,請看sleep的手冊頁:


BUGS
       sleep()  may be implemented using SIGALRM; mixing calls to alarm(2) and
       sleep() is a bad idea.

       Using longjmp(3) from a signal handler or  modifying  the  handling  of
       SIGALRM while sleeping will cause undefined results.

但是,這只是表象,本質他們是不同的,sleep是撥了一個臨時實時定時器并等待定時器到期,而alarm是用進程唯一的實時定時器來定時喚醒等待信號到來的進程執行。

如果需要更高的時間精度,可以采用精度為微秒的alarm版本ualarm:

       #include <unistd.h>

       useconds_t ualarm(useconds_t usecs, useconds_t interval);

   Feature Test Macro Requirements for glibc (see feature_test_macros(7)):

       ualarm(): _BSD_SOURCE || _XOPEN_SOURCE >= 500


或者是直接用setitimer操縱進程的實時定時器:

      #include <sys/time.h>

       int getitimer(int which, struct itimerval *value);
       int setitimer(int which, const struct itimerval *value,
                     struct itimerval *ovalue);


細心的你應該已經注意到了,ualarm和setitimer都額外提供了間隔時間的設置以便于間隔定時器用SIGALRM周期性的喚醒進程,這對于我們的需求有什么意義呢?請聽我慢慢道來。一般來說,需要定時執行的任務所消耗的時間都很短,至少都會少于間隔時間,否則這個需求就是無法實現的。我們前面的程序實現,都是假設任務消耗時間為0,實際上的任務并不總是像打印當前系統時間這么簡單,即便它們持續的時間真的短到相對來說可以忽略不計,如果這些小的忽略不計累積起來,也還是可能會造成長時間后的大偏差,所以我們有必要將這段時間計算進來。一種補救的措施是在任務執行的前后執行gettimeofday得到系統的時間,然后做差得到任務消耗時間并在接下來的“sleep”中將其扣除。問題看似解決了,但是我們畢竟沒有將系統進行上下文切換的時間和計算消耗時間的時間考慮進來,這樣的話,還是會存在較大的誤差。另一種計算量相對小些的算法是:直接通過時間間隔計算下一次超時的絕對時間,然后根據當前的絕對時間算出需要等待的時間并睡眠。但是,這也只是修修補補而已,并沒有從根本上解決問題。間隔定時器的出現從根本上解決了上面所提的問題,它自身就提供周期喚醒的功能,從而避免了每次都計算的負擔。因為ualarm已經被放棄,所以用setitimer再次改寫代碼:


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

int main(int argc, char *argv[])
{
        sigset_t block;
        struct itimerval itv;

        sigemptyset(&block);
        sigaddset(&block, SIGALRM);
        sigprocmask(SIG_BLOCK, &block, NULL);

        itv.it_interval.tv_sec = 2;
        itv.it_interval.tv_usec = 0;
        itv.it_value = itv.it_interval;
        setitimer(ITIMER_REAL, &itv, NULL);

        while (1) {
                printf("%d\n", time(NULL));
                sigwaitinfo(&block, NULL);
        }

        return 0;
}


進程的間隔計時器能夠提供的時間精度為微秒,對于大多數的應用來說,應該已經足夠,如果需要更高的時間精度,或者需要多個定時器,那么每個進程一個的實時間隔定時器就無能為力了,這個時候我們可以選擇POSIX實時擴展中的定時器:

      #include <signal.h>
       #include <time.h>

       int timer_create(clockid_t clockid, struct sigevent *restrict evp,
              timer_t *restrict timerid); 
       int timer_getoverrun(timer_t timerid);
       int timer_gettime(timer_t timerid, struct itimerspec *value);
       int timer_settime(timer_t timerid, int flags,
              const struct itimerspec *restrict value,
              struct itimerspec *restrict ovalue);


它實際上就是進程間隔定時器的增強版,除了可以定制時鐘源(nanosleep也存在能定制時鐘源的版本:clock_nanosleep)和時間精度提高到納秒外,它還能通過將evp->sigev_notify設定為如下值來定制定時器到期后的行為:
  • SIGEV_SIGNAL: 發送由evp->sigev_sino指定的信號到調用進程,evp->sigev_value的值將被作為siginfo_t結構體中si_value的值。
  • SIGEV_NONE:什么都不做,只提供通過timer_gettime和timer_getoverrun查詢超時信息。
  • SIGEV_THREAD: 以evp->sigev_notification_attributes為線程屬性創建一個線程,在新建的線程內部以evp->sigev_value為參數調用evp->sigev_notification_function。
  • SIGEV_THREAD_ID:和SIGEV_SIGNAL類似,不過它只將信號發送到線程號為evp->sigev_notify_thread_id的線程,注意:這里的線程號不一定是POSIX線程號,而是線程調用gettid返回的實際線程號,并且這個線程必須實際存在且屬于當前的調用進程。
更新后的程序如下(需要連接實時擴展庫: -lrt):

#include <stdio.h>
#include <signal.h>
#include <time.h>
#include <errno.h>
#include <sched.h>

int main(int argc, char *argv[])
{
        timer_t timer;
        struct itimerspec timeout;
        sigset_t block;
        struct sched_param param;

        sigemptyset(&block);
        sigaddset(&block, SIGALRM);
        sigprocmask(SIG_BLOCK, &block, NULL);

        timer_create(CLOCK_MONOTONIC, NULL, &timer);
        timeout.it_interval.tv_sec = 2;
        timeout.it_interval.tv_nsec = 0;
        timeout.it_value = timeout.it_interval;
        timer_settime(timer, 0, &timeout, NULL);

        while (1) {
                fprintf(stderr, "%d\n", time(NULL));
                sigwaitinfo(&block, NULL);
        }

        return 0;
}


至于時鐘源為什么是CLOCK_MONOTONIC而不是CLOCK_REALTIME,主要是考慮到系統的實時時鐘可能會在程序運行過程中更改,所以存在一定的不確定性,而CLOCK_MONOTONIC則不會,較為穩定。

至此為止,我們已經找到了目前Linux提供的精度最高的定時器API,它應該能滿足大多數情況的要求了。

其它問題

傳統信號的不可靠性

傳統UNIX信號是不可靠的,也就是說如果當前的信號沒有被處理,那么后續的同類信號將被丟失,而不是被排隊,而實時信號則沒有這個問題,它是被排隊的。聯系到當前應用,如果信號丟失,則是因為任務消耗了過多的處理器時間,而這個不確定性是那個任務帶來的,需要改進的應該是那個任務。

系統負載過高

如果系統的負載過高,使得我們的程序因為不能得到及時的調度導致時間精度降低,我們不妨通過nice提高當前程序的優先級,必要時可以通過sched_setscheduler將當前進程切換成優先級最高的實時進程已確保得到及時調度。

硬件相關的問題

硬件配置也極大的影響著定時器的精度,有的比較老的遺留系統可能沒有比較精確的硬件定時器,那樣的話我們就無法期待它能提供多高的時鐘精度了。相反,如果系統的配置比較高,比如說對稱多處理系統,那么即使有的處理器負載比較高,我們也能通過將一個處理器單獨分配出來處理定時器來提高定時器的精度。

更高的時間精度

雖然,Linux的API暗示它能夠提供納秒級的時間精度,但是,由于種種不確定因素,它實際上并不能提供納秒級的精度,比較脆弱。如果你需要更高強度的實時性,請考慮采用軟實時系統、硬實時系統、專有系統,甚至是專業硬件。

注意:

為了簡便,以上所有代碼都沒有出錯處理,請讀者在現實的應用中自行加入出錯處理,以提高程序的健壯性。尤其注意sleep類的返回值,它們可能沒到期就返回,這個時候你應該手動計算需要再睡眠多長才能滿足原始的睡眠時間要求,如果該API并沒有返回剩余的時間的話。
posted on 2011-08-01 11:32 老馬驛站 閱讀(1668) 評論(0)  編輯 收藏 引用 所屬分類: linux
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            国产精品99久久99久久久二8| 午夜精品久久| 亚洲视频 欧洲视频| 又紧又大又爽精品一区二区| 国产精品福利在线| 亚洲老司机av| 制服诱惑一区二区| 亚洲精品美女在线| 国产视频在线一区二区| 国产欧美日韩亚洲一区二区三区| 久久久久久网址| 久久久999精品视频| 午夜欧美大尺度福利影院在线看| 亚洲免费视频成人| 欧美精品福利在线| 欧美国产三区| 欧美激情黄色片| 欧美日韩亚洲高清| 夜夜爽夜夜爽精品视频| 久久综合999| 亚洲电影在线免费观看| 男男成人高潮片免费网站| 欧美激情1区2区| 亚洲精品黄色| 久久国产精品高清| 亚洲在线视频观看| 午夜精品视频| 免费久久99精品国产自| 欧美激情视频网站| 国产精品久久久久久久久久尿| 国产精品久久久久久久久婷婷| 国产精品国产三级国产aⅴ浪潮| 欧美日韩一区二区三区| 欧美成人国产va精品日本一级| 久久久999精品视频| 久久国产视频网| 欧美黄色网络| 久久久久久高潮国产精品视| 久久亚洲精选| 欧美日本韩国一区二区三区| 国产欧美亚洲日本| 国产毛片一区| 国产一在线精品一区在线观看| 欧美日韩91| 国产日韩欧美一区二区| 欧美日韩视频在线一区二区| 久久精品99无色码中文字幕| 老司机午夜精品视频在线观看| 欧美日韩国产综合一区二区 | 亚洲毛片在线观看.| 国内精品美女在线观看| 亚洲精品综合| 久久美女性网| 国产精品99久久不卡二区 | 国内揄拍国内精品久久| 亚洲第一免费播放区| 最新日韩在线| 一本色道久久综合狠狠躁篇怎么玩| 欧美中文字幕视频在线观看| 久久三级视频| 亚洲一区二区三区精品视频| 久久成人免费日本黄色| 国产精品久久久久99| 亚洲最新在线| 久久免费视频在线观看| 亚洲手机成人高清视频| 另类尿喷潮videofree| 国产一区成人| 正在播放日韩| 亚洲第一区在线观看| 亚洲欧美日韩视频二区| 最新日韩精品| 久久久久久有精品国产| 国精产品99永久一区一区| 亚洲欧美一区二区激情| 日韩一区二区精品在线观看| 亚洲精品国产精品国自产观看浪潮 | 久久免费视频网| 一本色道**综合亚洲精品蜜桃冫 | 一色屋精品视频在线观看网站| 欧美一区二区三区精品| 亚洲欧美日韩成人| 国产日韩欧美精品| 欧美一区二区在线| 欧美激情1区2区| 欧美高清视频www夜色资源网| 亚洲人成小说网站色在线| 亚洲国产精品精华液网站| 欧美精品亚洲精品| 国产精品99久久不卡二区| 亚洲伦理在线观看| 国产精品激情偷乱一区二区∴| 亚洲欧美韩国| 久久国产加勒比精品无码| 国产在线视频欧美| 欧美激情国产高清| 欧美视频精品在线| 久久精品国产清高在天天线 | 亚洲天堂网在线观看| 久久精品99国产精品| 亚洲精品乱码视频 | 一区二区三区国产精华| 国产精品一区二区你懂得| 久久精品国产免费观看| 欧美专区在线播放| 亚洲人成网站在线播| 在线午夜精品自拍| 国产精品二区二区三区| 久久久久久亚洲精品杨幂换脸 | 欧美成人综合一区| 欧美激情在线| 亚洲国产高清aⅴ视频| 久久久久综合一区二区三区| 亚洲午夜一区二区| 在线电影院国产精品| 日韩天堂在线观看| 在线观看91精品国产麻豆| 亚洲免费观看视频| 精品成人在线视频| 国产精品99久久久久久www| 红桃视频国产一区| 一区二区免费在线视频| 在线视频观看日韩| 亚洲一区精彩视频| 亚洲美女诱惑| 久久亚洲综合网| 欧美自拍偷拍午夜视频| 欧美激情中文字幕一区二区| 欧美在线亚洲在线| 欧美日韩精品二区| 欧美国产日韩在线| 国产日韩综合一区二区性色av| 在线观看亚洲一区| 亚洲欧洲精品成人久久奇米网| 欧美色大人视频| 欧美激情一二区| 国产日韩精品久久| 中文久久精品| 在线中文字幕一区| 久久午夜精品| 久久精品色图| 国产精品久久久久影院亚瑟 | 亚洲网站在线观看| 亚洲精品一线二线三线无人区| 欧美在线观看网址综合| 亚洲在线一区| 欧美日韩免费观看一区| 亚洲国产成人精品视频| 黄色成人免费观看| 亚洲国产一区二区三区在线播| 国产午夜精品美女视频明星a级| 亚洲婷婷免费| 亚洲一品av免费观看| 欧美国产日韩一区二区在线观看| 欧美成人亚洲| 国产午夜精品久久| 欧美亚洲在线播放| 久久久久久久综合日本| 国产在线欧美| 久久免费午夜影院| 欧美成人免费全部| 亚洲精品国产拍免费91在线| 免费在线观看成人av| 蜜乳av另类精品一区二区| 国产欧美日韩在线观看| 羞羞漫画18久久大片| 久久久噜噜噜久久| 亚洲免费成人av| 欧美日韩中文字幕在线视频| 欧美一区二区三区播放老司机| 免费黄网站欧美| 亚洲一二三区视频在线观看| 国产深夜精品福利| 欧美精品在线视频观看| 欧美在线关看| 亚洲精品国产日韩| 久久免费精品日本久久中文字幕| 一本大道久久a久久精二百| 国产一区二区三区成人欧美日韩在线观看| 久久先锋影音| 午夜视频一区在线观看| 最近看过的日韩成人| 久久一区二区三区超碰国产精品| 亚洲先锋成人| 亚洲三级观看| 在线观看亚洲专区| 国产亚洲成精品久久| 欧美日韩一区免费| 欧美激情一区二区三区在线视频| 久久不射2019中文字幕| 日韩午夜视频在线观看| 免费观看欧美在线视频的网站| 亚洲一区二区三区精品在线| 亚洲久色影视| 亚洲国产成人精品久久| 精品av久久707| 国产日韩欧美在线看| 国产精品v欧美精品v日韩精品| 欧美—级高清免费播放| 欧美本精品男人aⅴ天堂|