Linux 커널에서 SysRq(System Request) 기능은 시스템이 비정상적인 상태에 빠졌을 때, 특정한 명령을 통해 커널의 동작을 조작하거나 시스템을 복구할 수 있도록 돕는 강력한 진단 도구이다. SysRq 키를 이용하면 커널 디버깅, 로그 출력, 프로세스 종료, 재부팅 등 다양한 작업을 수행할 수 있지만, 이 기능은 **특수한 실행 컨텍스트(context)**에서 동작하므로 사용 시 몇 가지 중요한 제약 조건이 따른다. 이 글에서는 SysRq의 동작 원리, 그 실행 컨텍스트에서의 제약 조건, 예제 코드 그리고 안정적인 사용을 위한 고려사항에 대해 알아본다.
SysRq란?
SysRq는 키보드의 특별한 키(보통 Alt + PrintScreen
)와 함께 조합하여 커널 내부에 정의된 특정 작업을 직접 실행할 수 있는 기능이다. 커널이 응답하지 않거나 디버깅이 필요한 상황에서 사용할 수 있는 백도어(backdoor) 같은 역할을 한다.
사용 예
Alt + SysRq + t
: 현재 커널의 태스크 목록 출력Alt + SysRq + m
: 메모리 정보 출력Alt + SysRq + c
: 커널 크래시 (강제 panic)
SysRq 작동 흐름
SysRq 키 입력 → IRQ 핸들러 호출 → 키 값 해석 → SysRq 핸들러 호출
+-------------+
| Key Pressed |
+-------------+
|
v
+----------------+
| Interrupt (IRQ)|
+----------------+
|
v
+---------------------+
| Keyboard Driver |
| → sysrq_handle() |
+---------------------+
|
v
+---------------------+
| SysRq Command Logic |
+---------------------+
SysRq 키 입력은 키보드 인터럽트 핸들러에서 처리되며, 곧바로 커널 내부의 handle_sysrq()
함수로 연결된다.
SysRq Context란?
SysRq 핸들러는 일반적인 커널 코드와는 다른 실행 컨텍스트에서 동작한다. 이는 대개 하드웨어 인터럽트 컨텍스트 또는 non-preemptible한 상태에서 실행되며, 매우 제한된 환경에서 실행된다.
예: 인터럽트 컨텍스트
irq_handler() {
...
if (is_sysrq_key(scancode)) {
handle_sysrq(scancode);
}
...
}
SysRq Context의 제약 조건
SysRq는 제한된 컨텍스트에서 실행되기 때문에 다음과 같은 제약 조건을 반드시 지켜야 한다.
1. 슬립(sleep) 금지
SysRq는 인터럽트 컨텍스트에서 실행되기 때문에 sleep
, schedule()
, msleep()
등의 함수를 절대 호출해서는 안 된다.
잘못된 코드 예
void sysrq_custom_handler(int key) {
msleep(100); // 절대 금지
}
2. 락(lock) 사용 제한
- Spinlock은 사용 가능하지만 blocking이 필요한 mutex는 사용 불가
- 특히
mutex_lock()
이나down()
등의 함수는 호출 시 커널 패닉으로 이어질 수 있다.
허용되는 예
spin_lock(&lock);
... // 작업 수행
spin_unlock(&lock);
금지되는 예
mutex_lock(&my_mutex); // 금지
3. 메모리 할당 제약
GFP_KERNEL
로 메모리 할당 시 슬립 가능성이 있으므로 사용 금지- 대신
GFP_ATOMIC
을 사용해야 안전
void *ptr = kmalloc(1024, GFP_ATOMIC); // OK
4. 프린트 및 로깅 주의
printk()
사용은 가능하지만, 대량 출력은 콘솔 락이나 버퍼 문제로 시스템 hang 가능성이 있다.
- 가능:
printk(KERN_INFO "SysRq triggered\n");
- 위험: 10만 라인 이상의 연속 로그 출력
5. 재진입 가능성 고려
SysRq 핸들러는 인터럽트 중에 또 다른 인터럽트로 호출될 수 있으므로 **re-entrant-safe(재진입 가능)**하게 코드를 작성해야 한다.
SysRq 핸들러 구현 예제
커스텀 SysRq 키 핸들러 등록
#include <linux/sysrq.h>
#include <linux/spinlock.h>
static void my_sysrq_handler(int key)
{
printk(KERN_INFO "Custom SysRq handler: key = %d\n", key);
}
static struct sysrq_key_op my_sysrq_op = {
.handler = my_sysrq_handler,
.help_msg = "x - Custom handler",
.action_msg = "Custom SysRq action triggered",
.enable_mask = SYSRQ_ENABLE_ALWAYS,
};
static int __init my_sysrq_init(void)
{
register_sysrq_key('x', &my_sysrq_op);
return 0;
}
static void __exit my_sysrq_exit(void)
{
unregister_sysrq_key('x', &my_sysrq_op);
}
module_init(my_sysrq_init);
module_exit(my_sysrq_exit);
MODULE_LICENSE("GPL");
빌드 및 실행
make
sudo insmod my_sysrq.ko
사용
Alt + SysRq + x
SysRq 커널 설정
SysRq 기능을 사용하기 위해서는 커널 설정에서 해당 옵션이 활성화되어야 한다.
1. 커널 설정 확인
zcat /proc/config.gz | grep CONFIG_MAGIC_SYSRQ
CONFIG_MAGIC_SYSRQ=y
이면 SysRq 기능 활성화됨.
2. 런타임에서 SysRq 제어
# 전체 기능 활성화
echo 1 > /proc/sys/kernel/sysrq
# 특정 기능만 허용 (예: 16 = crash dump)
echo 16 > /proc/sys/kernel/sysrq
SysRq 기능 테이블
키 | 기능 | 설명 |
---|---|---|
b | Immediate reboot | 즉시 재부팅 |
o | Power-off | 시스템 전원 종료 |
t | Show task info | 태스크 정보 출력 |
m | Show memory info | 메모리 정보 출력 |
c | Trigger crash (panic) | 강제 커널 패닉 |
x | 사용자 정의 (위 예제 참조) | 커스텀 SysRq 핸들러 실행 |
이해를 돕기 위한 그림
┌───────────────┐
│ 키보드 입력 │
└────┬──────────┘
↓
┌───────────────────┐
│ 키보드 IRQ 핸들러 │
└────┬──────────────┘
↓
┌─────────────┐
│ sysrq_handle│
└────┬────────┘
↓
┌──────────────────────┐
│ 등록된 sysrq_key_op │
└──────────────────────┘
문제 상황 예시
커널 패닉 발생
원인
SysRq 핸들러에서 msleep()
또는 mutex_lock()
을 호출
해결
- 슬립이나 블로킹 함수 사용 제거
GFP_ATOMIC
,spin_lock()
등 Non-blocking 자원만 사용
결론
SysRq는 리눅스 커널 내부에서 강력한 진단 및 디버깅 도구로 작용하지만, 그 실행 컨텍스트는 일반적인 사용자 공간이나 커널 스레드와 다르다. 따라서 슬립, 블로킹, 과도한 로그 출력 등은 모두 SysRq context에서는 금지되며, 이를 위반할 경우 커널 패닉이나 시스템 정지가 발생할 수도 있다.
안전하게 SysRq 핸들러를 구현하려면 인터럽트 컨텍스트에 맞춘 프로그래밍이 필요하며, spinlock
, GFP_ATOMIC
, printk()
등의 도구를 올바르게 사용하는 것이 중요하다.