Bài 44.126 phút đọc
Tín hiệu và sigaction
Sau bài này bạn sẽ làm được
- Kể các tín hiệu quan trọng và hành vi mặc định
- Giải thích vì sao SIGKILL và SIGSTOP không bắt được
- Viết bộ xử lý bằng sigaction với SA_RESTART
- Dùng đúng kiểu volatile sig_atomic_t
Tín hiệu là cách hệ điều hành gõ vào vai chương trình của bạn: Ctrl+C, một tiến trình khác gửi kill, hay một lỗi bộ nhớ. Chúng đến bất kỳ lúc nào, và xử lý chúng đúng là một trong những phần tinh tế nhất của lập trình hệ thống.
#Tín hiệu là gì
Tín hiệu (signal)
Một ngắt mềm không đồng bộ do nhân gửi tới tiến trình. Không đồng bộ nghĩa là nó đến bất kỳ lúc nào, kể cả giữa chừng một hàm. Khi tín hiệu đến, chương trình tạm dừng việc đang làm, chạy bộ xử lý, rồi tiếp tục.
#Các tín hiệu quan trọng
| Tín hiệu | Số | Nguyên nhân | Mặc định | Bắt được |
|---|---|---|---|---|
| SIGINT | 2 | Ctrl+C | Kết thúc | Có |
| SIGQUIT | 3 | Ctrl+Bslash | Kết thúc + core | Có |
| SIGKILL | 9 | kill -9 | Kết thúc | KHÔNG |
| SIGSEGV | 11 | Truy cập bộ nhớ sai | Kết thúc + core | Có |
| SIGTERM | 15 | kill mặc định | Kết thúc | Có |
| SIGSTOP | 19 | Tạm dừng | Dừng | KHÔNG |
| SIGCHLD | 17 | Con kết thúc | Bỏ qua | Có |
| SIGPIPE | 13 | Ghi ống đã đóng | Kết thúc | Có |
| SIGUSR1 / SIGUSR2 | 10 / 12 | Người dùng định nghĩa | Kết thúc | Có |
#signal, đơn giản nhưng có vấn đề
sig.c, bắt SIGINT bằng signal()
#include <stdio.h>
#include <signal.h>
static volatile sig_atomic_t nen_thoat = 0;
static void xu_ly(int s) {
(void)s;
nen_thoat = 1; /* CHI dat co, khong lam gi khac */
}
int main(void) {
signal(SIGINT, xu_ly); /* SIGINT (Ctrl+C) -> goi xu_ly */
int vong = 0;
while (!nen_thoat) {
printf(" vong %d\n", ++vong);
if (vong == 3) raise(SIGINT); /* mo phong Ctrl+C */
}
printf("da bat tin hieu, don dep va thoat sach\n");
return 0;
}terminal
gcc -O2 -Wall -Wextra sig.c -o sig.exe && ./sig.exe
dang chay, se tu gui SIGINT sau 3 vong vong 1 vong 2 vong 3 da bat tin hieu, don dep va thoat sach
#sigaction, cách đúng
sigaction, kiểm soát đầy đủ
#include <signal.h>
#include <string.h>
static volatile sig_atomic_t nen_thoat = 0;
static void xu_ly(int s) { (void)s; nen_thoat = 1; }
int main(void) {
struct sigaction sa;
memset(&sa, 0, sizeof sa);
sa.sa_handler = xu_ly;
sigemptyset(&sa.sa_mask); /* khong chan tin hieu nao khac trong handler */
sa.sa_flags = SA_RESTART; /* tu khoi dong lai syscall bi ngat */
if (sigaction(SIGINT, &sa, NULL) < 0) { perror("sigaction"); return 1; }
if (sigaction(SIGTERM, &sa, NULL) < 0) { perror("sigaction"); return 1; }
while (!nen_thoat) { lam_viec(); }
don_dep();
return 0;
}signal() cho mã nghiêm túc
signal(SIGINT, xu_ly);
/* Hanh vi tuy he. Khong co SA_RESTART, nen syscall bi ngat tra
EINTR va ban phai xu ly bang tay khap noi. */
sigaction()
struct sigaction sa = {0};
sa.sa_handler = xu_ly;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
sigaction(SIGINT, &sa, NULL);
/* Nhat quan, co SA_RESTART, kiem soat day du. */
#Kiểu volatile sig_atomic_t
terminal
./sig.exe
vong 1 vong 2 vong 3 da bat tin hieu, don dep va thoat sach
Tự làm thử
- Bắt
SIGINTbằngsignalvà in một thông điệp trước khi thoát. - Viết lại bằng
sigactionvớiSA_RESTARTvà bắt cảSIGTERM. - Thử gửi
kill -9cho chương trình và xác nhận nó không bắt được. - Bỏ
volatilekhỏi biến cờ, dịch với-O2, và quan sát vòng lặp không thoát. - Bắt
SIGUSR1vàSIGUSR2để hai tín hiệu làm hai việc khác nhau. - Đặt
sa_maskchặnSIGTERMtrong khi xử lýSIGINTvà quan sát hiệu ứng.
Trình chấm điểm tự động sẽ được bổ sung ở giai đoạn sau. Hiện tại bạn tự chạy thử trên máy.
Tóm tắt
- Tín hiệu là ngắt mềm không đồng bộ, đến bất kỳ lúc nào kể cả giữa chừng một hàm.
SIGKILLvàSIGSTOPkhông bắt được; đó là cơ chế cuối cùng của hệ điều hành.signal()đơn giản nhưng hành vi khác nhau giữa các hệ; luôn dùngsigactioncho mã nghiêm túc.sigactioncósa_handler,sa_mask,sa_flagsvớiSA_RESTART, và hành vi nhất quán.- Biến chia sẻ với handler phải là
volatile sig_atomic_t; handler đặt cờ, main xử lý.