CFS 스케줄러 기초 정리

홍문화 2011.05.27 18:00 조회 수 : 12874

내가 사용하는 PC의 커널이 CFS 스케줄러를 사용 하는데 내 머리속에 들어있는 스케줄러는 O(1) 스케줄러라는

사실을 받아들이기 힘들어 CFS에 대해 공부하고 이해한 바를 간단히 정리 했습니다.

우리 스터디 멤버님들께 조금이나마 도움이 되었으면 하는 바람에 글을 올립니다.

잘못된 부분에대해 늘 그렇듯 지적 부탁 드립니다. ^^;

barrios님의 번역에 진심으로 감사드립니다. 


출처 : http://barrioskmc.egloos.com/1797590

         리눅스 커널 심층분석 3판


※ O(1) 스케줄러의 문제점

1. nice값 0(timeslice 100ms)을 가지는 두개의 태스크가 실행 된다고 가정하자.
   컨텍스트 스위칭은 100ms 간격으로 발생 할 것이다.
   nice값 20(timeslice 5ms)을 가지는 두개의 태스크가 실행 된다고 가정하자.
   컨텍스트 스위칭은 5ms 간격으로 발생 할 것이다.
   프로세서가 1000ms 동안 실행 된다고 가정 할 경우 과연 올바른 스케줄링 동작인가?
   이와 같은 문제는 nice 값이 절대적인 timeslice를 결정 하는데서 발생한다.

2. nice값 0과 1을 가지는 두개의 태스크가 실행 된다고 가정하자.
   timeslice는 100ms, 95ms가 할당 될 것이다.
   nice값 18과 19를 가지는 두개의 태스크가 실행 된다고 가정하자.
   timeslice는 10ms, 5ms가 할당 될 것이다.
   똑같이 nice값이 1차이가 나지만 두번째 경우에는 timeslice의 차이가 두배가 되었다.
   이 문제 또한 nice값이 절대적인 timeslice를 결정 하는데서 발생한다.
 
3. timeslice가 timer tick에 의존하여 변화한다.

4. Interactive task를 우선시 하는 문제가 있다.
   Interactive task가 휴면 상태에서 깨어날 때 즉시 실행되게 하므로 (심지어 timeslice를
   모두 소진 했을지라도 active 큐로 들어오는 보너스를 받는다.) expired 큐에 존재하는
   태스크의 실행이 지연된다.

※ CFS 스케줄러

1. CFS는 nice값을 태스크가 할당 받을 proportion of processor에 대한 weight로
   사용한다. 각각의 태스크는 실행 가능한 태스크들의 weight의 총 합을 기준으로
   자신의 weight만큼만 비례적으로 timeslice를 할당 받는 것이다. 예를 들어 프로세서가
   100ms 동안 실행 된다고 가정하고 같은 nice값을 가지는 태스크 두개만 존재 한다고
   생각해보자. 이 두 태스크가 nice값 -19를 가지든 20을 가지든 상관없이 timeslice는
   50ms씩을 얻게 된다. 만일 4개의 태스크만 존재하고 모두 같은 nice 값을 가진다면
   4개의 태스크는 모두 25ms의 timeslice를 얻게 된다. nice값을 절대적인 timeslice와
   분리 함으로써 O(1) 스케줄러의 문제점 1번이 해결 되었다.

2. 이번에도 프로세서가 100ms 동안 실행 된다고 가정해보자. nice값 0과 5를 가지는 두개의
   태스크만 실행 된다고 할 경우 CFS는 두개의 태스크에게 각각 1024와 335의 weight를
   주게 된다. 그러므로 각각의 태스크가 가지는 weight의 비율은 1024/(1024+335),
   335/(1024+335) 대략 3:1이 된다. 그러므로 nice값 0인 태스크는 75ms의 timeslice를
   nice값 5인 태스크는 25ms의 timeslice를 얻게 된다. nice값 5와 10를 가지는 두개의
   태스크만 실행 된다고 할 경우에도 weight의 비율은 3:1이 되므로 두개의 태스크는
   각각 75ms, 25ms의 timeslice를 얻게 된다. 마찬가지로 nice값을 절대적인 timeslice와
   분리 함으로써 O(1) 스케줄러의 문제점 2번이 해결 되었다.

3. Virtual Runtime은 normalized 또는 weighted 된 태스크의 실제 실행된 시간이다.
   Virtual Runtime의 units은 나노초 단위이다. 그러므로 CFS는 HZ상수의 분해능을 가지는
   timer tick에 의존하지 않는다. => 이 부분은 아직 정확히 이해하지 못했습니다. ㅋ  
  
   CFS에서 Virtual Runtime을 어떻게 사용하는지 알아보자.
   같은 nice값으로 인해 100ms의 동일한 timeslice를 가지는 두개의 태스크만 실행 되고
   스케줄러는 20ms 주기로 컨텍스트 스위칭을 수행한다고 가정하자.
   CFS(완전히 공평한) 스케줄링 이론에 따르면 60ms의 시간이 지난 후에 두 태스크는
   30ms씩 실행이 되어야 한다. 하지만 스케줄러는 태스크의 20ms 주기로 컨텍스트 스위칭을
   수행 하므로 두 태스크는 40ms, 20ms씩 실행이 된다. 그러나 CFS는 자신의 역할을 충실히
   달성하기 위해 20ms를 실행한 태스크를 선택하여 40ms를 실행한 태스크를 따라잡게 한다.
   이러한 방법으로 스케줄링을 한다면 두 태스크의 실행 시간 차이는 20ms를 넘어서지
   않을 것이다.

   이번에는 서로 다른 nice값 3:1의 weight 비율을 가지는 두개의 태스크만 실행 되고
   스케줄러는 20ms 주기로 컨텍스트 스위칭을 수행한다고 가정하자.
   이상적인 CFS 이론에 따르면 60ms의 시간이 지난 후에 두 태스크는 45ms, 15ms씩 실행이
   되어야 한다. 하지만 위와 마찬가지로 두 태스크는 40ms, 20ms씩 실행이 된다.
   이 상황에서 다음번에는 어떤 태스크가 스케줄링 되어야 할까?
   스케줄러는 이러한 상황을 판단하기 위해서 각 태스크의 실행 시간에 scale factor를
   곱하여 virtual runtime을 생성한다. 예를 들어 40ms 실행 된 태스크는 scale factor가
   1이고, 20ms 실행 된 태스크는 scale factor가 3이다. 두 태스크의 virtual runtime은
   40ms, 60ms가 된다. 이제 상황이 명확해졌다. virtual runtime 40ms를 실행한 태스크가
   더 뒤쳐져있다. 그러므로 스케줄러는 이 태스크를 실행해서 뒤쳐진 시간을 따라잡을
   것이다. 즉, virtual runtime이라는 개념이 도입되면서 낮은 우선순위의 태스크는
   높은 우선순위의 태스크가 timeslice를 모두 소비 할때까지 기다려야 하는 불공평한
   문제가 해결 된것이다.     

4. 3번과 같은 상황만을 고려하여 생각 한다면 새로 생성되는 태스크나 잠들었다 깨어난
   태스크는 이미 존재하는 태스크들의 실행 시간을 따라잡을 때까지 실행하게 될 것이다.
   이는 바람직하지 못한 상황이다. 그러므로 새로 생성되는 태스크는 기존에 실행 되고
   있던 태스크들 중에서 가장 작은 virtual runtime 실행한 태스크와 동일한
   virtual runtime을 가지게 되고 약간 조절되게 된다.

  
   CFS의 run queue는 virtual runtime을 기준으로 정렬한 red-black tree로 유지된다.
   CFS가 컨텍스트 스위칭을 수행 할 때 선택하는 태스크는 RB tree에서 가장 작은
   virtual runtime을 가진 태스크이다. 이 태스크는 RB tree의 가장 왼쪽에 위치하게 된다.


   CFS의 timeslice는 다음과 같다.
  
   timeslice = (se->load.weight / cfs_rq->load.weight) * period
   
   se는 struct sched_entity 구조체로 struct sched_class *sched_class와 함께
   struct task_struct에 새롭게 추가된 녀석이다. se를 태스크라고 생각해도 무방하다.
   period는 스케줄러가 모든 태스크들을 수행하는데 걸리는 시간으로 default 20ms이다.
   특정 태스크의 timeslice는 모든 태스크들의 weight의 총 합에서 자신의 weight가
   차지하는 비율로 계산된다.

   실행 중에 있는 특정 태스크의 virtual runtime은 다음과 같다.

   vruntime += (NICE_0_LOAD / se->load.weight) * delta_exec


   NICE_0_LOAD는 상수값 1024이다.     
   delta_exec은 실제로 CPU를 할당받아 실행된 시간이다.
   태스크의 weight값이 클수록 vruntime이 작아지는 것을 알 수 있다.
   즉, CPU 시간을 더 할당 받을 수 있는 기회가 커지는 것이다.

번호 제목 글쓴이 날짜 조회 수
공지 [공지] 커널 스터디 관련 Q&A 게시판 입니다. [5] woos 2016.04.09 2194
1725 [잡담] O(1) 스케쥴러에서 [5] 박은병 2008.04.02 13742
1724 u-boot 분석 참고자료 file 구본규 2012.07.27 13313
1723 [추가] linux, busybox .config / build 방법 [4] file 구본규 2012.07.29 13178
» CFS 스케줄러 기초 정리 홍문화 2011.05.27 12874
1721 이클립스에서 ARM Linux 개발 툴 사용하기(DS-5) [1] file 와사 2013.08.15 12867
1720 x86 linux booting 과정 file 백창우 2007.02.23 12734
1719 [문의] linux device driver 개정 3판 가지고 계신분? [6] 맥주 2008.03.27 12442
1718 링크 레지스터 오프셋 [6] 정현철 2011.06.12 12360
1717 안녕하세요~ 소성은 입니다~ [5] file 소성은 2010.04.05 12159
1716 [문서] 커널 분석 문서입니다. file 맥주 2008.11.10 12087
1715 커널 소스 분석에 도움을 주는 도구들 구본규 2012.08.06 12006
1714 RealMode? ProtectedMode? [2] 김태훈91 2012.05.11 11951
1713 linux부팅과정 설명 문서 [3] file 이상철 2009.03.18 11768
1712 ELF 파일 포맷 정리 [6] 도영주 2013.05.04 11536
1711 [x86] fixed_addresses pororo 2012.03.04 11498
1710 kernel stack과 이를 이용하는 context_switch()사이의 연관성에 대한 질문 [11] 이종인 2011.05.27 11433
1709 memory map in powerpc kernel [1] file 김강년 2007.07.08 11267
1708 커널 스터디 6기 멤버 소개(arm-11 mp-core) 소개 페이지로 이동 예정 [4] 강진성 2010.03.24 11224
1707 리눅스 커널 초기화(ARM) 참고 자료 [2] file 유강희 2010.04.07 11221
1706 다들 주무시죠?? 자~ 질문입니다 ㅋ [4] 변유준 2007.06.16 11220
XE Login