본문 바로가기

Core BSP 분석/리눅스 커널 핵심 분석

[Linux][Kernel] preempt_disable()/preempt_enable() 주의 사항

preempt_disable() 함수를 쓸 때 주의해야 할 점이 있습니다.
preempt_disable() 함수는 preempt_enable() 함수와 반드시 Pair로 써야 합니다.
 
preempt_disable() 함수를 호출한 다음 스케줄링 동작을 수행하는 함수를 쓰면 리눅스 시스템은 오동작합니다.
 
예를 들어 다음과 같은 드라이버를 초기화하는 코드를 예를 들겠습니다.
1 void configure_something_driver(void) 
2 {
3 preempt_disable();
4
5 do_something();
6 mdelay(100);
7 do_something();
8
9 preempt_enable();
10 }
 
5~7 번 코드가 실행할 때 Preemption이 되면 안된다고 판단했습니다.
그래서 3 번째와 9 번째 줄에 preempt_disable() 함수와 preempt_enable() 함수를 추가해서
5~7 번째 코드 구간에서 Preemption으로부터 보호한 것입니다.
 
위 코드를 실행하면 커널 패닉이 발생하거나 이상한 커널 로그를 출력합니다.
그 이유는 무엇일까요?
 
6 번째 줄 mdelay() 함수를 호출하면 schedule() 함수를 호출하기 때문입니다.
 
리눅스 커널 입장에서 다음 함수와 같이 해석할 수 있다는 것입니다.
1 void configure_something_driver(void) 
2 {
3 preempt_disable();
4
5 do_something();
6 schedule();
7 do_something();
8
9 preempt_enable();
10 }
 
위 코드는 다음과 같이 수정하면 문제가 발생하지 않을 것입니다. 
1 void configure_something_driver(void) 
2 {
3 preempt_disable();
4 do_something();
5 preempt_enable();
6
7 mdelay(100);
...
8 }
 
리눅스 커널 메일링 리스트에도 유사한 내용을 확인할 수 있습니다.
Sleeping in the kernel with preemption disabled is considered to be a
bug.  So the scheduler will print an error and a stack dump when this
happens.
 
In contrast, it is OK to do the following:
 
preempt_disable();
do_something();
preempt_enable();
schedule();
preempt_disable();
do_something_else();
preempt_enable();
 
preempt_disable() 함수를 호출해서 Preemption을 보류시키고 휴면에 들어가면 안된다는 내용입니다.
 
 
 
#Reference 시스템 콜
 
 
Reference(워크큐)
워크큐(Workqueue) Overview