프로세스 인터리빙과 시분할 시스템의 실행 순서

프로세스 인터리빙의 시분할 실행 원리와 문맥 교환, 경쟁 조건, 스케줄링 방식, 동시성 제어 기법을 정리한다.

2026-08-14 · 최초 발행 2026-01-04

CPU를 나눠 쓰는 방식

단일 프로세서에서 여러 프로세스가 동시에 돌아가는 듯 보이는 이유는 CPU가 한 작업을 끝까지 수행한 뒤 다음 작업으로 넘어가는 대신, 각 작업의 명령어를 짧게 교차해 실행하기 때문이다. 이 실행 방식이 프로세스 인터리빙이며 시분할 시스템의 기반이다.

인터리빙은 여러 프로세스의 명령어가 섞여 실행되는 구조다. CPU 시간은 작은 단위로 나뉘어 각 프로세스에 배정되고, 실제 실행은 순차적이지만 사용자는 병렬 실행과 비슷한 반응을 경험한다. 이때 프로세스를 전환하려면 기존 실행 상태를 보관하고 다음 프로세스의 상태를 되살리는 문맥 교환이 필요하다.

실행 방식의 차이는 다음과 같다.

실행 방식순차 실행 (Sequential)인터리빙 (Interleaved)병렬 실행 (Parallel)P1 완료 P2 시작 실행 시간 = T1 + T2P1, P2 교차 실행응답 시간 개선P1, P2 동시 실행 실행 시간 = max(T1, T2)

순차 실행에서는 한 프로세스가 끝난 뒤 다음 프로세스가 시작한다.

P1: [========P1========]
P2:                     [========P2========]
Time: 0-----------------10------------------20

인터리빙에서는 각 프로세스가 실행 구간을 번갈아 차지한다.

P1: [==P1==]  [==P1==]  [==P1==]  [==P1==]
P2:   [==P2==]  [==P2==]  [==P2==]  [==P2==]
Time: 0--2--4--6--8--10--12--14--16--18--20

스케줄러와 문맥 교환이 만드는 실행 전환

타임 슬라이스는 프로세스 하나에 할당되는 CPU 시간 조각이다. 일반적 크기는 10~100ms이며 시스템에 따라 달라진다. 이 값이 지나치게 작으면 문맥 교환 오버헤드가 커지고, 반대로 너무 크면 응답 시간이 떨어져 대화식 시스템에 맞지 않게 된다.

단기 스케줄러는 준비 큐에서 다음에 실행할 프로세스를 고른다. 디스패처는 그 프로세스에 CPU 제어권을 넘기며, 이 과정에서 상태 저장과 복원이 수행된다. Round-Robin이나 Priority-based 방식 같은 스케줄링 알고리즘은 이 선택 순서에 영향을 준다.

Process P1 실행인터럽트 발생P1 상태 저장PC, 레지스터, 메모리스케줄러 호출다음 프로세스 P2 선택P2 상태 복원Process P2 실행

문맥 교환 시에는 다음 상태가 저장되거나 복원된다.

  • 프로그램 카운터(PC): 다음에 실행할 명령어 주소
  • 레지스터 세트: 범용 및 특수 레지스터 값
  • 프로세스 상태: Running, Ready, Waiting
  • 메모리 관리 정보: 페이지 테이블, 세그먼트 테이블
  • I/O 상태 정보: 열린 파일, 네트워크 연결

공유 자원에서 실행 순서가 문제가 되는 지점

인터리빙 자체는 CPU 활용을 위한 방식이지만, 공유 데이터를 여러 실행 흐름이 함께 변경하면 원자성이 깨질 수 있다. 예를 들어 같은 balance 값을 동시에 읽고 수정하는 두 프로세스를 생각할 수 있다.

// 공유 변수
int balance = 1000;

// Process 1
balance = balance + 100;

// Process 2
balance = balance - 50;

명령어 수준에서 실행 순서가 교차하면 다음과 같은 결과가 나온다.

T1: P1 읽기: R1 = balance (1000)
T2: P2 읽기: R2 = balance (1000)
T3: P1 계산: R1 = R1 + 100 (1100)
T4: P2 계산: R2 = R2 - 50 (950)
T5: P1 쓰기: balance = R1 (1100)
T6: P2 쓰기: balance = R2 (950)

결과: balance = 950 (잘못된 결과, 기대값: 1050)

이처럼 실행 순서에 따라 결과가 달라지는 현상이 경쟁 조건이다. 공유 자원에 대한 비원자적 연산이 원인이며, 은행 계좌 동시 입출금, 재고 관리, 좌석 예약 같은 상황에서 나타날 수 있다. Mutex나 Semaphore 같은 동기화 메커니즘으로 임계 영역을 보호한다.

인터리빙은 비결정성도 만든다. 같은 입력에서도 서로 다른 출력이 나올 수 있고, 타이밍 의존적 버그인 Heisenbug는 재현과 디버깅을 어렵게 한다. 가능한 모든 인터리빙 패턴을 검증하기 어렵기 때문에 결정적 재생 도구를 활용해 완화할 수 있다.

동기화로 실행 경계를 세우는 방법

임계 영역은 한 시점에 하나의 실행 흐름만 접근해야 하는 코드 구간이다. Mutex는 이 구간을 보호하는 대표적인 방법이다.

// Mutex를 이용한 임계 영역 보호
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;

void safe_update() {
    pthread_mutex_lock(&lock);

    // Critical Section (원자적 실행 보장)
    balance = balance + 100;

    pthread_mutex_unlock(&lock);
}

원자적 연산은 Compare-and-Swap, Test-and-Set 같은 하드웨어 지원을 이용한다. GCC의 __atomic_*, C11의 _Atomic, java.util.concurrent.atomic, C++의 std::atomic도 활용할 수 있으며 Lock-free 구조와 높은 성능이 특징이다.

트랜잭션 메모리는 충돌을 자동으로 감지하고 롤백하는 방식이다. 소프트웨어 TM(STM)은 런타임 라이브러리로 구현하며, 하드웨어 TM(HTM)의 예로 Intel TSX와 IBM Power가 있다. 다만 하드웨어 지원 범위가 제한적이고 abort 처리가 복잡하다.

스케줄링 정책이 바꾸는 인터리빙

Round-Robin은 모든 프로세스에 동일한 CPU 시간을 배정한다.

시간 P1 P2 P3
0-10 실행 대기 대기
10-20 대기 실행 대기
20-30 대기 대기 실행
30-40 실행 대기 대기

이 방식에서는 n개 프로세스일 때 최대 응답 시간이 n × quantum이며, 문맥 교환 오버헤드 때문에 처리량은 다소 감소할 수 있다. 시분할 시스템과 대화식 환경에 적용된다.

Priority-based Scheduling은 우선순위 큐에서 높은 우선순위 프로세스를 먼저 실행한다. 선점형 방식에서는 높은 우선순위 프로세스가 도착하면 즉시 전환할 수 있다. 낮은 우선순위 프로세스가 무한히 대기하는 기아 문제는 시간에 따라 우선순위를 높이는 Aging으로 완화한다.

Multilevel Queue는 포그라운드의 대화식 작업과 백그라운드의 배치 작업을 분리한다. 포그라운드에는 RR, 백그라운드에는 FCFS를 적용할 수 있고, 큐 사이에는 고정 우선순위 또는 시간 할당을 사용한다. Linux CFS의 여러 스케줄링 클래스가 예시다.

운영체제가 적용하는 방식

Linux의 CFS(Completely Fair Scheduler)는 프로세스에 공정한 CPU 시간을 제공하기 위해 가상 런타임인 vruntime을 사용한다. vruntime은 실제 실행 시간을 가중치로 정규화한 값이며, Red-Black Tree에서 가장 작은 vruntime을 선택해 실행한다.

CFS (Completely FairScheduler)가상 런타임 (vruntime)Red-Black Tree 관리가장 작은 vruntime 선택프로세스 실행vruntime 증가

CFS는 너무 작은 타임 슬라이스를 막기 위한 최소 세분성(Granularity)을 두며, Nice 값은 -20 ~ +19 범위에서 우선순위를 조정한다.

Windows 스케줄러는 0~31 우선순위 레벨을 사용하는 우선순위 기반 구조다. Multi-Level Feedback Queue로 동적 우선순위를 조정하며, 대화식 작업에는 짧은 Quantum, 배치 작업에는 긴 Quantum을 둔다. CPU Affinity도 고려한다.

RTOS는 최악 실행 시간을 보장하는 결정적 응답 시간이 필요하다. 엄격한 선점형 우선순위와 인터럽트 지연 최소화, 빠른 컨텍스트 스위칭이 핵심이며 VxWorks, FreeRTOS, QNX가 예시다.

전환 비용과 동시성 코드를 다룰 때

문맥 교환 비용을 줄이려면 프로세스보다 가벼운 컨텍스트인 쓰레드를 사용할 수 있다. 사용자 수준 쓰레드는 커널 개입 없이 전환하며, 파이버와 코루틴은 명시적 양보와 스택 전환만으로 동작한다. 같은 CPU에 다시 스케줄링해 캐시 친화성을 높이는 방법도 있다.

타임 슬라이스는 I/O bound와 CPU bound 워크로드를 분석해 조정한다. quantum을 동적으로 바꾸거나 대화식 작업과 배치 작업에 차등을 둘 수 있으며, perf, sar, vmstat로 측정한다.

Lock-free 프로그래밍에서는 Compare-and-Swap 기반 CAS 루프와 Queue, Stack, List 같은 Lock-free 자료구조를 다룬다. 메모리 순서에서는 Acquire/Release 시맨틱이 필요하고, ABA 문제는 Hazard Pointer나 Epoch-based Reclamation으로 대응한다.

재현하기 어려운 버그를 추적하는 도구

동시성 버그 탐지에는 Clang/GCC의 ThreadSanitizer, Valgrind의 쓰레드 오류 검사기인 Helgrind, 상용 동시성 분석 도구인 Intel Inspector, Visual Studio 타임라인 분석 도구인 Concurrency Visualizer를 사용할 수 있다.

재현과 검증에서는 Mozilla의 결정적 디버거인 rr(Record and Replay), Deterministic Multi-Threading, SPIN과 TLA+를 이용한 Model Checking, 다양한 타이밍 조건을 다루는 Fuzzing이 활용된다.

프로세스 인터리빙은 제한된 CPU 자원으로 여러 작업을 처리하는 핵심 방식이다. 스케줄링 알고리즘은 실행 순서와 응답성을 결정하고, 동기화 메커니즘은 공유 자원의 정확성을 지킨다. 멀티코어 시스템에서도 프로세서 내부의 하드웨어 쓰레드 관리에는 유사한 원리가 적용된다.

프로세스 인터리빙시분할운영체제문맥 교환동시성