atexit()註冊的函數會在正常程序終止時被呼叫,無論是透過exit()還是main()返回。呼叫順序與註冊順序相反(後進先出)。不傳遞參數。
許多開發者以為 atexit 函數在 main() 返回之後、所有執行緒已結束時才執行。實際上:
exit() 先執行 atexit 註冊函數,然後 呼叫 _exit() 進入核心清理(關閉檔案描述符、釋放記憶體、終止執行緒)。main() 中建立執行緒後直接 return 或 exit(),atexit 函數執行期間,其他執行緒仍在運行!
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
void bye(void) {
printf("before bye\n");
sleep(2);
printf("after bye\n");
}
void* thread_func(void* arg) {
while (1) {
printf("--------------I'm thread!\n");
sleep(1);
}
return NULL;
}
int main() {
atexit(bye);
pthread_t tid;
pthread_create(&tid, NULL, thread_func, NULL);
sleep(3); // 讓執行緒跑幾次
exit(0); // 或 return 0
}
--------------I'm thread! --------------I'm thread! --------------I'm thread! before bye --------------I'm thread! ← atexit 執行期間執行緒仍在跑! --------------I'm thread! after bye
void exit(int status) {
__run_exit_handlers(status, &__exit_funcs, true); // 1. 執行 atexit/on_exit 函數
_exit(status); // 2. 系統呼叫:終止進程、清理資源
}
__run_exit_handlers 遍歷 __exit_funcs 鏈表,逆序呼叫每個註冊函數。只有全部完成後才呼叫 _exit()。
malloc、printf、鎖操作),否則可能死結或損壞狀態。pthread_join),最後 呼叫 exit()。
// 正確關閉範例
volatile sig_atomic_t shutdown = 0;
void* worker(void* arg) {
while (!shutdown) { /* 工作 */ }
return NULL;
}
void cleanup(void) {
// 僅做 async-signal-safe 操作:寫入管道、設定旗標
// 避免 printf、malloc、pthread_mutex_lock
}
int main() {
atexit(cleanup);
pthread_t tid;
pthread_create(&tid, NULL, worker, NULL);
// 等待關閉信號
getchar();
shutdown = 1;
pthread_join(tid, NULL); // 確保執行緒已退出
return 0; // 觸發 atexit(cleanup)
}
man 3 atexit、man 3 exit、man 3 _exitpthread_atfork、pthread_cleanup_pushstd::atexit、std::quick_exit、std::at_quick_exit