컴퓨터 구조 운영체제 네트워크 자료구조 알고리즘 데이터베이스 내 학습
os · 19

실시간 스케줄링(RM/EDF)

EDFRM실시간

일반 운영체제의 스케줄러는 평균 응답 시간이나 공정성을 높이는 데 초점을 둡니다. 반면 자동차 제동 제어나 항공기 제어, 오디오 재생 같은 실시간 시스템에서는 계산이 정해진 시간(마감, deadline) 안에 끝나는 것이 결과의 정확성만큼 중요합니다. 마감을 반드시 지켜야 하는 경우를 경성(hard) 실시간, 마감을 가끔 놓쳐도 응답 시간의 분포가 허용 범위 안이면 되는 경우를 연성(soft) 실시간이라고 부릅니다. 그래서 실시간 시스템은 "평균적으로 빠른가"가 아니라 "최악의 경우에도 마감을 지키는가"를 따집니다.

이 토픽에서는 주기적으로 반복되는 작업(주기 작업)을 대상으로 한 두 가지 대표 알고리즘을 봅니다. RM(Rate Monotonic)은 주기가 짧을수록 높은 고정 우선순위를 주고, EDF(Earliest Deadline First)는 마감이 가장 가까운 작업에 가장 높은 우선순위를 줍니다. 같은 작업 집합을 두 방식으로 실행해 어느 쪽이 마감을 지키는지, 그리고 CPU 이용률이 어디까지 올라가도 보장되는지 비교합니다.

핵심 개념

경성 · 연성 실시간

마감을 반드시 보장해야 하는 것이 경성, 응답 시간의 통계적 분포가 허용 범위 안이면 되는 것이 연성입니다. 경성 실시간일수록 최악의 경우를 미리 분석해야 합니다.

핵심 개념

주기 작업 모델

작업마다 한 번 실행에 필요한 최대 시간(C)과 반복 주기(P)가 있고, 마감은 흔히 다음 주기가 시작될 때로 둡니다. 이용률은 C/P를 모든 작업에 대해 더한 값입니다.

핵심 개념

RM (Rate Monotonic)

주기가 짧은(자주 실행되는) 작업일수록 높은 우선순위를 주는 고정 우선순위 방식입니다. 우선순위가 한 번 정해지면 바뀌지 않아 구현과 분석이 단순합니다.

핵심 개념

EDF (Earliest Deadline First)

실행 가능한 작업 가운데 마감이 가장 가까운 것을 먼저 실행합니다. 작업이 도착할 때마다 우선순위를 다시 정하는 동적 우선순위 방식입니다.

핵심 개념

이용률 한계

CPU 이용률이 한계 이하이면 마감을 보장할 수 있습니다. 주기와 마감이 같고 작업이 독립인 선점형 단일 프로세서에서 EDF의 한계는 100%이고, RM은 작업 수 n에 따라 n(2^(1/n) − 1)이며 작업이 많아지면 약 69%로 줄어듭니다.

핵심 개념

선점과 마감 실패

실시간 스케줄링은 높은 우선순위 작업이 낮은 작업을 선점할 수 있다고 가정합니다. 선점으로도 감당하지 못하면 어떤 작업이 마감을 넘기게 됩니다.

빠르다가 아니라 예측 가능하다 — 실시간의 핵심은 최악의 경우에도 마감을 지키는 것이고, 평균 속도는 부차적입니다.
RM은 고정, EDF는 동적 — RM의 우선순위는 주기로 한 번 정하면 끝이고, EDF는 시점마다 가까운 마감을 따라 바뀝니다.
EDF가 더 높은 이용률을 쓴다 — 이론적으로 EDF는 CPU를 100%까지 쓰는 작업 집합도 소화하지만 RM의 보장 한계는 그보다 낮습니다.
RM은 단순해서 널리 쓰입니다 — 고정 우선순위만 지원해도 구현할 수 있어, 우선순위 스케줄러만 있는 시스템에서 자주 쓰는 방식입니다.
이 시각화에서 확인할 것 — "RM" 탭에서 같은 작업이 마감을 놓치는 과정을, "EDF" 탭에서 마감을 모두 지키는 과정을, "이용률 한계" 탭에서 두 알고리즘의 보장 한계를 STEP으로 확인하세요.

실시간 스케줄링의 배경

RM과 EDF는 1973년 Liu와 Layland가 논문 "Scheduling Algorithms for Multiprogramming in a Hard-Real-Time Environment"에서 분석해 널리 알려졌습니다. 이 논문은 단일 프로세서에서 주기 작업을 대상으로 두 알고리즘의 최적성과 이용률 한계를 보였고(논문에서는 EDF를 deadline driven 알고리즘이라 불렀습니다), 이후 실시간 스케줄링 이론의 대표적인 기초가 되었습니다.

숫자로 확인하기

작업 집합 예시 — T1은 실행 시간 2에 주기 5, T2는 실행 시간 4에 주기 7입니다. 이용률은 2/5 + 4/7 ≈ 97.1%이고, 마감은 각 작업의 다음 주기 시작 시점입니다(시간 단위는 예시).
RM으로 실행 — T1이 높은 우선순위라 t=0~2, t=5~7에 실행하고, T2는 t=2~5에 3단위만 끝낸 채 t=7이 마감입니다. 남은 1단위를 t=7~8에 늦게 끝내 t=14까지 마감 실패가 1번 생깁니다. 이용률 97.1%는 RM의 보장 한계인 약 82.8%(작업 2개)를 넘습니다.
EDF로 실행 — t=5에 T1의 두 번째 작업이 도착해도 마감이 10이라 마감 7인 T2가 계속 실행되고, T2는 t=6에 끝나 마감을 지킵니다. t=14까지 마감 실패가 없습니다.
RM의 한계 값 — n(2^(1/n) − 1)은 n=1이면 100%, n=2이면 약 82.8%, n=3이면 약 78.0%이고, n이 커지면 ln 2 ≈ 69.3%에 가까워집니다.

자주 하는 오해

실시간 시스템은 빠른 시스템이다 — 빠른 것이 아니라 마감을 예측 가능하게 지키는 시스템입니다. 느린 장치라도 마감 안에 끝나면 실시간 요구를 만족합니다.
EDF가 항상 RM보다 낫다 — 이용률 면에서는 EDF가 유리하지만, 마감 정보를 직접 다뤄야 해서 마감을 지원하지 않는 커널에서는 고정 우선순위만 있으면 되는 RM이 구현하기 쉽습니다. 과부하일 때 RM은 가장 높은 우선순위 작업이 낮은 작업의 초과 실행에 영향받지 않지만, EDF는 초과 실행이 어느 작업의 마감 실패로 이어질지 퍼집니다. 둘 다 과부하 대책이 따로 필요합니다.
RM 이용률 한계를 넘으면 반드시 마감을 놓친다 — 한계는 보장하는 충분 조건이어서 넘어도 마감을 지킬 수 있습니다. 다만 한계를 넘으면 보장이 없어 별도의 분석(응답 시간 분석 등)이 필요합니다.
우선순위가 높으면 항상 먼저 끝난다 — 락 같은 공유 자원을 쓰면 높은 작업이 낮은 작업을 기다리는 우선순위 역전이 생겨 이론의 가정이 깨질 수 있습니다.

면접에서 이렇게 물어요

면접 질문

RM과 EDF의 차이는 무엇인가요?

RM은 주기가 짧을수록 높은 우선순위를 고정으로 주고, EDF는 마감이 가까운 작업에 그때그때 높은 우선순위를 줍니다. EDF는 단일 프로세서에서 이용률 100%까지 보장하고 RM은 그보다 낮은 한계까지 보장합니다.

면접 질문

경성과 연성 실시간은 어떻게 다른가요?

경성은 마감을 반드시 지켜야 하는 경우이고, 연성은 마감을 가끔 놓쳐도 응답 시간의 분포가 허용 범위이면 되는 경우입니다. 항공기 제어는 경성에, 영상 재생은 연성에 가깝습니다.

면접 질문

CPU 이용률은 어떻게 구하나요?

각 작업의 실행 시간을 주기로 나눈 값(C/P)을 모든 작업에 대해 더합니다. 이 값을 알고리즘의 이용률 한계와 비교해 스케줄 가능성을 판단합니다.

면접 질문

일반 스케줄러와 무엇이 다른가요?

일반 스케줄러는 평균 응답 시간과 공정성이 목표이고, 실시간 스케줄러는 최악의 경우에 마감을 지키는 것이 목표입니다. 그래서 실시간은 작업 시간과 주기를 알고 우선순위를 엄격하게 정합니다.

실제 시스템에서는

리눅스 실시간 정책 — SCHED_FIFO와 SCHED_RR은 정적 우선순위(1~99)를 쓰는 실시간 정책이어서, 개발자가 작업의 주기에 따라 우선순위를 정하면 RM을 구현할 수 있습니다.
리눅스 SCHED_DEADLINE — 작업마다 runtime · deadline · period를 지정하면 EDF(전역 EDF)와 CBS(Constant Bandwidth Server)를 기반으로 스케줄링하고, 요청한 대역폭이 수용 가능한지 검사한 뒤 받아들입니다(리눅스 3.14부터).
실시간 운영체제(RTOS) — FreeRTOS 같은 RTOS는 고정 우선순위 선점형 스케줄러를 제공합니다. 우선순위를 주기에 따라 정하면 RM 방식으로 쓸 수 있습니다.
PREEMPT_RT — 리눅스 커널 안에서 선점 가능한 구간을 늘려 최악의 지연을 줄이는 패치 계열이며, 리눅스 6.12부터 주류 커널에서 설정으로 켤 수 있습니다(지원 아키텍처 한정).

함께 보면 좋은 토픽

CPU 스케줄링 — 선점과 우선순위 스케줄링의 기본을 봅니다.
우선순위 역전 — 실시간 시스템에서 락 때문에 마감이 깨지는 문제를 봅니다.
컨텍스트 스위칭 — 선점이 일어날 때 드는 전환 비용을 봅니다.

참고 자료

논문 — Liu, Layland, "Scheduling Algorithms for Multiprogramming in a Hard-Real-Time Environment", Journal of the ACM 20(1), 1973
비교 연구 — Buttazzo, "Rate Monotonic vs. EDF: Judgment Day", Real-Time Systems 29, 2005

C (POSIX) — 실시간 우선순위로 RM 구현

#include <sched.h>
#include <stdio.h>
#include <stdlib.h>

/* 주기가 짧은 작업일수록 큰 우선순위 값 (RM) */
int set_rm_priority(int prio) {
    struct sched_param sp = { .sched_priority = prio };
    if (sched_setscheduler(0, SCHED_FIFO, &sp) == -1) {
        perror("sched_setscheduler");   /* 권한이 없으면 실패 */
        return -1;
    }
    return 0;
}

int main(int argc, char **argv) {
    /* ./task 60 (T1, 주기 5), ./task 50 (T2, 주기 7) */
    int prio = argc > 1 ? atoi(argv[1]) : 50;
    return set_rm_priority(prio);
}

sched_setscheduler는 대상(0이면 호출한 스레드, 단일 스레드 프로세스라면 자기 자신)의 스케줄링 정책과 우선순위를 정합니다. SCHED_FIFO는 정적 우선순위를 쓰는 실시간 정책이라, 주기가 짧은 작업에 더 큰 값을 주면 RM과 같은 우선순위 배치가 됩니다. SCHED_FIFO 우선순위는 리눅스에서 1~99이고, 실시간 정책을 쓰려면 CAP_SYS_NICE 권한(보통 root)이 있거나 RLIMIT_RTPRIO 한도 안이어야 합니다. 마감을 확인하는 기능은 이 정책에 들어 있지 않아, 우선순위만 맞게 정하는 것은 개발자의 몫입니다. 위 코드는 두 프로세스를 각각 실행하는 상황을 보이는 최소 예시입니다.

C (리눅스) — SCHED_DEADLINE으로 EDF 사용

#define _GNU_SOURCE
#include <sched.h>
#include <stdint.h>
#include <sys/syscall.h>
#include <unistd.h>

#ifndef SCHED_DEADLINE
#define SCHED_DEADLINE 6
#endif

struct sched_attr {
    uint32_t size;
    uint32_t sched_policy;
    uint64_t sched_flags;
    int32_t  sched_nice;
    uint32_t sched_priority;
    uint64_t sched_runtime;    /* 주기마다 보장받을 실행 시간(ns) */
    uint64_t sched_deadline;   /* 상대 마감(ns) */
    uint64_t sched_period;     /* 주기(ns) */
};

int main(void) {
    struct sched_attr attr = {
        .size           = sizeof(attr),
        .sched_policy   = SCHED_DEADLINE,
        .sched_runtime  = 2 * 1000 * 1000,   /* 2ms */
        .sched_deadline = 5 * 1000 * 1000,   /* 5ms */
        .sched_period   = 5 * 1000 * 1000,   /* 5ms */
    };
    return syscall(SYS_sched_setattr, 0, &attr, 0) == -1;
}

SCHED_DEADLINE은 작업마다 runtime, deadline, period를 정해 주면 커널이 EDF 방식으로 스케줄링하는 리눅스의 정책입니다. 주기(period)마다 runtime만큼의 CPU 시간을 deadline 안에 받는 것을 목표로 하며, runtime ≤ deadline ≤ period 관계가 필요합니다. 요청한 대역폭(runtime ÷ period)의 합이 커널이 허용하는 상한(기본은 CPU당 95%)을 넘으면 커널이 수용 검사에서 거절합니다. 이 검사는 필요 조건이라 통과해도 모든 마감이 보장되는 것은 아니며, 이 정책은 CAP_SYS_NICE 권한이 필요합니다. glibc 2.40 이하처럼 sched_setattr 래퍼와 sched_attr 정의가 없는 환경을 기준으로 직접 정의하고 syscall로 부르는 최소 예시입니다. glibc 2.41 이상은 sched.h가 sched_attr 구조체와 sched_setattr 함수를 제공하므로 직접 정의하지 말고 그대로 부릅니다. 실제 사용은 sched(7) 문서를 따라야 합니다.

Python — 이용률과 한계 계산

tasks = [(2, 5), (4, 7)]   # (실행 시간 C, 주기 P)

u = sum(c / p for c, p in tasks)
n = len(tasks)
rm_bound = n * (2 ** (1 / n) - 1)

print(f"U = {u:.3f}, RM 한계 = {rm_bound:.3f}")
print("RM 마감 보장:", u <= rm_bound)    # False (보장 없음)
print("EDF 마감 보장:", u <= 1)          # True

이용률 U는 작업별 실행 시간을 주기로 나눈 값의 합이고, 이 예시는 2/5 + 4/7 ≈ 0.971입니다. RM의 한계 n(2^(1/n) − 1)은 작업이 2개일 때 약 0.828이라 U가 이를 넘고, EDF의 한계는 1이라 U가 이를 넘지 않습니다. RM 한계를 넘었다고 반드시 마감을 놓치는 것은 아니고 보장이 없다는 뜻이며, 이 시각화의 작업 집합에서는 실제로 RM 실행이 마감을 놓칩니다. 이 검사는 주기와 마감이 같고 작업이 서로 독립이며 선점 가능한 단일 프로세서라는 가정에서만 성립하므로, 락이나 여러 코어가 있는 시스템에서는 더 자세한 분석이 필요합니다.

Q1

Rate Monotonic(RM) 스케줄링은 우선순위를 어떻게 정할까?

Rate(발생 빈도)가 높은 작업이 우선입니다.
RM은 주기가 짧을수록(자주 실행될수록) 높은 고정 우선순위를 부여합니다. 마감 시간이 가까운 작업에 높은 우선순위를 주는 것은 EDF입니다.

Q2

EDF(Earliest Deadline First) 스케줄링의 규칙은?

이름 그대로 "가장 이른 마감 우선"입니다.
EDF는 매 시점마다 마감 시간이 가장 임박한 작업을 먼저 실행하는 동적 우선순위 방식입니다. 작업이 바뀔 때마다 우선순위가 다시 계산됩니다.

Q3

단일 프로세서에서 EDF가 RM보다 이론적으로 유리한 점은?

RM은 이용률이 어느 정도 이하일 때만 보장됩니다.
EDF는 주기와 마감이 같은 작업 집합에서 CPU 이용률이 100% 이하이면 마감을 모두 지킬 수 있는 최적 알고리즘입니다. RM은 이용률이 이보다 낮은 한계(작업이 많을 때 약 69%) 이하일 때 보장됩니다.