이화여자대학교 컴퓨터공학과 반효경 교수님의 "운영체제 (KOCW)" 강의를 필기한 내용입니다.

다소 잘못된 내용과 구어적 표현 이 포함되어 있을 수 있습니다.

문서를 이전하는 과정에서, 이미지 데이터가 유실되어 문서상에 보이지 않습니다.

CPU, IO Burst

(사진 사라짐)

  • 프로세스가 실행되는 것은 (일반적으로) CPU 를 연속적으로 사용하다가 IO 때문에 Block 되있거나 하는 것의 반복이라고 할 수 있는데 이때
  • CPU 를 연속적을 사용하는 구간을 CPU Burst 라고 하고
  • IO 때문에 Block 먹어있는 구간을 IO Burst 라고 한다

CPU, IO Bound Job

(사진 사라짐)

  • 이 그래프는 한 CPU Burst 의 실행시간과 CPU Burst 의 빈도를 나타낸 그래프인데
  • 보면 왼쪽은 CPU Burst 의 기간이 아주 짧고 빠르게 반복된다
    • 이것은 잦은 IO 에 의해 CPU Burst 와 IO Burst 가 빈번하게 반복되는경우인데
    • 이러한 Job (== Process) 들을 IO Bound Job 이라고 한다
    • 일반적으로 IO 는 표준 입출력 등의 사람과 Interaction 하기 위한 것이 많기 때문에 사람과의 interaction 이 잦은 경우에 IO Bound Job 이 된다
  • 그리고 오른쪽의 Job 들은 한번 CPU 가 잡으면 오랫동안 사용하여 CPU Burst 의 기간이 길고 따라서 빈도는 낮아지는데 (당연히 한번 잡았을때 길게 쓰니까 빈도는 작아질 수 밖에 없다)
    • 이러한 Job 들을 CPU Bound Job 이라고 부르고 일반적으로 연구 등의 목적을 위해 복잡한 계산을 오랫동안 진행하는 Job 들인 경우가 많다
  • 뭐 그래서 IO Bound Job 이 사람과의 상호작용이 잦기 때문에 CPU Bound Job 이 너무 CPU 를 오래 잡고 있어 사용자 Response 가 늦어지는 일이 벌어지지 않게 하기 위해 CPU Scheduling 을 한다네

Scheduler, Dispatcher

  • CPU Scheduler: Ready 인 프로세스 중에 Running 상태가 될 프로세스를 고르는 커널 프로세스
    • CPU Scheduling 이 발생하는 경우는 대표적으로 다음과 같다
      • 프로세스가 CPU Time 을 _자진 반납_하는 경우 (Non-preemptive)
        • IO 등의 사유로 CPU Time 을 반납 (Running → Blocked)
        • Process Terminate 로 CPU 반납 (Running → Exit)
      • 프로세스가 CPU Time 을 빼앗기는 경우 (Preemptive)
        • Timer interrupt (Running → Ready)
        • IO 가 완료된 프로세스의 우선순위가 현재 프로세스보다 높을 때 (Blocked → Ready)
  • Dispatcher: 현재의 프로세스에서 CPU Scheduler 가 고른 프로세스로 Context switch 를 진행하는 커널 프로세스

CPU Scheduling

고려사항들

  1. Ready 상태인 (CPU Burst 에 진입한) 프로세스 중 누구한테 CPU 를 줄 것인가?
  2. 한번 CPU 를 받았으면 끝날때까지 계속 쓰게 할 것인가 아니면 중간에 뺏을 것인가?

Performance Index (Measure, Criteria)

  • Performance Index 는 성능을 측정하는 척도를 의미하는데 아래와 같이 두개로 나눌 수 있다
  • System Performance: 얼마나 시스템의 자원을 효율적으로 굴리느냐
    • CPU Utilization: 얘는 CPU 이용률을 의미한다
      • “률” 이기 때문에 당연히 전체에서 부분이 차지하는 비율을 의미하는데
      • CPU Utilization 에서는 “시간” 을 기준으로 측정한다 → 즉, 시스템이 작동하고 있는 전체 시간 중에서 CPU 가 일을 하고 있는 비율이 얼마냐
    • Throughput: 얘는 처리량을 의미하는데
      • CPU Utilization 이 시간에 대한 값이었다면 얘는 양을 나타내는 값이다
      • 즉, 단위시간동안 처리한 작업의 양을 의미하는 것
  • Program Performance: 사용자가 느끼는 프로세스의 빠릿빠릿함
    • Turnaround Time: 한번의 CPU Burst 동안 걸린 시간의 총합
    • Waiting Time: 한번의 CPU Burst 동안 CPU 가 할당되지 않고 기다린 시간의 총합
    • Response Time: 한번의 CPU Burst 동안 CPU 가 처음으로 할당되기까지 걸린 시간
    • 그냥 이렇게만 보면 멍게소리인가 싶을텐데 한번의 CPU Burst 에 어떤 일들이 일어나는지를 생각해보면 알기 쉽다
      • 먼저 IO Burst 가 끝나고 CPU Burst 에 들어온 시간이 0초라고 해보라
      • 그러고 바로 CPU 를 할당받을 수 있으면 기모찌하겠지만 인생이란게 그렇게 녹록하지 않아서 4초에 CPU 를 할당받아서 작업을 했다라고 치면
      • 첫 4초가 Response Time 이 되는 것
        • 즉, CPU Burst 가 시작된 이래로 얼마나 빨리 CPU 가 할당되었느냐 이다
      • 그리고 만일 Preemptive 로 스케줄링되어 6초에 CPU 를 빼앗겼다가 7초에 다시 받고 10초에 IO 가 생겨서 IO Burst 로 빠져나갔다고 치면
      • 일단 기다린 시간을 다 합쳐보면 맨 처음 4초에 중간에 1초 기다렸으니까 5초 → 이게 Waiting Time 이 된다.
        • 즉, Waiting Time 은 처음의 Response Time에다가 CPU Burst 중간중간에 쉬는시간까지 다 합친 값이다
      • 그리고 전체적으로는 10초가 걸렸으므로 이게 Turnaround Time 이 된다
        • 즉, Turnaround Time 은 IO Burst 사이의 시간 간격이라고 생각할 수도 있고
        • Waiting Time 에다가 CPU Time 까지 합친 시간이라고 생각할 수도 있다

스케줄링 알고리즘 분류

  1. Preemptive: 하나의 프로세스가 너무 오래 CPU 를 차지하지 못하도록 중간에 뺏는 알고리즘
  2. Non-preemptive: 하나의 프로세스가 CPU 를 먹으면 IO 등의 이슈가 없는 한 계속 들고 있게 하는 알고리즘

Priority Scheduling

  • 그냥 단순하게 생각해서 우선순위에 따라 다음 프로세스를 선택하는 방식인데
  • Preemptive 의 경우에는 당연히 우선순위가 높은 놈이 들어오면 빼앗고 Non-preemptive 의 경우에는 높은놈이 들어와도 빼앗지 않았다가 그놈이 끝나면 그 다음 우선순위 높은놈에게 주는 방식
  • 주의할 점은 일반적으로 UNIX(Linux) 계열에서는 숫자가 낮을수록 우선순위가 높은 거다
    • Syslog 생각해봐도 emerg 가 0 이잖여

Starvation, Aging

  • 이후에 등장하는 스케줄링 알고리즘에서 Starvation 이라는 말이 나오는데 이건 알고리즘의 부작용으로 특정 프로세스가 CPU 를 할당받지 못하는 상황을 의미한다
    • 만일 우선순위가 낮은 프로세스의 경우 해당 프로세스보다 우선순위가 높은 프로세스가 항상 존재한하면 해당 프로세스는 영원히 CPU 를 받지 못한다.
  • 이를 해결하기 위한 방법으로 Aging 이 있는데 이건 우선순위가 낮은 프로세스가 오랫동안 CPU 를 할당받지 못하면 자연스럽게 우선순위가 높아지게 하는 방법을 의미한다

Scheduling Algorithms

FCFS (First Come First Serve)

  • 뭐 별거 없다 → 선입선출

    • 당연히 무지성 선입선출이기 때문에 Non-preemptive 이다
  • 얘의 문제점은 예상하시는 바와 같이 앞에 오래걸리는 놈이 하나 버티고 있으면 그 뒤에 있는 놈들은 다 지연된다는 거다

  • 예를 들어 아래의 두개 상황을 비교해봐라

    (사진 사라짐)

    • 첫번째의 경우는 앞에 오래걸리는 애가 있어서 평균 Waiting Time 이 17이나 되지만
    • 만일 앞에 짧은 애가 오면 평균 Waiting Time 은 3으로 거의 1/6 이 줄어든다
    • 이렇듯 FCFS 에서 앞에 오래걸리는 한놈때문에 나머지가 전부 지연되는 것을 Convoy effect 라고 하더라

SJF (Shortest Job First) 혹은 SPN (Shortest Process Next)

  • FCFS 를 보면서 ‘그럼 제일 적게걸리는 놈한테 먼저 주면 되는거 아닌가’ 라고 생각했으면 이게 그거다

  • 즉, CPU Time 이 제일 적은 놈에게 우선적으로 CPU 를 주는 것

  • 얘는 이제 Non-preemptive 하고 Preemptive 두가지 버전이 있는데

  • Non-preemptive SJF 은 일단 CPU 를 CPU Time 이 적은놈한테 주되 이걸 빼앗을 수는 없으므로 더 짧은 놈이 들어와도 일단은 현상유지하는 것이고

  • Preemptive SJF 는 CPU Time 이 더 짧은 놈이 오면 CPU 를 빼앗아서 이놈한테 주는거다

    • 그런데 이때 CPU Time 은 지금 실행중인 놈의 남은 시간과 새로운 놈의 시간을 비교하기 때문에 SRTF (Shortest Remaining Time First 혹은 그냥 SRT) 라고도 부른다
    • 또한 이 경우는 평균 Waiting Time 이 최소가 되는 것으로 알려저 있다 (Waiting Time Optimal)
      • 똑똑이들이 증명해놨다네
  • 뭐 아주 좋아보이지만 아쉽게도 얘도 문제가 있다

    1. Starvation: 눈치챘겠지만 앞에 짧은 애들만 오면 긴놈은 절대로 CPU 를 받을 수 없다.
    2. 그리고 CPU Time 는 사전에 알지 못하는 값이다
  • CPU Time 을 예측하는 방법으로 Exponential Averaging 이라는게 있는데 다른 분야에도 등장하는 개념이라니까 간단하게 짚고 넘어가면

    • t(n) 은 n 번째의 CPU Time 이고
    • 따우(n) 은 n 번째의 CPU Time 예측값일때
    • Exponential Averaging 의 공식은 다음과 같다

    (사진 사라짐)

    • 이 식을 전개해보면

    (사진 사라짐)

    • 가 되는데 상수 a0 < a && a < 1 이기 때문에 제곱할수록 작아진다
    • 즉, 제일 최근의 값은 1에 그나마 가까우므로 가중치가 높아지고 옛것으로 갈수록 0에 가까워지니까 가중치가 낮아지는 것으로도 생각할 수 있는 것
    • 찾아보니까 이 개념은 머신러닝에서도 사용되는듯

RR (Round Robin)

  • 지겹다 지겨워 그냥
  • 알다시피 무지성 돌라돌라골림판이다
    • 즉, 일정한 Time Quantum 으로 CPU Time 을 난도질해서 해당 시간이 끝나면 다른 프로세스에게 또 Time Quantum 만큼의 CPU Time 만 주는 방법이다
  • 얘는 다양한 CPU Time 을 가지는 프로세스들이 섞여있을 때 맛집이 된다
    • 스케줄링 할 때 CPU Time 을 예측할 필요도 없고
    • CPU Time 크든 작든 돌아가며 CPU 가 할당되므로 Starvation 에 빠질 우려도 없다
    • 심지어 CPU Time 길수록 Waiting Time 도 늘어나는 합리성까지 보여준다
  • 하지만 CPU Time 이 전부 똑같을때는 똥된다
    • 단순히 FCFS 를 생각해도 100 짜리 4개가 들어오면 100, 200, 300, 400 의 시간에 프로세스가 종료되지만
    • RR 로 돌리면 다같이 돌다가 전부 400에 프로세스가 종료되기 때문
    • 이렇듯 CPU Time 이 같은 경우에는 비효율적이나 일반적인 상황이 아니기 때문에 대부분 효율적이다
  • Time Quantum 이 극단적이 되면 어찌되는가
    • q 가 너무 커지면 FSFC 와 다를바가 없어서 비효율적이고
    • q 가 너무 작아지면 Context Switching 의 오버헤드가 너무 커져 비효율적이 된다

Multi-level Process Queueing

  • 위에 소개된 알고리즘들은 전부 프로세스를 큐 하나에 때려박고 적당히 꺼내서 CPU 를 할당해주는 방식이었다면
  • 지금부터 소개되는 알고리즘들은 프로세스를 우선순위에 따라 여러 큐에 넣어서 관리하는 방법들이다
  • 이러한 방식은 일반적으로 큐마다 다른 스케줄링 알고리즘을 사용하고
  • 상위 우선순위의 큐가 비지 않아 Starvation 이 발생할 것을 방지하기 위해 CPU 를 차등 분배한다
    • 즉, 상위 우선순위의 큐가 비어야만 하위 큐로 가는게 아니고
    • 우선순위가 높은 큐에는 CPU 를 더 많이 할당해주고 낮은 큐에는 적게 할당하는 식으로 유도리있게

Multi-level Queue

(사진 사라짐)

  • 얘는 프로세스의 특성에 따라 우선순위를 두고 우선순위에 따른 큐를 여러개 만들어 상위 우선순위의 큐가 비어야 그 아래 큐에 있는 프로세스에게 CPU 가 할당될 수 있도록 하는 Preemptive 한 방식이다
    • 위 그림은 그렇게 여러개의 큐를 나누어놓은 예시 그림임
  • 하지만 이러한 방식은 우선순위 변동의 유연함이 없어 문제가 있더라

Multi-level Feedback Queue (Feedback Scheduling)

(사진 사라짐)

  • 얘는 RR 방식의 큐를 여러개 준비해놓고 아래로 내려갈수록 Quantum 값이 증가하게 해놓은 다음
  • 처음 들어온 프로세스는 제일 우선순위가 높은 큐에 넣고
  • Quantum 내에 끝내지 못하면 그 아래 큐로 내려보내는 방식이다
  • 이 방식은 SJF 알고리즘에서 CPU Time 을 알 수 없다는 단점을 해결했다고 볼 수 있는데 왜냐면
  • 일단 처음에는 CPU Time 을 알 수 없으니까 Quantum 을 짧게 하는 대신 우선순위를 높여주고
  • 해당 Quantum 내에 끝내지 못했다면 Quantum 을 좀 더 오래 가져가는 대신 우선순위가 낮아지게 함으로써
  • 자연스럽게 빨리 끝나는 프로세스는 우선순위가 높아지고 오래걸리는 프로세스는 우선순위가 낮아지는 효과를 볼 수 있다

그 외의 여러가지 CPU Scheduling 방식들

Multi-processor Scheduling

  • 프로세스들이 고만고만한 경우 (Homogeneous): 하나의 큐에 다 때려넣고 여러개의 CPU 들이 나눠먹거나
    • 반드시 특정 CPU 에서만 실행되어야 하는 경우에는 걔를 위해 CPU 하나를 할당해주고 나머지를 나눠먹거나
  • CPU 부하 분산 (Load Sharing): 놀고 있는 CPU 가 없도록 하기 위해 공동 큐를 두거나 CPU 마다 큐를 구성
  • Symmetric Multiprocessing: CPU 들이 동등한 자격으로 스스로 스케줄링 하는 방식
  • Asymmetric Multiprocessing: 얘는 마스터를 선출하는거마냥 스케줄링 작업을 전담하는 CPU 를 하나 선출해서 나머지는 이놈이 스케줄링한거에 그냥 따르는 방식

Real Time Scheduling

  • 일반적으로 Real Time 이라고 하면 주어진 시간 내로 반드시 종료되야 하는 것을 의미하는데
  • Hard Real Time: 말 그대로 → 시간 내에 반드시 끝나야 함
  • Soft Real Time: 얘는 시간 내에 끝나야 하긴 하지만 그렇지 못한다고 해서 큰 문제가 생기지 않는 경우를 의미하는데
    • 그냥 일반적인 스케줄링에서 우선순위를 좀 높여주는 방식으로 해결 가능하고
    • 동영상 재생이 대표적인 예시다 → 1초에 24프레임 이상을 로드해야되지만 그렇지 못했다고 해서 지구멸망은 아닌

Thread Scheduling

  • User Thread 의 경우: Local Scheduling 이라고 부르는데 OS 는 이 쓰레드의 존재를 모르기 때문에 POSIX Thread 같은 라이브러리들이 직접 해준다
  • Kernel Thread 의 경우: Global Scheduling 이라고 부르는데 Short-term scheduler 가 프로세스 스케줄링하는것과 동일하게 해준다

Algorithm Evaluation

  • Queueing Model: 아래 그림처럼 입력 데이터(Arrival Rate) 를 확률분포로 주고 이때의 출력(Service Rate) 를 이용해 성능을 측정

    • 다분히 이론적이어서 많이는 사용하지 않는댄다

    (사진 사라짐)

  • Implementation & Measurement: 실제로 코드를 작성해 커널을 빌드하여 성능 측정

  • Simulation: 커널 전체가 아닌 해당 알고리즘만 코드로 작성해 실제 OS에서의 작동 방식을 분석해 만든 입력 데이터 (Trace) 를 이용한 방법