本文示範如何在 Linux/Unix 環境下,結合 SIGINT 信號處理與 sigprocmask/sigsuspend 原語,實作一個輕量級的檔案大小變更監控器。核心邏輯:
sigflag 翻轉stat() 比對檔案大小,若異則報警static volatile sig_atomic_t sigflag = 0;
static void TurnFlag(int signo) {
sigflag = (sigflag == 0) ? 1 : 0; // 翻轉旗標
}
int InitHandler(int signo) {
struct sigaction act = {0};
act.sa_handler = TurnFlag;
act.sa_flags = 0;
sigemptyset(&act.sa_mask);
sigaddset(&act.sa_mask, signo);
return sigaction(signo, &act, NULL);
}
重點:sig_atomic_t 保證原子讀寫;sa_mask 在處理器執行期間自動阻塞同一信號,防止重入。
int ControlSig(int signo) {
sigset_t unblock, block, oldmask;
sigprocmask(SIG_SETMASK, NULL, &unblock); // 取得目前遮罩
block = unblock;
sigaddset(&block, signo); // 欲阻塞的信號
sigdelset(&unblock, signo); // 欲解除阻塞的集合
sigprocmask(SIG_BLOCK, &block, &oldmask); // 阻塞信號、保存舊遮罩
while (sigflag != 0) // 旗標已置位 → 處理完畢前持續等待
sigsuspend(&unblock); // 原子性:解除阻塞並休眠
sigprocmask(SIG_SETMASK, &oldmask, NULL); // 還原遮罩
return 0;
}
設計精華:sigsuspend() 原子地「切換遮罩 + 睡眠」,避免 sigprocmask() + pause() 之間的競態條件。
int main() {
struct stat oldst, newst;
const char *path = "makefile";
const int interval = 2;
int signo = SIGINT;
stat(path, &oldst);
for (;;) {
InitHandler(signo);
ControlSig(signo); // 等待信號觸發
stat(path, &newst);
if (newst.st_size != oldst.st_size) {
oldst.st_size = newst.st_size;
fprintf(stderr, "檔案大小已變更!目前大小:%ld\n", newst.st_size);
}
sleep(interval);
}
}
epoll 監控多個 inotify_fd。ReadDirectoryChangesW,macOS 採 FSEvents,統一抽象介面。sigflag 必須宣告為 volatile sig_atomic_t,否則編譯器優化可能導致主迴圈看不到更新。sigsuspend() 的遮罩參數是「臨時解除阻塞的集合」,而非「要阻塞的集合」——初學者最易搞反。printf、malloc、fprintf),本例僅翻轉旗標屬安全操作。