c語言退出程序命令exit「c語言exit函數用法」

使用C/C++語言在UNIX或者Linux系統下編程,應該都會遇到很多的進程退出相關的函數。有些是C標準庫提供的函數,有些是系統調用,有些又是某個系統所獨有的系統調用或者函數,並且命名上也極為類似,給人眼花繚亂的感覺。

這篇文章嘗試去總結下其中常見的那幾個系統調用和函數,並通過一個例子來展示下基本用法。

進程退出系列系統調用/函數

  • _exit

_exit(2) 屬於 POSIX 系統調用,適用於 UNIX 和 Linux 系統。調用該系統調用後會導致當前進程直接退出,且函數不會返回。內核會關閉該進程打開的文件描述符,若還存在子進程,則交由1號進程領養,再向進程的父進程發送 SIGCHLD 信號。

函數原型如下:

#include <unistd.h>
noreturn void _exit(int status);

參數列表

– `status`: 進程退出碼

返回值

無返回值

  • exit_group

exit_group(2) 是 Linux 系統所獨有的系統調用,調用後會使得進程的所有線程都退出。從 glibc 2.3 開始,_exit 實際上是對 exit_group 系統調用的包裝。因此,在Linux系統上兩者是等價的。

函數原型如下:

#include <linux/unistd.h>
void exit_group(int status);

參數列表

– `status`: 進程退出碼

返回值

無返回值

  • _Exit

_Exit(3) 是C標準庫函數,功能上等價於 _exit 系統調用,由 C99 引入。由於是標準庫提供的函數,在跨平台移植性上比 _exit 好,建議優先使用。

函數原型如下:

#include <stdlib.h>
void _Exit(int status);

參數列表

– `status`: 進程退出碼

返回值

無返回值

  • exit

exit(3) 是C標準庫函數,也是最常用的進程退出函數。它區別於 _exit、_Exit 的地方在於,除了使進程退出(也是通過調用 _exit 系統調用實現的)這個核心功能外,它還會執行一些前置動作

  1. 逐個執行用戶註冊的自定義清理函數(通過 atexit 或者 on_exit 函數註冊)
  2. 刷新標準I/O流緩衝區並關閉
  3. 刪除由標準庫函數 tmpfile 創建的臨時文件

函數原型如下:

#include <stdlib.h>
noreturn void exit(int status);

參數列表

– `status`: 進程退出碼

返回值

無返回值

  • atexit

atexit(3) 是C標準庫函數,用於註冊進程退出清理函數。該函數在使用時有以下幾個注意點:

  1. 清理函數的執行順序與註冊順序相反。
  2. 當進程收到致命信號時,註冊的清理函數不會被執行。
  3. 當進程調用 _exit(或者 _Exit)時,註冊的清理函數不會被執行。
  4. 當執行到某個清理函數時,若收到致命信號或者清理函數內調用了 _exit(或者 _Exit),那麼該清理函數不會返回並且後續的其它清理函數也會被丟棄。
  5. 當同一個清理函數被註冊多次,那麼正常情況下該清理函數也會被執行相應的次數。
  6. 父進程在調用 fork 前註冊了清理函數,那麼這些清理函數也會被子進程所繼承;若子進程後續又調用了 exec 系列函數,那麼子進程所繼承的清理函數則會被移除。
  7. 單個進程能夠註冊的清理函數的數量不會少於32個。

函數原型如下:

#include <stdlib.h>
int atexit(void (*function)(void));

參數列表

– `function`: 用戶自定義的進程退出清理函數。

返回值

成功返回0,非0值則表示失敗。

  • on_exit

功能上與 atexit 函數類似的,還有on_exit(3)函數。它是 Linux 系統下所獨有的函數,用於註冊進程退出清理函數,區別於 atexit 函數的是,它支持了額外的入參。

函數原型如下:

#include <stdlib.h>
int on_exit(void (*function)(int, void *), void *arg);

參數列表

– `function`: 用戶自定義的進程退出清理函數。

– `arg`: `void *`類型的自定義參數。

返回值

成功返回0,非0值則表示失敗。

示例

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

void cleanup1() {
    fprintf(stderr, "[1]cleanup\n");
    sleep(1);
}
void cleanup2() {
    fprintf(stderr, "[2]cleanup\n");
    sleep(1);
}
void cleanup3(int status, void *arg) {
    fprintf(stderr, "[3]cleanup: %s\n", (char *)arg);
    sleep(1);
}

int main(int argc, char *argv[]) {
    if (argc != 2) {
        fprintf(stderr, "Usage: %s exit|_exit|_Exit|return\n", argv[0]);
        return EXIT_FAILURE;
    }

    // atexit註冊自定義清理函數
    atexit(cleanup1);
    atexit(cleanup2);
    atexit(cleanup2); // 多次註冊同一個函數

    // 非標準函數on_exit,僅Linux下有效
    // on_exit(cleanup3, (void *)"bye!!!");
    // on_exit(cleanup3, (void *)"bye!!!"); // 多次註冊同一個函數

    fprintf(stdout, "a newline!\n"); // 向stdout寫入帶換行符的字元串(行緩衝,遇到換行符的情況下就會調用write系統調用輸出內容)
    fprintf(stderr, "[stderr]a newline!"); // 向stderr寫入不帶換行符的字元串(stderr默認情況下無緩衝,直接調用write系統調用)
    fprintf(stdout, "[stdout]forgot a newline!"); // 向stdout寫入不帶換行符的字元串(若不刷新緩衝區,則該行內容不會被輸出)

    if (strcmp("exit", argv[1]) == 0) {
        // 作用:執行一些前置的清理操作並終止當前進程
        // 標準庫函數(C89)
        // #include <stdlib.h>
        // 調用exit函數會執行以下操作:
        // 1、調用用戶註冊的清理函數
        // 2、刷新緩衝區並關閉所有標準IO流
        // 3、刪除臨時文件
        // 4、調用_exit系統調用
        exit(0);
    } else if (strcmp("_Exit", argv[1]) == 0) {
        // 作用:直接終止當前進程(含進程的所有線程)
        // 標準庫函數(C99)
        // #include <stdlib.h>
        // 效果等同於_exit,但移植性更好。
        _Exit(0);
    } else if (strcmp("_exit", argv[1]) == 0) {
        // 作用:直接終止當前進程(含進程的所有線程)
        // 是對exit_group系統調用的包裝(可退出所有線程)
        // #include <unistd.h>
        _exit(0);
    }
    return EXIT_SUCCESS; // main函數return會調用exit函數
}

原創文章,作者:投稿專員,如若轉載,請註明出處:https://www.506064.com/zh-tw/n/208752.html

(0)
打賞 微信掃一掃 微信掃一掃 支付寶掃一掃 支付寶掃一掃
投稿專員的頭像投稿專員
上一篇 2024-12-08 15:20
下一篇 2024-12-08 15:20

相關推薦

發表回復

登錄後才能評論