使用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 系統調用實現的)這個核心功能外,它還會執行一些前置動作:
- 逐個執行用戶注冊的自定義清理函數(通過 atexit 或者 on_exit 函數注冊)
- 刷新標準I/O流緩沖區并關閉
- 刪除由標準庫函數 tmpfile 創建的臨時文件
函數原型如下:
#include <stdlib.h>
noreturn void exit(int status);
參數列表
– `status`: 進程退出碼
返回值
無返回值
- atexit
atexit(3) 是C標準庫函數,用于注冊進程退出清理函數。該函數在使用時有以下幾個注意點:
- 清理函數的執行順序與注冊順序相反。
- 當進程收到致命信號時,注冊的清理函數不會被執行。
- 當進程調用 _exit(或者 _Exit)時,注冊的清理函數不會被執行。
- 當執行到某個清理函數時,若收到致命信號或者清理函數內調用了 _exit(或者 _Exit),那么該清理函數不會返回并且后續的其它清理函數也會被丟棄。
- 當同一個清理函數被注冊多次,那么正常情況下該清理函數也會被執行相應的次數。
- 父進程在調用 fork 前注冊了清理函數,那么這些清理函數也會被子進程所繼承;若子進程后續又調用了 exec 系列函數,那么子進程所繼承的清理函數則會被移除。
- 單個進程能夠注冊的清理函數的數量不會少于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函數
}
版權聲明:本文內容由互聯網用戶自發貢獻,該文觀點僅代表作者本人。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。如發現本站有涉嫌抄襲侵權/違法違規的內容, 請發送郵件至 舉報,一經查實,本站將立刻刪除。