Sự kiện Ctrl+C trên Linux và Windows
Học để hiểu bản chất chứ không chỉ chạy được code.
Trong hệ thống máy tính, khi có sự kiện xảy ra phần cứng có thể phát sinh một ngắt gửi đến CPU.
Ở tầng kernel, khi có interrupt xảy ra, CPU sẽ tạm dừng luồng đang chạy để chuyển sang xử lý sự kiện ngắt. Sau khi hoàn tất, CPU quay lại tiếp tục công việc trước đó.
Tuy nhiên, ở tầng ứng dụng (user-space), một số sự kiện như tổ hợp phím Ctrl+C được hệ điều hành chuyển thành các thông báo (signal hoặc event), và cách chuyển cũng như xử lý các thông báo này khác nhau giữa Linux và Windows.
Ctrl+C trên Linux
Trên Linux, khi người dùng nhấn Ctrl+C
Kernel nhận ngắt từ bàn phím
Kernel chuyển sự kiện này thành signal SIGINT
Signal handler được thực thi trong chính context của thread nhận signal
Vì handler chạy trên luồng đang thực thi, nên trong thời gian xử lý signal, luồng chính bị tạm dừng.
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
// Hàm xử lý signal SIGINT (Ctrl+C) ở user-space
void signint_handle(int sig) {
printf("Đã nhận tín hiệu Ctrl+C %d\n", sig);
// Nghỉ 5 giây
sleep(5);
printf("Đang quay lại công việc ...\n");
}
int main() {
// Đăng kí handler xử lý signal SIGINT
signal(SIGINT, signint_handle);
printf("Chương trình đang chạy, nhấn Ctrl+C để gửi signal SIGINT ...\n");
while (1)
{
printf("Đang làm việc ...\n");
// Nghỉ 1 giây
sleep(1);
}
return 0;
}
Kết quả, luồng chính bị block cho đến khi hàm xử lý SIGINT hoàn thành.

Ctrl+C trên Windows
Trên Windows, Ctrl+C không được chuyển thành signal như Linux.
Thay vào đó, Console Subsystem của Windows:
Bắt sự kiện CTRL_C_EVENT
Tạo một luồng nội bộ riêng gọi hàm đã đăng ký bằng SetConsoleCtrlHandler()
Do handler chạy trên luồng riêng, chương trình chính vẫn tiếp tục chạy song song.
#include <stdio.h>
#include <Windows.h>
// Hàm xử lý sự kiện Console Ctrl+C (CTRL_C_EVENT) trên thread riêng
BOOL WINAPI ConsoleHandler(DWORD dwControlType) {
switch (dwControlType) {
// Tổ hợp phím Ctrl+C được nhấn
case CTRL_C_EVENT:
printf("Đã nhận tín hiệu Ctrl+C\n");
// Tạm dừng 5 giây
Sleep(5000);
printf("Đang quay lại công việc ...\n");
return TRUE;
default:
return FALSE;
}
}
int main() {
// Ép Console sử dụng bảng mã UTF-8 (Code Page 65001) để Hiển thị tiếng việt
SetConsoleOutputCP(65001);
// Đăng kí handler xử lý sự kiện Ctrl+C của Console (user-space)
if(SetConsoleCtrlHandler(ConsoleHandler, TRUE)) {
printf("Chương trình đang chạy, nhấn Ctrl+C để gửi CTRL_C_EVENT ...\n");
while (1)
{
printf("Đang làm việc ...\n");
// Nghỉ 1 giây
Sleep(1000);
}
}
return 0;
}
Kết quả, luồng xử lý sự kiện Ctrl+C chạy song song với luồng chính

The End.