用 C 語言處理 SIGINT 訊號
本文將演示有關如何處理 C 語言中 SIGINT
訊號的多種方法。
使用 signal
函式註冊 SIGINT
訊號處理程式
SIGINT 是與終端中斷字元(通常為Ctrl+C)相關的預定義訊號之一。它使殼程式停止當前程序並返回其主迴圈,向使用者顯示一個新的命令提示符。注意,訊號只是核心內部的程序和使用者空間之間傳送的小通知。它們有時被稱為軟體中斷,因為它們通常會停止程式的正常執行並針對給定的訊號型別執行特殊操作。這些動作大多數情況下被定義為系統中的預設動作,但是使用者可以實現特殊功能並將其註冊為訊號的新動作。
請注意,某些訊號具有從作業系統分配的嚴格固定的行為,因此不能被覆蓋,因為核心使用它們來執行一些重要的事情,例如終止無響應的程序。
但是,SIGINT
是可以處理的一種訊號,這意味著使用者可以註冊一個自定義函式,以便在程序接收到該訊號時執行該函式。SIGINT
訊號的預設動作是使程序終止。在以下示例程式碼中,我們實現了一個程式,該程式執行一個無限的 while
迴圈,並在其中不斷呼叫 fprintf
函式。儘管在迴圈開始之前,我們呼叫 signal
函式將 SIGINT
,sigintHandler
函式註冊為處理程式,該處理程式只有一個函式呼叫,並在 stdout
中列印了一個字串。
請注意,使用 write
而不是 printf
,因為訊號處理程式程式碼不得呼叫在模組內部修改全域性程式資料的非可重入函式。為了演示該示例,你應該執行該程式,然後從另一個終端傳送 SIGINT
訊號以觀察其行為。通常,它應該停止執行 while
迴圈,列印"Caught SIGINT!"
字串,並以成功狀態程式碼退出。
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define errExit(msg) \
do { \
perror(msg); \
exit(EXIT_FAILURE); \
} while (0)
static void sigintHandler(int sig) {
write(STDERR_FILENO, "Caught SIGINT!\n", 15);
}
int main(int argc, char *argv[]) {
if (signal(SIGINT, sigintHandler) == SIG_ERR) errExit("signal SIGINT");
while (1) {
fprintf(stderr, "%d", 0);
sleep(3);
}
exit(EXIT_SUCCESS);
}
使用 sigaction
功能註冊 SIGINT
訊號處理程式例程
即使 UNIX 系統中 signal
函式呼叫的現代實現對於簡單的用例也可以可靠地工作,但還是建議使用 sigaction
函式來註冊訊號處理程式。與 signal
呼叫相比,它提供了更多的選擇,但它也提供了對於訊號的任何嚴重使用情況都必需的核心功能。sigaction
帶有特殊的 struct sigaction
引數,以指定處理程式函式指標和其他指示符。在這種情況下,我們實現了一個方案,其中子程序執行帶有全域性變數 shutdown_flag
作為條件表示式的 while
迴圈,而父程序等待它。請注意,shutdown_flag
變數的型別為 sig_atomic_t
,這是一個特殊的整數,可以從訊號處理程式程式碼中安全地對其進行全域性修改。因此,一旦使用者向子程序傳送 SIGINT
訊號,則將呼叫 cleanupRoutine
函式,該函式會將 shutdown_flag
設定為 0
值,並且控制元件返回到 while
迴圈,在該迴圈中再次評估條件表示式,零迫使它從迴圈中中斷。當 waitpid
函式返回時,子程序退出,父程序獲得其狀態。
#define _POSIX_C_SOURCE 199309
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
#define errExit(msg) \
do { \
perror(msg); \
exit(EXIT_FAILURE); \
} while (0)
volatile sig_atomic_t shutdown_flag = 1;
void cleanupRoutine(int signal_number) { shutdown_flag = 0; }
int main(void) {
int wstatus;
pid_t c_pid = fork();
if (c_pid == -1) errExit("fork");
if (c_pid == 0) {
printf("printed from child process - %d\n", getpid());
int count = 0;
struct sigaction sigterm_action;
memset(&sigterm_action, 0, sizeof(sigterm_action));
sigterm_action.sa_handler = &cleanupRoutine;
sigterm_action.sa_flags = 0;
// Mask other signals from interrupting SIGINT handler
if (sigfillset(&sigterm_action.sa_mask) != 0) errExit("sigfillset");
// Register SIGINT handler
if (sigaction(SIGINT, &sigterm_action, NULL) != 0) errExit("sigaction");
while (shutdown_flag) {
getpid();
}
printf("pid: %d exited\n", getpid());
exit(EXIT_SUCCESS);
} else {
printf("printed from parent process - %d\n", getpid());
int ret;
if (waitpid(c_pid, &wstatus, WUNTRACED) == -1) errExit("waitpid");
}
exit(EXIT_SUCCESS);
}