前言
sleep() 僅支援秒級休眠,無法滿足高精度需求。Linux 提供多種微秒/奈秒級休眠方案:setitimer() 間隔定時器、select() 超時機制、usleep()、nanosleep()。本文詳細比較其原理、精度與適用場景。
1. setitimer() 間隔定時器
#include <sys/time.h>
int setitimer(int which, const struct itimerval *new_value, struct itimerval *old_value);
三種定時器類型
| 常數 | 說明 | 信號 |
ITIMER_REAL | 真實時間 (牆鐘時間),最常用 | SIGALRM |
ITIMER_VIRTUAL | 進程用戶態 CPU 時間 | SIGVTALRM |
ITIMER_PROF | 進程用戶態+核心態 CPU 時間 | SIGPROF |
時間結構
struct itimerval {
struct timeval it_interval; // 間隔週期 (週期性觸發)
struct timeval it_value; // 初始延遲 / 下次觸發剩餘時間
};
struct timeval {
time_t tv_sec; // 秒
suseconds_t tv_usec; // 微秒 (0~999999)
};
休眠範例 (單次)
void ms_sleep(long ms) {
struct itimerval it = { .it_value = { .tv_sec = ms/1000, .tv_usec = (ms%1000)*1000 } };
setitimer(ITIMER_REAL, &it, NULL);
pause(); // 等待 SIGALRM
}
缺點
- 進程僅能擁有 3 個定時器 (對應三種類型),資源有限。
- 需設置信號處理函數,
pause() 會被任何信號喚醒,需重新檢查時間。
- 信號處理上下文切換開銷大。
2. select() / pselect() 超時休眠 (推薦)
#include <sys/select.h>
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
微秒級休眠封裝
int us_sleep(long us) {
struct timeval tv = { .tv_sec = us / 1000000, .tv_usec = us % 1000000 };
return select(0, NULL, NULL, NULL, &tv);
}
優點
- 無需信號,不佔用定時器資源。
- 精度達微秒級 (受限於系統時鐘中斷頻率 HZ,現代核心動態滴答可達高精度)。
- 可被信號中斷 (
EINTR),易於整合事件迴圈。
3. usleep() 與 nanosleep()
| 函數 | 精度 | 標準 | 備註 |
usleep(useconds_t) | 微秒 | BSD / POSIX.1-2001 (標記過時) | 內部常用 nanosleep() 實現 |
nanosleep(const struct timespec *, struct timespec *) | 奈秒 | POSIX.1b (即時擴展) | 支援剩餘時間回傳 (remain),信號安全 |
timespec 結構
struct timespec {
time_t tv_sec; // 秒
long tv_nsec; // 奈秒 (0~999999999)
};
nanosleep 範例
void ns_sleep(long ns) {
struct timespec req = { .tv_sec = ns / 1000000000, .tv_nsec = ns % 1000000000 };
struct timespec rem;
while (nanosleep(&req, &rem) == -1 && errno == EINTR) {
req = rem; // 被信號中斷,續睡剩餘時間
}
}
4. 精度與時鐘源
- 核心配置
CONFIG_HIGH_RES_TIMERS + 動態滴答 (NO_HZ) 可達奈秒級精度。
- 硬體時鐘源 (TSC, HPET, ACPI PM) 影響實際精度,
cat /sys/devices/system/clocksource/clocksource0/current_clocksource 查看。
- 進程調度延遲 (CFS、即時優先級) 通常是誤差主因,非 API 本身。
5. 選擇建議
| 場景 | 推薦 API |
| 簡單微秒級延遲 | usleep() / select() |
| 高精度週期性任務 | timer_create() + timer_settime() (POSIX 定時器) |
| 事件驅動主迴圈 | select()/poll()/epoll() 超時 |
| 需被信號中斷並續睡 | nanosleep() |
| 硬體即時控制 | 考慮 SCHED_FIFO + clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME) |
結語
現代 Linux 應用開發中,nanosleep() 為首選高精度休眠 API,select() 適合事件迴圈整合,setitimer() 因資源限制與信號複雜度逐漸被 timerfd / POSIX 定時器取代。理解時鐘源、調度延遲與信號交互是實現精確定時的關鍵。