Windows XP Windows 7 Windows 2003 Windows Vista Windows 綜合教學 Linux 系統教學
Windows 10 Windows 8 Windows 2008 Windows NT Windows Server 電腦軟體教學

Linux mysleep 函數實現與競態條件分析 (alarm/pause/sigaction)

編輯:Linux 系統常見問題解答

基於 alarm()、pause()、sigaction() 實現可重入的 mysleep(),並分析信號處理中可能出現的競態條件與優化方案。

核心介面

函數參數返回值
alarm(seconds)unsigned int0 或剩餘秒數
pause()void僅出錯返回 -1 (errno=EINTR)
sigaction(signo, &act, &oact)int, struct sigaction*, struct sigaction*成功 0,失敗 -1

sigaction 結構體關鍵欄位


struct sigaction {
    void (*sa_handler)(int);           // 傳統處理函數
    void (*sa_sigaction)(int, siginfo_t*, void*); // 擴充處理函數 (SA_SIGINFO)
    sigset_t sa_mask;                  // 處理期間暫時阻塞的信號集
    int sa_flags;                      // SA_RESTART, SA_NODEFER, SA_RESETHAND 等
    void (*sa_restorer)(void);         // 已棄用
};

mysleep 參考實現


#include 
#include 

void myhandler(int sig) {}  // 空處理函數,僅為中斷 pause

int mysleep(int timeout) {
    struct sigaction act, oact;
    act.sa_handler = myhandler;
    sigemptyset(&act.sa_mask);
    act.sa_flags = 0;
    sigaction(SIGALRM, &act, &oact);  // 註冊 SIGALRM 處理
    alarm(timeout);                    // 設定鬧鐘
    pause();                           // 等待信號
    int ret = alarm(0);                // 取消鬧鐘,獲取剩餘秒數
    sigaction(SIGALRM, &oact, NULL);   // 恢復原處理
    return ret;
}

int main() {
    while (1) {
        printf("mysleep start...\n");
        mysleep(3);
        printf("mysleep success\n");
    }
    return 0;
}

執行流程與競態條件分析

  1. main 呼叫 mysleep → 註冊 SIGALRM 處理函數 myhandler (預設動作為終止進程,必須覆蓋)。
  2. alarm(timeout) 設定核心定時器。
  3. pause() 讓進程睡眠,核心切換至其他進程。
  4. timeout 秒後,核心發送 SIGALRM 給進程。
  5. 核心態返回用戶態前處理未決信號,發現 SIGALRM,執行 myhandler (信號自動屏蔽/解除屏蔽)。
  6. myhandler 返回 → sigreturn 系統調用 → 回用戶態繼續 pause() 後指令 (alarm(0))。

潛在競態條件

  • 若在 alarm() 後、pause() 前進程收到其他信號並執行處理函數,pause() 可能提前返回,導致睡眠時間不足。
  • 多執行緒環境下,信號可能遞送至任一執行緒,pause() 僅阻塞呼叫執行緒,其他執行緒繼續運行。
  • alarm() 與 setitimer() 共用實時定時器,相互覆蓋。

優化方案

  • 使用 nanosleep() (POSIX.1b) 替代,無信號副作用、奈秒精度、可被信號中斷並返回剩餘時間結構體。
  • 若必須用信號,設置 sa_flags = SA_RESTART 讓系統自動重啟被中斷的系統調用 (部分系統不支援 pause)。
  • 關鍵區使用 sigprocmask() 封鎖 SIGALRM 避免競態。

教學用途理解信號機制足夠,生產環境請優先使用 nanosleep / clock_nanosleep / std::this_thread::sleep_for 等高階 API。

Copyright © Windows教程網 All Rights Reserved