在 Unix/Linux 系統程式設計中,sleep 與 alarm 是兩個常用的定時相關函數。它們看似簡單,但結合使用時卻容易產生微妙的行為差異。本文將透過實例深入剖析兩者的運作機制、返回值含義,以及如何正確協調它們以實現精確的定時控制。
sleep 函數聲明於 <unistd.h>,原型為:
unsigned int sleep(unsigned int seconds);
它會將調用進程掛起,直到滿足以下任一條件:
seconds 秒(牆上時鐘時間)注意:由於系統調度等因素,實際喚醒時間可能比請求時間稍晚,這點與 alarm 類似。
考慮下列程式碼片段:
#define DIAPAUSE 30 // sleep 30 秒
#define TIMEOUT 5 // alarm 5 秒
void sigalrm(int signo) { printf("timeout...\n"); return; }
int main() {
struct sigaction sa = {0};
sa.sa_handler = sigalrm;
sigemptyset(&sa.sa_mask);
sigaction(SIGALRM, &sa, NULL);
while(1) {
printf("alarm(TIMEOUT)...\n");
alarm(TIMEOUT);
printf("...\n");
printf("sleep(DIAPAUSE) == %d\n", sleep(DIAPAUSE));
}
return 0;
}
alarm(5) 設置 5 秒後發送 SIGALRMsleep(30) 開始睡眠SIGALRM 觸發,信號處理函數 sigalrm 執行sleep 被中斷,返回剩餘時間:30 - 5 = 25alarm(5) 和 sleep(30)...因此程式會持續輸出 sleep(DIAPAUSE) == 25。
若 TIMEOUT > DIAPAUSE(如 50),sleep(30) 會在 30 秒後正常返回,返回值為 0。此時 alarm 尚未觸發,但下一次循環的 alarm(50) 會重置計時器,原有的 50 秒鬧鐘被取消。
實際開發中(如 Socket 通訊超時控制),常見需求:在某個操作後睡眠固定時間(如 30 秒),期間不受先前 alarm 干擾。
關鍵在於 alarm 的特性:
alarm(0) 可取消現有計時器,並返回剩餘秒數alarm 調用會替換舊計時器在 printf("...\n"); 之後、sleep(DIAPAUSE) 之前插入 alarm(0);:
while(1) {
printf("alarm(TIMEOUT)...\n");
alarm(TIMEOUT); // 設置業務超時鬧鐘
printf("...\n");
alarm(0); // 關鍵:清除業務鬧鐘,避免干擾後續固定睡眠
printf("sleep(DIAPAUSE) == %d\n", sleep(DIAPAUSE));
}
這樣 sleep(30) 就能安穩睡滿 30 秒,不被提前喚醒,完美滿足「先處理業務超時,再固定間隔輪詢」的雙重定時需求。
sleep 返回值揭示了是否被信號中斷alarm 單計時器特性決定了新舊鬧鐘互斥alarm(0) 可精確控制定時器生命週期掌握這些細節,能讓你在網絡編程、守護進程、定時任務等場景中遊刃有餘。