- 커널 디버깅에는 세 가지 요소가 필요하다.
- 버그가 처음 등장한 커널 버전을 파악할 수 있는가?
- 버그를 재현할 수 있는가?
- 커널 코드에 관한 지식을 갖추고 있는가?
- 버그를 명확하게 정의하고 안정적으로 재현할 수 있다면 성공적인 디버깅에 절반 이상 달성한 것이다.
-
커널 출력 함수인
printk()
는 C 라이브러리의 printf() 함수와 거의 동일하다.- 주요 차이점은 로그수준(Loglevel)을 지정할 수 있다는 점이다.
- 가장 낮은 수준인
KERN_DEBUG
부터 가장 높은 수준인KERN_EMERG
까지 7단계로 설정할 수 있다.
-
printk()
의 장점은 커널의 어느 곳에서도 언제든지 호출할 수 있다는 점이다.- 인터럽트 컨텍스트, 프로세스 컨텍스트 모두 호출 가능하다.
- 락을 소유하든 소유하지 않든 호출 가능하다.
- 어느 프로세서에서도 사용할 수 있다.
-
printk()
의 단점은 커널 부팅 과정에서 콘솔이 초기화되기 전에는 사용할 수 없다는 점이다.- 이식성을 포기하고
printk()
함수 변종인early_printk()
함수를 사용하는 해결책이 있다.
- 이식성을 포기하고
-
printk()
를 사용할 때 주의할 점은, 너무 빈번하게 호출되는 커널 함수에 사용하면 시스템이 뻗어버린다는 것이다.- 출력 폭주를 막기 위해 두 가지 방법을 사용할 수 있다.
- 첫 번째 방법: jiffies를 이용해서 수초마다 한 번씩만 출력한다.
- 두 번째 방법:
printk_ratelimit()
함수를 이용해서 n초에 한 번씩만 출력한다.
-
커널 메시지는 크기가
LOG_BUF_LEN
인 원형 큐(버퍼)에 저장된다.- 크기는
CONFIG_LOG_BUF_SHIFT
옵션을 통해 설정할 수 있으며 기본값은 16KB다. - 원형 큐이므로 가득찼을 때 가장 오래된 메시지를 덮어쓴다.
- 표준 리눅스 시스템은 사용자 공간의 ‘klogd’ 데몬이 로그 버퍼에서 커널 메시지를 꺼내고 ‘syslogd’ 데몬을 거쳐서 시스템 로그 파일(기본:
/var/log/messages
)에 기록한다.
- 크기는
-
웁스(oops)는 커널이 사용자에게 무언가 나쁜 일이 일어났다는 것을 알려주는 방법이다.
- 커널은 심각한 오류가 났을 때 사용자 프로세스처럼 책임감 없이 프로세스를 종료할 수 없다.
- 커널은 웁스가 발생했을 때 최대한 실행을 계속 하려고 시도한다.
- 더 이상 커널 실행이 불가능하다고 판단할 경우 ‘패닉 상태’가 된다.
- 커널은 콘솔 오류 메시지 + 레지스터 내용물 + 콜스택 역추적 정보 등을 출력한다.
- 커널은 심각한 오류가 났을 때 사용자 프로세스처럼 책임감 없이 프로세스를 종료할 수 없다.
BUG()
,BUG_ON()
: 웁스를 발생한다.- 의도적으로 웁스를 발생시키는 시스템콜이다.
- 이 함수는 발생해서는 안 되는 상황에 대한 조건문을 확인할 때 사용한다.
if (...) BUG()
랑BUG_ON(...)
는 동일한 구문이다. 그래서 대부분 커널 개발자는BUG_ON()
을 사용하는 것을 더 좋아하며 조건문에unlikely()
를 함께 사용(BUG_ON(unlikely());
)한다.
panic()
: 패닉을 발생한다.- 좀 더 치명적인 오류의 경우에는 웁스가 아닌 패닉을 발생시킨다.
- 이 함수를 호출하면 오류 메시지를 출력하고 커널을 중지시킨다.
dump_stack()
: 디버깅을 위한 스택 역추적 정보를 출력한다.
참고