4장 프로세스 스케줄링. [916] DATE

Download Report

Transcript 4장 프로세스 스케줄링. [916] DATE

4장 프로세스 스케줄링
Sang-bok, Heo
스케줄러
스케줄러
•
•
•
•
리눅스 같은 멀티태스킹 운영체제의 기본 요소
어떤 프로세스를 얼마나 오랫동안 실행할 것인지 결정
동시에 실행되고 있는 듯한 느낌을 주어야 함
실행 가능한 프로세스가 여럿 주어졌을 때 다음에 어떤 프로세스를
실행할 것인가?
2
멀티태스킹
멀티태스킹 운영체제
• 하나 이상의 프로세스를 동시에 실행할 수 있는 운영체제
• 메모리상에서 많은 프로세스가 있을 수 있지만, 현대 리눅스
시스템에서 실행 중인 프로세스는 하나
• 협동형 멀티태스킹, 선점형 멀티태스킹 방식이 존재
- 협동형 : 프로세스가 자발적으로 실행을 중단할 때까지 중단되지 않음
- 선점형 : 프로세스가 언제 실행을 중지하고 또는 실행을 계속할
것인가를 바로 스케줄러가 결정
3
스케줄링 정책
리눅스의 프로세스 스케줄러
• 프로세스 응답시간을 빠르게 하는 것 시스템 사용률을 최대화
• 우선순위가 낮은 프로세스에게 공정함을 보장하면서, 실행가치가
가장 높은 프로세스를 선택
• 첫 버전 ~ 2.4 커널 시리즈 : O(1) 스케줄러
- 대화형(interactive) 프로세스가 없는 커다란 서버 작업에 이상적
• 2.6.23 커널 ~ : CFS(Completely Fair Scheduler)
- Rotating Staircase Deadline 스케줄러, 큐잉 이론에서 빌려다 적용
4
스케줄러 정책
I/O bound vs. CPU bound
• I/O bound
- 대부분의 시간을 I/O 요청
- 실제 실행시간은 아주 짧음
• CPU bound
- 코드를 실행하는데 대부분의 시간을 보냄
- 좀 더 긴 시간 동안, 덜 자주 실행하는 스케줄러 정책이 좋음
• I/O 및 프로세스를 많이 사용하는 경우
- X Window Server
- 워드프로세서
• 대립되는 목표
- 빠른 프로세스 응답시간(낮은 지연)
- 높은 프로세스 처리량
5
스케줄링 정책
프로세스 우선순위
•
•
•
•
프로세스가 얼마나 시간을 필요로 하는지에 따라 결정
높은 우선순위의 프로세스가 낮은 우선순위보다 먼저 실행
같은 우선순위는 Round-Robin방식으로 수행
리눅스의 우선순위
- nice값에 따른 우선순위
· nice는 -20~+19 값을 가지며 큰 nice 값이 낮은 우선순위(default : 0)
- 실시간 우선순위
· 0~99까지의 값을 가짐
· POSIX실시간 표준에 근거하고 있음
6
스케줄링 정책
타임슬라이스(timeslice)
• 선점되기 전까지 작업을 얼마나 더 실행할 수 있는지 나타내는 값
• 타임슬라이스가 길면 대화형 성능이 떨어짐, 짧으면 프로세스간
스위칭이 많아 시간적 오버헤드가 걸림
• I/O 중심인지 프로세스 중심인지에 대한 고려
• 많은 운영체제에선 10ms로 설정
• 리눅스에서는 프로세스에 할당되는 프로세스 시간은 시스템 부하에
따른 함수로 결정
- 나이스 값이 가중치로 작동해 각 프로세스에 할당되는 프로세서 시간의
비율을 변경
• 대부분의 운영체제는 프로세스 우선순위와 남아있는 타임슬라이스
양에 따른 함수를 통해 실행 여부 결정
• 리눅스는 얼마나 많은 비율의 프로세서 시간을 사용했는지를
나타내는 함수를 이용하는 새로운 CFS, 스케줄러가 결정
7
스케줄링 정책
스케줄링 관련 system call
시스템 호출
설명
nice()
프로세스의 나이스 값을 설정한다.
sched_setscheduler()
프로세스의 스케줄링 정책을 설정한다.
sched_getscheduler()
프로세스의 스케줄링 정책을 가져온다.
sched_setparam()
프로세스의 실시간 우선순위를 설정한다.
sched_getparam()
프로세스의 실시간 우선순위를 가져온다.
sched_get_priority_max()
실시간 우선순위의 최대값을 가져온다.
sched_get_priority_min()
실시간 우선순위의 최소값을 가져온다.
sched_rr_get_interval()
프로세스의 타임슬라이스 값을 가져온다.
sched_setaffinity()
프로세스의 프로세서 지속성 정보를 설정한다.
sched_getaffinity()
프로세스의 프로세서 지속성 정보를 가져온다.
sched_yield()
일시적으로 프로세서를 양보한다.
8
리눅스 스케줄링 구현
리눅스 CFS 스케줄러
• 리눅스 스케줄러는 2.6.23버전부터 CFS 스케줄러 도입
• CFS는 SCHED_NORMAL로 정의된 리눅스 일반 프로세스용
스케줄러 클래스이며, kernel/sched_fair.c에 정의
• CFS 구성요소
- 시간 기록
- 프로세스 선택
- 스케줄러 진입 위치
- 휴면 및 깨어남
9
리눅스 스케줄링 구현
시간기록
• 스케줄러 단위 구조체
- 프로세스의 실행시간을 기록(공정하게 할당된 몫만큼 사용하기 위해)
- struct sched_entity라는 스케줄러 단위 구조체를 사용해 프로세스와
관련된 스케줄러 정보를 저장
linux/sched.h
10
리눅스 스케줄링 구현
시간기록
• 가상 실행시간
- 프로세스의 가상 실행시간이 저장, 정규화한 실제 실행시간을 의미
- kernel/sched/fair.c에 정의된 update_curr() 함수가 처리
- 시간 기록 처리 절차
kernel/sched/fair.c
· update_curr()함수는 현재 프로세스의 실행시간을 계산
· 그 값을 delta_exec에 저장
· __update_curr()함수에 전달 및 가중치를 계산, 프로세스의 vruntime 저장
11
리눅스 스케줄링 구현
프로세스 선택
• 다음 실행할 프로세스를 선택할 때 CFS는 vruntime이 가장 작은
프로세스를 선택
• 가장 작은 실행가능 프로세스를 효율적으로 찾고 관리하기 위해
레드블랙트리를 사용
• CFS의 프로세스 선택 알고리즘은 “레드블랙트리의 가장 왼쪽
노드에 해당하는 프로세스를 실행한다”로 요약
• kernel/sched/fair.c에 정의된 __pick_next_entity()함수가 처리
- enqueue_entity()함수 : 프로세스를 트리에 추가
- dequeue_entity()함수 : 프로세스를 트리에서 제거
12
리눅스 스케줄링 구현
스케줄러 진입 위치
• schedule()함수 : 프로세스 스케줄링 작업을 시작
• 커널의 다른 부분에서 이 함수를 통해 프로세스 스케줄러를
호출함으로써 다음에 실행할 프로세스를 선택하고 실행
• pich_next_task()함수를 호출하면서 가장 높은 우선순위의
프로세스를 찾아냄
kernel/sched/core.c
13
리눅스 스케줄링 구현
휴면과 깨어남
• 휴면 중이거나 대기상태인 작업은 실행 불가능한 특별한 상태
• 대기열을 이용해 휴면 상태를 처리
- 대기열은 특정 조건이 일어나기를 기다리는 단순한 프로세스 목록
• 휴면 상태가 되는 이유 : 특정 조건이 발생하는 것을 기다림
- 일정 시간이 지나는 것
- 파일 입출력으로 데이터 읽기
- 다른 하드웨어 이벤트 발생
- 세마포어를 획득하는 경우
• ex) read()시스템 호출 -> 대기상태임을 표시 -> 대기열에 들어감
-> 레드블랙 트리에서 제거 -> schedule() 함수 호출
• 대기 상태
- TASK_INTERRUPTIBLE : 시그널을 무시
- TASK_UNINTERRUPTIBLE : 시그널을 받으면 조건이 발생하지 않아도
깨어날 수 있음
14
리눅스 스케줄링 구현
kernel/wait.c
대기열
• DEFINE_WAIT() 매크로를 이용해 대기열에 추가할 항목 생성
• add_wait_queue() 함수를 이용해 작업을 대기열에 추가
- 대기열이 기다리고 있는 조건이 발생하면 프로세스를 깨움
• prepare_to_wait() 함수를 호출해 프로세스 상태를
TASK_INTERRUPTIBLE이나 TASK_UNINTERRUPTIBLE 상태로 변경
• 작업상태가 TASK_INTERRUPTIBLE라면 시그널에 의해 프로세스가
깨어날 수 있음
• 작업이 깨어나면 조건이kernel/kmod.c
만족됐는지 다시 확인
- 조건 만족 : 루프를 벗어남
- 조건 불만족 : 다시 schedule() 호출해 앞의 과정을 반복
• 조건이 만족되면 작업 상태를 TASK_RUNNING으로 변경,
finish_wait() 함수를 호출해 대기열에서 작업을 제거
include/linux/sched.h
15
리눅스 스케줄링 구현
깨어남
•
•
•
•
•
작업을 깨우는 일은 wake_up() 함수를 통해 처리
주어진 대기열에 있는 모든 작업을 깨움
try_to_watke_up() 함수를 호출해 작업상태를 TASK_RUNNING 변경
enqueue_task() 함수를 호출해 작업을 다시 레드블랙트리에 추가
깨어난 작업의 우선순위가 지금 실행 중인 작업의 우선순위보다
높으면 need_resched 값을 설정
__add_wait_queue() 함수는 작업을 대기열에 추가하고 상태를 TASK_INTERRUPTIBLE
로 바꾼 다음 schedule() 함수를 호출한다. schedule() 함수는 deactivate_task() 함수를
통해 작업을 실행 대기열에서 제거한다.
TASK_RUNNING
시그널을 받으면 작업 상태가 TASK_RUNNING
으로 바뀌고 작업은 시그널 핸들러를 실행한다.
TASK_INTERRUPTIBLE
작업이 기다리던 조건이 발생하면 try_to_wake_up() 함수는 작업 상태를
TASK_RUNNING으로 변경하고, activate_task() 함수를 호출해 작업을 실행대기열에
추가한 다음 schedule() 함수를 호출한다. __remove_wait_queue() 함수는 작업을 대기
열에서 제거한다.
16
선점과 컨텍스트 전환
선점(preemption)
• 유저 선점
- 시스템 호출에서 사용자 공간으로 돌아갈 때
- 인터럽트 처리를 끝내고 사용자 공간으로 돌아갈 때
• 커널 선점
- 인터럽트 처리를 마치고 커널 공간으로 돌아갈 때
- 커널 코드가 다시 선점 가능한 상태가 되었을 때
- 커널 내부 작업이 명시적으로 schedule() 함수를 호출하는 경우
- 커널 내부 작업이 중단돼 대기 상태일 때
· schedule() 함수를 호출하게 되는 경우
17
선점과 컨텍스트 전환
문맥(Context)
• 프로세스의 실행 중단 시 보존되고, 재개 시 다시 원상 복구되어야
하는 프로세스의 실행을 위한 모든 정보를 말함
CPU 내의 각종 범용 및
명령영역(text)
자료영역(data)
스택영역(stack)
특수 레지스터의 내용
프로세스의 현재의 각종
자원 사용 정보
기타 커널의 프로세스
관리 정보
User-level Context
System-level Context
18
선점과 컨텍스트 전환
컨텍스트 전환
•
•
•
•
실행 중인 한 작업에서 다른 작업으로 전환하는 것
kernel/sched.c->kernel/sched/core.c에 정의된 context_switch()처리
schedule()함수가 새로 실행할 프로세스를 선택하면 이 함수가 호출
context_switch() 함수
- <asm/mmu_context.h>에 정의된 switch_mm() 함수를 호출해 이전
프로세스의 가상 메모리 매핑을 새 프로세스의 것으로 바꿈
- <asm/system.h>에 정의된 switch_to() 함수를 호출해 이전 프로세스의
프로세서 상태를 현재 프로세스의 상태로 바꿈
19
스케줄링 호출 그래프
커널 2.6의 schedule() 함수 호출 그래프
schedule()
spin_lock_irq()
preempt_disable()
release_kernel_lock()
PF_DEAD?
yes
EXIT_DEAD
this_rq()
INTERRUPTIBLE?
schedstat_inc()
deactivate_task()
yes
TASK_RUNNING 유지
sched_clock()
run_time 계산
smp_processor_id()
running in rq?
go_idle
yes
switch_tasks
20
스케줄링 호출 그래프
switch_tasks 레이블에서 수행하는 핵심코드
3048
3049
3050
3051
3052
3053
3054
3055
3056
3057
3058
3059
3060
3061
3062
3063
3064
sched_info_switch(prev, next);
if (likely(prev != next)) {
next->timestamp = now;
rq->nr_switches++;
rq->curr = next;
++*switch_count;
prepare_task_switch(rq, next);
prev = context_switch(rq, prev, next);
barrier();
/*
* this_rq must be evaluated again because prev may have moved
* CPUs since it called schedule(), thus the 'rq' on its stack
* frame will be invalid.
*/
finish_task_switch(this_rq(), prev);
} else
21
스케줄링 호출 그래프
switch_tasks 레이블에서 수행하는 핵심코드
• sched_info_switch() 함수 : 프로세스의 prev, next의 스케줄러 관련
통계 정보를 업데이트
• prepare_task_switch() 함수 : 프로세스 전환을 위한 준비 작업을
수행(각종 잠금 작업)
• finish_task_switch() 함수 : 전환 작업을 위해 획득했던 각종 잠금
작업을 해제
• context_switch() 함수 : 프로세스의 메모리 관리 부분과 레지스터
값을 교환하는 것을 처리
22
소스 위치 찾기
pick_next_task() 소스 찾기
•
•
•
•
grep –n pick_next_task ./*
vi core.c
:2889
http://lxr.free-electrons.com/ident
23