首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

深入解析 atexit():註冊函數到底在 main 之後還是 exit() 時執行?

     編輯:關於C語言

man 手冊定義

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

glibc exit() 內部流程


void exit(int status) {
    __run_exit_handlers(status, &__exit_funcs, true); // 1. 執行 atexit/on_exit 函數
    _exit(status);                                    // 2. 系統呼叫:終止進程、清理資源
}

__run_exit_handlers 遍歷 __exit_funcs 鏈表,逆序呼叫每個註冊函數。只有全部完成後才呼叫 _exit()。

關鍵結論

  1. atexit 函數在 exit() 內部最先執行,早於執行緒終止、檔案關閉、記憶體釋放。
  2. main() 返回相當於 exit(0),同樣觸發 atexit。
  3. 執行緒安全:atexit 處理函數中不可呼叫非 async-signal-safe 函數(如 malloc、printf、鎖操作),否則可能死結或損壞狀態。
  4. 正確關閉模式:主執行緒應先通知工作執行緒退出(如原子旗標 + 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 _exit
  • POSIX pthread_atfork、pthread_cleanup_push
  • C++ std::atexit、std::quick_exit、std::at_quick_exit
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved