✍️v1.2
✍️v2.2
✍️v2.4
✍️v2.6
by Ingo Molnar
kernel 2.6.0 ~ 2.6.22 사용
v2.4의 문제점을 개선
전체 작업을 반복하지 않고도 스케줄링할 다음 작업을 식별 할 수 있다.
훨씬 효율적이고 확장성이 높다.
다음 process를 뽑고 다시 넣고 하는게 모두 constant time에 의해 결정
constant time에 의해 처리가 되어서 Order(1)이라고 부름
각 프로세스는 fixed time quantum이 주어짐.
일정시간이 bound되면 다음 process를 바로 뽑는다.
복잡하고 자료구조도 많이 들어가고 하다보니 버그가 많이 나옴.
runnalbe task를 하나의 실행 큐로 추적
실제로는 우선순위 레벨별로 두개의 실행 큐 사용(SMP scheduling)
하나는, active task를 위한 queue (active array)
다른 하나는, expired task를 위한 queue(expired array)
다음 실행 작업을 위해 스케줄러는 각 우선 순위에 해 당하는 특정 active queue에서 다음 작업을 가져오기만 하면 된다.(active queue에 아무것도 남지 않을때까지 반복)
확장성이 훨씬 높아졌으며, 상호 메트릭과 여러 추론 방법을 통합하여 I/O 또는 프로세스 관련 작업인지 여부 결정
하지만, 커널에서 다루기기가 쉽지 않다.
추론 방법을 계산하는데 필요한 코드의 양이 매우 많아서, 근본적으로 관리하기 어렵다.
이를 해결하기 위해서 CFS로 넘어감(현재 리눅스에서 사용중인 형태)
✍️Completely fair scheduling(CFS)
✍️process priority들은 시간이 흐르면서 동적으로 변한다.
기본적인 동작은 sequence로 나열되어 있는 process들중 가장 먼저 있는 process를 돌리고 일정 시간이 지나면 적당한 위치에 다시 넣어 놓는다. 그러고 나서 다음 process를 또 찾아서 일정시간동안 실행시킨다.
계단이 있다고 생각해보자. 각 층마다 process들이 있고, 아래층부터 scheduler가 process들을 실행한다. 1층의 process를 실행시키고 일정 시간이 지나면 scheduler가 적당히 계산해서 14층 정도의 다시 넣어놓고 2층으로 향한다.2층에서도 마찬가지로 process를 실행시키고 일정 시간이 지나면 적당히 24층 이정도에 넣어 놓는다.
다시 넣어놓을때 정해지는 층수는 process의 계산에 의해서 priority가 알맞은 자리를 찾는다.
그렇다면 어떤 순서로 process들을 나열 할 수 있을까?? linked-list를 사용하면 앞에 head만 보고 꺼낼수 있어서 어떤 process들을 실행시킬지는 쉽게 결정된다.
그렇다면 사용하고난 process들을 sequence에 어딘가에 넣을때는 모든 층들을 뒤져봐야 알 수 있다.
이렇게 하면 CFS보다 앞서 썼던 V2.4와도 다를바가 없다.
어떻게 해결할 수 있을까??
✍️red-black tree를 사용하면 된다. red-black tree는 자동으로 balance를 맞춘다. process들을 꺼내서 실행시킬때는 constant하고, 다시 넣을 다음 층수를 계산할때는 O(logn)이 걸린다.
물론 O(1)보다는 시간이 오래걸리지만 직관적으로 구조가 이해가 간다.
✍️target latency
process를 꺼낸다. cpu가 가지고 있는 target latency 만큼 실행할 수 있다.
실행 하고도 target latency가 부족했다면 process를 적당한 위치에 다시 넣어 놓는다.
TL이 target Latency이다. TL은 proportional scheduling처럼 process에게 priority가 높으면 더주고 priority가 낮으면 덜주는 식으로 다르게 시간을 배분한다.
위 그림에서 보면, 위 그림은 priority가 모두 같아서 같은 비율로 주고, 아래 그림은 priority가 A가 더 높아서 A에게 시간을 더 많이 할당한다.
priority가 높으면 process를 다시 넣을때도, 멀지 않은 곳에 넣어놓고, priority가 낮으면 조금 먼곳에 넣어놓는 식으로 한다.
이렇게 되면 priority가 높은 process는 더 자주 scheduling이 될 것이다.
✍️process를 다시 던져 놓는 층수는 어떻게 결정 되는가?
참고로, wait queue에 있는 process를 ready queue로 넣을때도 어느정도의 층에 넣어야 하는지 계산한후 넣는다.
✍️starvation은 발생하지 않는다.
마트를 예로 들어보자. 마트에 가면 계산을 해주는 casher 들이 있다. 그리고 계산 하는 손님 들이 줄을 서 있다. 계산하는 방식에는 두가지가 있는데,
1. 손님들이 자발적으로 각 casher에게 줄을 서거나
2. 손님들을 줄 세워주는 직원이 한명 있는 것이다.
scheduling에서는 casher를 processor, 손님을 process로 대응시킬 수 있다.
✍️Asymmetric multiprocessing
✍️Symmetric multiprocessing(SMP)
그렇다면 이게 scheduling과 무슨상관일까?
어떤 cpu가 malloc을 했다고 하자. 당연히 근처에 있는 memory를 할당해준다.(momory access가 빠르기 때문) 그런데 이런게 엄청 쌓여서 여러 process가 cpu에서 돌게 되었다. 그러면 process가 일을 많이 하기 때문에 다른 processor가 pull migration을 해주게 된다. 문제는, pull migration한 cpu에서 memory가 멀기 때문에 접근하는데 시간이 오래걸리게 된다. 그러면 cpu의 환경은 좋아졌지만 갑자기 내 컴퓨터에 performance는 낮아지게 된다.
운영체제가 알아서 해줄 수는 없을까??
운영체제도 cpu가 어떻게 행동할지 미래정보를 알 수는 없다.
NUMA에서 computer perfomance가 내려가는 문제를 해결하기 위해서, 사용자는 process가 특정 cpu에서 돌 수 있도록 system call을 통해 지정해줄 수 있다.(processor Affinity)
만약에 process가 cpu에서 과부하가 되더라도 pull migration 당하지 않고 내가 지정한 cpu에서만 돌게 하는 것이다. 이렇게 하면 memory 접근이 멀어지는 것을 막을 수 있다.
✍️hyperthreading 문제
하지만, 두 thread간에 타이밍이 잘 안맞는다면??
예를들어 thread0에 compute cylce이 길어버리면 thread1은 0이 끝날때까지 기다릴 수 밖에 없다. 성능이 들쭉날쭉할 수 있다.
성능이 낮아도 일정한 성능이 필요하다면 hyperthreading을 끈다.
때문에 위에 기숙사 예를든 것 처럼 thread 쌍을 잘 맞춰줘야(compute cycle과 memory stall 타이밍이 잘맞게) ALU를 효과적으로 잘쓸수 있다.
예전에는 사람들은 백그라운드 환경에 있는 것들보다는 보이는 것이 중요하다는 생각을 하게 되었고, 백그라운드는 아예 동작을 멈추기로 함. foreground만 성능을 부스팅 시켜줌. 예를들어 CFS에서 사용자가 보고 있는것들은 priority를 높게 해서 계속 사용할 수 있게 해주고 background에 있는 것들은 priority를 낮게 해서 조금 느리게 실행되도 상관없었다.
요즘에는, 베터리 성능이나 cpu 성능도 좋아져서 이정도로 제약이 심하지는 않다.
사용자가 백그라운드에서 사용할지 말지 정할 수 있다.