在多任務/中斷環境中,可重入性 是函數能否被安全並發調用的核心屬性。本文從定義、反例、修復方案三個維度建立完整認知體系。
可重入函數 可被多個任務/中斷同時調用,不導致數據損壞。特徵:
- 僅使用局部變量(棧/寄存器,天然隔離)
- 或使用全局變量但有完備保護機制(互斥鎖、關中斷、原子操作)
- 不調用非可重入函數
- 不操作共享硬體資源(或已互斥)
void strcpy(char *dest, char *src) {
while (*dest++ = *src++) { ; }
*dest = '\0';
}
參數通過棧傳遞,每次調用擁有獨立副本,循環僅操作參數指向的內存 → 天然可重入。
int Temp; // 全局變量!
void swap(int *x, int *y) {
Temp = *x;
*x = *y;
*y = Temp;
}
Temp = *x(Temp=1)Temp = *x(Temp=3),完成交換*y = Temp → 用 Task_H 的 Temp=3 覆蓋 b!
後果: Task_L 數據損壞,且極難復現(需特定中斷時機)。
| 方案 | 代碼修改 | 優點 | 缺點/適用場景 |
|---|---|---|---|
| 局部變量化(首選) | void swap(int *x, int *y) { int Temp = *x; ... } | 零開銷、天然線程安全、無副作用 | 遞歸/大對象棧溢出風險 |
| 關中斷保護 | void swap(...) { INT_OFF; ... INT_ON; } | 不改函數簽名、兼容舊代碼 | 增加中斷延遲、禁用於長操作、多核無效 |
| 信號量/互斥鎖 | SemPend(mutex); ... SemPost(mutex); | 任務級互斥、支援多核、可超時 | 上下文切換開銷、死鎖風險、不可在 ISR 用 |
strtok、asctime、localtime、rand、getenv、tmpnamstrtok_r、localtime_r、rand_r、getenv_r(POSIX)static / 全局變量?malloc/free 且分配器非線程安全?若以上均為「否」 → 可重入。否則需加鎖/改局部/用重入版。
可重入性是嵌入式/系統級開發的「免疫系統」。優先將狀態局部化(棧上),實在需要共享狀態時,用最小粒度鎖保護。代碼評審時,對每個非靜態函數問一句:「這能被中斷/高優任務打斷重入嗎?」能攔截 90% 的並發 Bug。