Linux Kernel(4.19) Hacks

rousalome.egloos.com

포토로그 Kernel Crash


통계 위젯 (화이트)

90258
1323
114592


[리눅스커널] 스케줄링: TASK_INTERRUPTIBLE 상태로 바뀔 때 호출되는 함수 - p 10. Process Scheduling

보통 프로세스가 휴면에 진입할 때 커널은 프로세스 상태를 TASK_INTERRUPTIBLE 로 바꿔 줍니다. 그렇다면 커널 어느 코드에서 프로세스 상태를 TASK_INTERRUPTIBLE 상태로 바꿀까요?

프로세스 상태를 TASK_INTERRUPTIBLE 상태로 바꾸는 다양한 커널이나 드라이버 코드를 볼 수 있습니다. 그 중 대표적인 함수를 요약하면 다음과 같습니다.
 
[그림 10.18] 프로세스 상태가 TASK_INTERRUPTIBLE로 바뀔 때 호출하는 함수

wait_event_interruptible
wait_event_interruptible() 함수를 호출하면 휴면상태(TASK_INTERRUTIBLE)로 프로세스 상태를 바꿉니다. wait_event_interruptible() 함수를 호출하면 웨이트 큐에서 웨이트 큐 이벤트가 실행될 때까지 기다립니다.

이제 커널이 wait_event_interruptible() 함수를 호출하면 프로세스 상태를 어떻게 바꾸는지 살펴보겠습니다.
[https://elixir.bootlin.com/linux/v4.19.30/source/include/linux/wait.h]
1 #define wait_event_interruptible(wq_head, condition) \
2 ({ \
3 int __ret = 0; \
4 might_sleep(); \
5 if (!(condition)) \
6 __ret = __wait_event_interruptible(wq_head, condition); \
7 __ret; \
8 })
9
10 #define __wait_event_interruptible(wq_head, condition) \
11 ___wait_event(wq_head, condition, TASK_INTERRUPTIBLE, 0, 0, \
12       schedule())

wait_event_interruptible() 함수는 매크로 포멧으로 구성돼 있습니다.
wait_event_interruptible() 함수를 호출하면 3~7번째 줄 코드로 치환되는데 __wait_event_interruptible() 호출합니다.

이어서 __wait_event_interruptible() 함수 구현부를 보면 11 번째 줄과 같이 ___wait_event() 함수를 호출합니다.

다음으로 ___wait_event() 함수 구현부를 보겠습니다.
[https://elixir.bootlin.com/linux/v4.19.30/source/include/linux/wait.h]
1 #define ___wait_event(wq_head, condition, state, exclusive, ret, cmd) \
2 ({ \
3 __label__ __out; \
4 struct wait_queue_entry __wq_entry; \
5 long __ret = ret; /* explicit shadow */ \
6 \
7 init_wait_entry(&__wq_entry, exclusive ? WQ_FLAG_EXCLUSIVE : 0); \
8 for (;;) { \
9 long __int = prepare_to_wait_event(&wq_head, &__wq_entry, state);\
... \
10 } \
11 finish_wait(&wq_head, &__wq_entry); \
12 __out: __ret; \
13 })

7 번째 줄에서 웨이트 큐를 초기화한 다음 9 번째 줄 코드에서 prepare_to_wait_event() 함수를 호출해서 웨이트 큐 전처리 과정을 수행합니다.

prepare_to_wait_event() 함수 코드를 볼 차례입니다.
[https://elixir.bootlin.com/linux/v4.19.30/source/kernel/sched/wait.c]
1 long prepare_to_wait_event(struct wait_queue_head *wq_head, struct wait_queue_entry *wq_entry, int 2 state)
3 {
4 unsigned long flags;
5 long ret = 0;
6
7 spin_lock_irqsave(&wq_head->lock, flags);
8 if (unlikely(signal_pending_state(state, current))) {
...
9 } else {
10 if (list_empty(&wq_entry->entry)) {
11 if (wq_entry->flags & WQ_FLAG_EXCLUSIVE)
12 __add_wait_queue_entry_tail(wq_head, wq_entry);
13 else
14 __add_wait_queue(wq_head, wq_entry);
15 }
16 set_current_state(state);
17 }
...
18 }

11~14 번째 줄 코드에서 웨이트 큐를 Enqueue 시킨 다음, 16 번째 줄 코드에서 set_current_state() 함수를 호출해서 프로세스 상태를 TASK_INTERRUPTIBLE(휴면상태)로 바꿉니다.


set_current_state() 함수 구현부는 다음과 같습니다.
[https://elixir.bootlin.com/linux/v4.19.30/source/include/linux/sched.h]
1 #define set_current_state(state_value) \
2 do { \
3 WARN_ON_ONCE(is_special_task_state(state_value));\
4 current->task_state_change = _THIS_IP_; \
5 smp_store_mb(current->state, (state_value)); \
6 } while (0)

5 번째 줄 코드가 핵심인데 current->state 필드에 state_value 인자를 저장합니다.
이 과정에 smp_store_mb() 함수를 써서 GCC 컴파일러가 코드 최적화를 위해 코드 위치를 바꾸는 것을 방지합니다.


sys_pause() 함수
이번에 볼 코드는 시그널을 기다릴 때 호출하는 sys_pause() 함수입니다.
[https://elixir.bootlin.com/linux/v4.19.30/source/kernel/signal.c]
1 SYSCALL_DEFINE0(pause)
2 {
3 while (!signal_pending(current)) {
4 __set_current_state(TASK_INTERRUPTIBLE);
5 schedule();
6 }
7 return -ERESTARTNOHAND;
8 }

sys_pause() 함수는 다음 동작을 수행합니다
팬딩된 시그널(자신에게 전달된 시그널) 없는지 점검
자신을 TASK_INTERRUPTIBLE(휴면상태)로 변경
schedule() 함수 호출로 휴면에 진입

위 함수 4 번째 줄 코드를 보면 프로세스 상태를 TASK_INTERRUPTIBLE(실행대기)로 바꿉니다.


시그널에 대한 내용은 12장을 참고하세요.


"혹시 궁금한 점이 있으면 댓글로 질문 남겨주세요. 아는 한 성실히 답변 올려드리겠습니다!" 

Thanks,
Austin Kim(austindh.kim@gmail.com)

Reference(프로세스 스케줄링)

스케줄링 소개
프로세스 상태 관리
   어떤 함수가 프로세스 상태를 바꿀까?
스케줄러 클래스
런큐
CFS 스케줄러
   CFS 관련 세부 함수 분석  
선점 스케줄링(Preemptive Scheduling)   
프로세스는 어떻게 깨울까?
스케줄링 핵심 schedule() 함수 분석
컨택스트 스위칭
스케줄링 디버깅
   스케줄링 프로파일링
     CPU에 부하를 주는 테스트   
     CPU에 부하를 주지 않는 테스트


핑백

덧글

댓글 입력 영역