Windows XP Windows 7 Windows 2003 Windows Vista Windows 綜合教學 Linux 系統教學
Windows 10 Windows 8 Windows 2008 Windows NT Windows Server 電腦軟體教學
您现在的位置: Windows教程網 >> Windows教程 >  >> Linux 系統教學 >> Linux 教學

Linux C 程式設計:sleep、fflush 與標準 I/O 緩衝區深度解析

編輯:Linux 教學

前言

Linux 系統程式設計中,sleep 族函數控制執行緒休眠,fflush 控制輸出緩衝區刷新,而標準 I/O 的三種緩衝模式(全緩衝、行緩衝、無緩衝)直接決定程式輸出行為。本文結合 POSIX 標準、glibc 實現與 APUE 經典理論,系統梳理這三大主題的坑點與最佳實踐。

一、sleep 族函數對比

函數標準精度參數單位備註
sleep(sec)POSIX.1秒unsigned int 秒信號中斷返回剩餘秒數
usleep(usec)BSD/POSIX.1-2001 (已废弃)微秒useconds_t最大 1,000,000,建議改用 nanosleep
nanosleep(&ts, &rem)POSIX.1b奈秒struct timespec高精度、支援剩餘時間返回、信號安全
clock_nanosleep()POSIX.1b奈秒clockid_t + timespec可指定時鐘源(CLOCK_MONOTONIC 避免系統時間調整影響)
select/pselectPOSIX.1微秒/奈秒timeval/timespec可同時監控 fd,常用於精確定時器

跨平台差異陷阱

  • Windows Sleep(ms):毫秒、windows.h、大寫 S。
  • Linux sleep(s):秒、unistd.h、小寫 s。
  • 移植建議:封裝 msleep(msec) 內部呼叫 nanosleep。

範例:可被信號中斷的精確休眠


#include 
#include 

void msleep(long msec) {
    struct timespec ts = { msec / 1000, (msec % 1000) * 1000000 };
    while (nanosleep(&ts, &ts) == -1 && errno == EINTR)
        continue;  // 信號中斷則繼續休眠剩餘時間
}

二、fflush 深度解析

C99 標準定義


int fflush(FILE *stream);
  • 輸出流/更新流且最近非輸入操作:將用戶空間緩衝區數據寫入內核(文件/終端/管道)。
  • 輸入流(stdin):行為未定義(C99 §7.19.5.2)。嚴禁 fflush(stdin)。
  • stream = NULL:刷新所有輸出流。
  • 返回值:成功 0,失敗 EOF(設定 errno)。

常見誤區

  1. fflush(stdin) 清除輸入緩衝:未定義行為,MSVC 擴展實現但不可移植。正確做法:int c; while ((c = getchar()) != '\n' && c != EOF);
  2. 認為 printf 立即顯示:標準輸出連終端預設行緩衝,遇 \n 才刷新;重導向檔案變全緩衝(4KB/8KB),需手動 fflush(stdout) 或 setvbuf(stdout, NULL, _IONBF, 0)。
  3. fclose 隱含 fflush:正常關閉無需額外呼叫,但 abort()/_exit() 會跳過,數據可能遺失。

三、標準 I/O 三種緩衝模式(APUE Ch.5 核心)

模式觸發刷新條件典型場景預設檔案
全緩衝緩衝區滿(通常 4KB/8KB)、fflush、fclose、程式正常結束磁碟檔案、管道、Socket(非終端)檔案、重導向的 stdout/stderr
行緩衝遇換行符 \n、緩衝區滿、fflush、讀取無緩衝/行緩衝流輸入連接終端的 stdout/stderr互動式終端的 stdout/stderr
無緩衝每次 I/O 直接系統調用(write/read)stderr、setvbuf(..., _IONBF, 0)stderr(標準錯誤)

ANSI C / glibc 預設規則

  • stdin、stdout:若 isatty(fileno(fp)) 真 → 行緩衝,否 → 全緩衝。
  • stderr:永遠無緩衝(確保錯誤訊息即時顯示)。
  • 可用 setvbuf(fp, buf, mode, size) 強制切換(必須在首次 I/O 前呼叫)。

緩衝區互動陷阱:讀取行緩衝輸入會刷新所有行緩衝輸出


printf("Prompt: ");  // 無 \n,留在行緩衝區
fflush(stdout);      // 必須手動刷新,否則使用者看不到提示
fgets(buf, sizeof(buf), stdin);  // 讀取 stdin(行緩衝)會自動刷新 stdout

四、實戰最佳實踐清單

  1. 互動式提示:printf("> "); fflush(stdout); 或 setvbuf(stdout, NULL, _IONBF, 0);(調試階段)。
  2. 日誌寫入:setvbuf(logfp, NULL, _IOLBF, 0); 行緩衝,確保每行即時落盤;關鍵審計日誌用 _IONBF。
  3. 高效能批次寫入:大塊 malloc 緩衝區 + setvbuf(fp, buf, _IOFBF, BUFSIZ*16); 減少系統調用。
  4. 多進程共享檔案:open(..., O_APPEND) + 無緩衝 write,避免用戶空間緩衝區導致資料交錯。
  5. 信號處理函數中:僅呼叫 async-signal-safe 函數(write 可、fprintf/fflush 不可)。

五、總結

  • sleep 秒級、nanosleep 奈秒級、clock_nanosleep 可選時鐘源,避免 usleep 舊介面。
  • fflush 僅用於輸出流/更新流,嚴禁 fflush(stdin)。
  • 理解「終端→行緩衝、檔案→全緩衝、stderr→無緩衝」預設規則,善用 setvbuf 掌控刷新時機。
  • APUE 第 5 章是系統程式設計師的必讀聖經,建議精讀並實作驗證。
Copyright © Windows教程網 All Rights Reserved