Arm Linux Kernel Hacks

rousalome.egloos.com

포토로그 Kernel Crash


통계 위젯 (화이트)

40111
637
415416


[리눅스커널] 스케줄링: 5가지 스케줄러 클래스란 무엇일까? - p 10. 프로세스 스케줄링

다음 코드와 같이 struct sched_class 구조체로 스케줄러 클래스를 정의할 수 있습니다. 
[https://elixir.bootlin.com/linux/v4.19.30/source/kernel/sched/sched.h]
1 extern const struct sched_class stop_sched_class;
2 extern const struct sched_class dl_sched_class;
3 extern const struct sched_class rt_sched_class;
4 extern const struct sched_class fair_sched_class;
5 extern const struct sched_class idle_sched_class;

1~5 번째 줄에서 볼 수 있는 5가지 전역 변수를 스케줄러 클래스라고 합니다. 각각 전역 변수를 열어보면 struct sched_class 구조체에 스케줄러 별로 실행하는 함수를 볼 수 있습니다. 예를 들어 RT Class 스케줄러 클래스 선언부를 보겠습니다.
[https://elixir.bootlin.com/linux/v4.19.30/source/kernel/sched/rt.c]
1 const struct sched_class rt_sched_class = {
2 .next = &fair_sched_class,
3 .enqueue_task = enqueue_task_rt,
4 .dequeue_task = dequeue_task_rt,
5 .yield_task = yield_task_rt,
6
7 .check_preempt_curr = check_preempt_curr_rt,
8
9 .pick_next_task = pick_next_task_rt,
10 .put_prev_task = put_prev_task_rt,
...
11 .get_rr_interval = get_rr_interval_rt,
12
13 .prio_changed = prio_changed_rt,
14 .switched_to = switched_to_rt,
15
16 .update_curr = update_curr_rt,
17 };

위 코드를 보면 struct sched_class 구조체 필드에 RT 스케줄러 세부 함수를 선언했습니다. 이렇게 스케줄러 동작을 모듈화한 struct sched_class 구조체 필드에 스케줄러별 세부 함수를 등록한 것이 스케줄러 클래스라고 볼 수 있습니다.

다음 그림에서 5가지 스케줄러 클래스와 세부 함수를 확인할 수 있습니다.
 
[그림 10.21] 스케줄러 클래스 별 세부 연산 함수 

정리하면 스케줄러 클래스는 스케줄러 세부 동작을 표현한 인터페이스용 객체라고 볼 수 있습니다.

스케줄러 클래스 우선순위에 대해서
스케줄러 클래스 사이에 우선순위가 있습니다.
 
[그림 10.22] 스케줄러 클래스 별 우선순위 

우선순위가 가장 큰 스케줄러 클래스는 stop_sched_class인데 오른쪽 화살표 방향 순서 우선순위가 줄어듭니다. RT 스케줄러 클래스(rt_sched_class)는 CFS 스케줄러 클래스(fair_sched_class)보다 우선순위가 높습니다. 

    스케줄러에서 스케줄러 클래스 우선순위에 따라 스케줄러 클래스를 처리하는 코드는 
   무엇일까? 

정답은 for_each_class() 함수입니다. 스케줄러 클래스를 순회할 때 for_each_class() 함수를 호출합니다.

for_each_class() 함수 구현부를 보면 stop_sched_class 전역변수로 시작해서 class->next에 지정된 다음 우선순위 스케줄러 클래스 전역 변수에 접근합니다. 우선순위 방향으로 연결된 스케줄러 클래스를 순차적으로 호출하는 동작입니다.

스케줄러 클래스 별 struct sched_class 구조체 확인하기

이해를 돕기 위해 stop_sched_class 부터 idle_sched_class 전역 변수 선언부를 보겠습니다.
1 const struct sched_class stop_sched_class = {
2 .next = &dl_sched_class,
...
3 const struct sched_class dl_sched_class = {
4 .next = &rt_sched_class,
...
5 const struct sched_class rt_sched_class = {
6 .next = &fair_sched_class,
...
7 const struct sched_class fair_sched_class = {
8 .next = &idle_sched_class,
...
9 const struct sched_class idle_sched_class = {
10 /* .next is NULL */

스케줄러 클래스 구조체인 struct sched_class 의 첫 번째 필드는 struct sched_class next입니다. next 필드에 다음 우선순위 스케줄러 클래스를 지정하는 것입니다.

다음 코드와 같이 struct sched_class 구조체 첫 번째 필드는 const struct sched_class *next입니다.
[https://elixir.bootlin.com/linux/v4.19.30/source/kernel/sched/sched.h]
1 struct sched_class {
2 const struct sched_class *next;
3
4 void (*enqueue_task) (struct rq *rq, struct task_struct *p, int flags);

이번에는 커널 스케줄링 공통 코드에서 for_each_class() 함수를 어떻게 쓰는지 알아봅시다.
[https://elixir.bootlin.com/linux/v4.19.30/source/kernel/sched/core.c]
1 void check_preempt_curr(struct rq *rq, struct task_struct *p, int flags)
2 {
3 const struct sched_class *class;
4
5 if (p->sched_class == rq->curr->sched_class) {
6 rq->curr->sched_class->check_preempt_curr(rq, p, flags);
7 } else {
8 for_each_class(class) {
9 if (class == rq->curr->sched_class)
10 break;
11 if (class == p->sched_class) {
12 resched_curr(rq);
13 break;
14 }
15 }
16 }

check_preempt_curr() 함수 8 번째 줄 코드에서 for_each_class(class) 를 볼 수 있습니다. for_each_class() 코드는 stop_sched_class 전역변수로 시작해서 다음 전역 변수를 순회하는 동작입니다.
[https://elixir.bootlin.com/linux/v4.19.30/source/kernel/sched/sched.h]
#define for_each_class(class) \
   for (class = sched_class_highest; class; class = class->next)

#define sched_class_highest (&stop_sched_class)

for_each_class() 함수 구현부를 보면 stop_sched_class 전역변수로 시작해서 class->next에 지정된 다음 우선순위 스케줄러 클래스 전역 변수에 접근합니다. 이는 우선순위 방향으로 연결된 스케줄러 클래스를 순차적으로 호출하는 것입니다.

배운 내용을 참고해서 다음 코드는 어떻게 해석할 수 있을까요?
8 for_each_class(class) {
9 if (class == rq->curr->sched_class)
10 break;
11 if (class == p->sched_class) {
12 resched_curr(rq);
13 break;
14 }
15 }

먼저 8~15 번째 사이 코드 실행 조건을 봅시다. 이 구간 코드는 stop_sched_class 전역 변수부터 idle_sched_class 전역 변수까지 순회합니다.

다음 9 번째 줄 코드를 보면 현재 CPU를 점유하면서 실행 중인 프로세스의 스케줄러 클래스와 같은지 점검합니다. 

만약 현재 CPU를 점유하면서 실행 중인 프로세스가 RT 스케줄러 클래스에 등록돼 있으면
stop_sched_class, dl_sched_class 변수를 순회한 다음 rt_sched_class 전역 변수와 비교한 후 10 번째 줄 break 문에서 for_each_class 문을 빠져 나올 것입니다. RT 스케줄러 클래스로 등록된 프로세스는 current->sched_class = &rt_sched_class로 지정돼 있기 때문입니다. 


다음 그림은 라즈베리파이에서 확인한 스케줄러 클래스 실제 자료구조입니다.
 
[그림 10.23] Trace32로 본 스케줄러 클래스 자료구조


이번 소절까지 스케줄러 동작을 모듈화하는 스케줄러 클래스와 이 자료 구조를 이용한 5 가지 스케줄러 클래스를 알아봤습니다. 다음 소절에서는 5가지 스케줄러 클래스를 프로세스가 어떻게 등록하는지 살펴보겠습니다.

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

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

Reference(프로세스 스케줄링)

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


# Reference: For more information on 'Linux Kernel';

디버깅을 통해 배우는 리눅스 커널의 구조와 원리. 1

디버깅을 통해 배우는 리눅스 커널의 구조와 원리. 2


 


핑백

덧글

댓글 입력 영역