Linux Kernel(4.19) Hacks

rousalome.egloos.com

포토로그 Kernel Crash


통계 위젯 (화이트)

96258
1323
114598


[리눅스커널] 스케줄링: TASK_RUNNING(실행 대기)로 바뀔 때 호출되는 함수 - p 10. Process Scheduling

TASK_RUNNING(실행 대기)로 바뀔 때 호출하는 함수 분석
프로세스가 다음과 같은 동작을 수행할 때 실행대기(TASK_RUNNING) 상태로 바꿉니다.
프로세스를 깨울 때
프로세스를 처음 생성하고 실행 요청을 할 때
프로세스 관련 정보를 업데이트 할 때

보통 휴면 중에 있는 프로세스를 깨우면 프로세스는 실행대기(TASK_RUNNING)상태로 바꿉니다.

프로세스 상태가 실행대기(TASK_RUNNING)로 바뀔 때 실행하는 함수 목록은 다음과 같습니다.

 
[그림 10.15] 프로세스 상태가 실행 대기로 바뀔 때 호출하는 함수 목록

위에서 소개한 함수를 분석하면서 프로세스 상태를 실행대기(TASK_RUNNING) 바꾸는 과정을 살펴봅시다.

wake_up_new_task()
프로세스가 생성된 직후 커널은 생성한 프로세스 상태를 실행대기(TASK_RUNNING) 상태로 바꿉니다.
 
[그림 10.16] 프로세스 생성 직후 실행 대기 상태로 바뀌는 함수 흐름도

프로세스를 생성할 때 _do_fork() 함수를 호출합니다. _do_fork() 함수에서 copy_process() 함수 호출로 부모 프로세스 리소스를 자식 프로세스에게 복사합니다. 
이 과정을 마무리하면 부모 프로세스는 바로 wake_up_new_task() 함수를 호출해서 생성한 자식 프로세스를 깨웁니다. 즉, 프로세스를 생성한 다음 바로 실행 요청을 하는 것입니다.

이번에 wake_up_new_task() 함수 코드를 보겠습니다.
[https://elixir.bootlin.com/linux/v4.19.30/source/kernel/sched/core.c]
1 void wake_up_new_task(struct task_struct *p)
2 {
3 struct rq_flags rf;
4 struct rq *rq;
5
6 raw_spin_lock_irqsave(&p->pi_lock, rf.flags);
7 p->state = TASK_RUNNING;

7번째 줄 코드에서 TASK_RUNNING 로 프로세스 상태를 바꿉니다. 여기서 다음과 같은 의문이 생깁니다. 

    wake_up_new_task() 함수는 어떤 흐름으로 호출될까?

다음 코드를 보면 알수 있듯이 프로세스를 생성하는 _do_fork() 함수에서 wake_up_new_task() 함수를 호출해 생성한 프로세스를 깨웁니다.
[https://elixir.bootlin.com/linux/v4.19.30/source/kernel/fork.c]
01 long _do_fork(unsigned long clone_flags,
02       unsigned long stack_start,
03       unsigned long stack_size,
04       int __user *parent_tidptr,
05       int __user *child_tidptr,
06       unsigned long tls)
07 {
08 struct completion vfork;
...
09 wake_up_new_task(p);

wake_up_process() 함수
휴면 중인 프로세스를 깨울 때 가장 많이 쓰는 함수입니다. wake_up_process() 함수를 호출하면 프로세스는 TASK_RUNNING(실행 대기) 상태로 바꿉니다. 
                   
[그림 10.17] 프로세스를 깨울 때 함수 실행 흐름도

wake_up_process() 함수를 호출하면 위 그림과 같은 함수 흐름으로 ttwu_do_wakeup() 함수를 호출합니다.

프로세스 상태를 TASK_RUNNING(실행 대기)로 바꾸는 ttwu_do_wakeup() 함수 코드를 같이 보겠습니다.
[https://elixir.bootlin.com/linux/v4.19.30/source/kernel/sched/core.c]
1 static void ttwu_do_wakeup(struct rq *rq, struct task_struct *p, int wake_flags,
2    struct rq_flags *rf)
3 {
4 check_preempt_curr(rq, p, wake_flags);
5 p->state = TASK_RUNNING;
6 trace_sched_wakeup(p);

5번째 줄 코드를 보면 태스크 디스크립터 state 필드에 TASK_RUNNING를 저장합니다. 프로세스 상태를 TASK_RUNNING로 바꾸는 코드입니다.


프로세스를 깨운다는 의미를 잠깐 더 생각해 봅시다.

“프로세스를 깨운다”란 문장은 프로세스 실행을 스케줄러에게 요청한다는 의미입니다. 이때 런큐에서 실행을 기다리는 프로세스들과 우선순위를 참고해서 실제 실행은 스케줄러가 수행합니다.

프로세스를 깨운다는 의미는 프로세스 실행을 스케줄러에게 요청한다는 것입니다.


yield() 함수
프로세스 실행을 잠시 양보할 때 호출하는 yield() 함수에서도 프로세스 상태를 실행 대기(TASK_RUNNING)로 바꿉니다.

yield() 함수 코드를 소개합니다.
[https://elixir.bootlin.com/linux/v4.19.30/source/kernel/sched/core.c]
1 void __sched yield(void)
2 {
3 set_current_state(TASK_RUNNING);
4 sys_sched_yield();
5 }

3번째 줄 코드를 보면 set_current_state() 함수 호출로 프로세스 상태를 TASK_RUNNING으로 바꿉니다.

4 번째 줄 코드에서 호출하는 sys_sched_yield() 함수는 스케줄러 클래스 yield_task 메소드를 호출한 다음 스스로 휴면에 진입합니다.

do_nanosleep() 함수
이번에는 특정 시각 동안 휴면에 진입하는 do_nanosleep() 함수 코드를 보겠습니다.
[https://elixir.bootlin.com/linux/v4.19.30/source/kernel/time/hrtimer.c]
1 static int __sched do_nanosleep(struct hrtimer_sleeper *t, enum hrtimer_mode mode)
2 {
3 struct restart_block *restart;
4
...
5 __set_current_state(TASK_RUNNING);

5번째 줄 코드와 같이 프로세스 상태를 TASK_RUNNING 으로 바꿉니다.

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

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

Reference(프로세스 스케줄링)

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




핑백

덧글

댓글 입력 영역