jongkwan.dev
개발 · Essay №027

프로세스와 CPU 스케줄링

프로세스 상태와 PCB부터 FCFS·SJF·라운드로빈·CFS까지, 운영체제가 CPU를 나눠 주는 규칙을 정리합니다.

이종관2026년 1월 13일17 min read
Contents

CPU 코어는 한순간에 한 작업만 실행하지만, 운영체제는 프로세스 상태와 스케줄러로 수백 개가 동시에 도는 것처럼 보이게 합니다.

프로세스와 PCB

프로세스는 실행 중인 프로그램입니다. 같은 실행 파일이라도 두 번 띄우면 별개의 프로세스가 되고, 각자 독립된 메모리 공간과 실행 흐름을 가집니다. 운영체제는 이 프로세스 하나하나를 PCB(Process Control Block)라는 자료구조로 추적합니다.

PCB는 프로세스를 잠시 멈췄다가 나중에 정확히 같은 지점에서 이어 실행하기 위한 정보를 담습니다. 다음은 PCB의 대표 필드입니다.

필드담는 정보
Process ID (PID)프로세스 고유 식별자
Process State현재 상태 (Ready, Running 등)
Program Counter다음에 실행할 명령어 주소
CPU Registers레지스터 값 (멈출 때 저장, 재개할 때 복원)
Memory Info페이지 테이블, 세그먼트 정보
I/O Status열린 파일, 할당된 입출력 장치
Scheduling Info우선순위, 스케줄링 큐 포인터

이 표에서 핵심은 Program Counter와 CPU Registers입니다. 이 둘만 저장해 두면 멈췄던 프로세스를 같은 지점에서 다시 시작할 수 있고, 이것이 뒤에서 다룰 컨텍스트 스위칭의 바탕이 됩니다.

프로세스의 다섯 가지 상태

프로세스는 생성부터 종료까지 다섯 상태를 오갑니다. 상태가 바뀌는 시점은 정해진 사건에서만 일어납니다.

위 그림은 프로세스의 상태 전이를 화살표로 보여 줍니다. 화살표에 붙은 이름이 전이를 일으키는 사건입니다.

상태설명백엔드 예시
New프로세스 생성 중fork() 시스템 콜 직후
ReadyCPU 할당 대기요청 처리 준비 완료, 실행 큐에서 대기
RunningCPU에서 실행 중HTTP 요청 처리 로직 실행
WaitingI/O 완료 대기DB 쿼리 응답 대기, 파일 읽기 대기
Terminated실행 완료응답 전송 후 프로세스 종료

여기서 주의할 전이는 Running에서 Ready로 돌아가는 경로입니다. 작업이 끝나서가 아니라 타이머 인터럽트로 CPU를 빼앗기는 것이고, 이 강제 회수가 한 프로세스의 독점을 막습니다. 반면 I/O를 기다릴 때는 Waiting으로 빠져 CPU를 자발적으로 내놓습니다.

컨텍스트 스위칭

컨텍스트 스위칭은 실행 중인 프로세스의 상태를 PCB에 저장하고, 다음 프로세스의 상태를 PCB에서 복원하는 과정입니다. 여기서 컨텍스트란 레지스터 값과 Program Counter처럼 실행을 이어 가는 데 필요한 정보를 말합니다.

스위칭은 다음 네 시점에서 일어납니다.

  • 타임 슬라이스 만료: 선점형 스케줄링에서 할당 시간을 다 쓴 경우
  • I/O 요청: 프로세스가 입출력을 요청해 Waiting으로 전환되는 경우
  • 인터럽트: 하드웨어나 소프트웨어 인터럽트가 발생한 경우
  • 시스템 콜: 커널 모드 전환이 필요한 작업을 호출한 경우

문제는 이 전환이 그 자체로는 아무 일도 처리하지 않는 순수 오버헤드라는 점입니다. 비용은 다음 요소에서 발생합니다.

비용 요소내용
레지스터 저장과 복원수십 개의 CPU 레지스터를 메모리에 저장했다가 다시 로드
TLB 플러시프로세스마다 가상 주소 공간이 다르므로 주소 변환 캐시를 무효화
캐시 오염새 프로세스 데이터가 L1·L2·L3 캐시에 올라오며 이전 데이터를 밀어냄
파이프라인 플러시CPU 파이프라인에서 실행 중이던 명령어를 폐기

TLB는 Translation Lookaside Buffer의 약자로, 가상 주소를 물리 주소로 바꾼 결과를 캐싱해 변환 비용을 줄이는 하드웨어입니다. 프로세스가 바뀌면 주소 공간이 달라져 이 캐시를 비워야 하고, 이것이 스위칭 비용의 큰 부분을 차지합니다. 한 번의 컨텍스트 스위칭 비용은 보통 1~10마이크로초 수준으로 추정됩니다. 초당 수만 번 스위칭이 일어나면 전체 CPU 시간의 적지 않은 몫이 오버헤드로 빠져나갑니다.

프로세스와 스레드

스레드는 한 프로세스 안에서 실행 흐름만 따로 가진 단위입니다. 같은 프로세스의 스레드들은 메모리를 공유하므로, 스레드 전환은 주소 공간이 바뀌지 않아 프로세스 전환보다 쌉니다.

특성프로세스스레드
메모리 공간독립적 (각자의 주소 공간)공유 (같은 프로세스의 힙·데이터)
스택독립적스레드마다 독립적
생성 비용높음 (메모리 복제)낮음 (스택만 새로 할당)
컨텍스트 스위칭높음 (TLB 플러시 필요)낮음 (같은 주소 공간)
통신IPC 필요 (파이프, 공유 메모리)직접 메모리 접근
안정성하나가 죽어도 다른 프로세스는 무관하나가 죽으면 전체 프로세스가 종료될 수 있음

표의 마지막 두 줄이 선택의 기준입니다. 빠른 통신과 낮은 전환 비용이 필요하면 스레드가, 장애 격리가 중요하면 프로세스가 유리합니다. 스레드 풀이 자주 쓰이는 이유도 매 요청마다 스레드를 새로 만드는 대신 만들어 둔 스레드를 재사용해 생성·전환 비용을 줄이기 위해서입니다.

CPU 스케줄링이 답하는 질문

스케줄러는 Ready 큐에 쌓인 여러 프로세스 중 다음에 어떤 것을 실행할지 고릅니다. 무엇이 좋은 선택인지는 측정 기준에 따라 달라집니다.

기준의미방향
CPU 이용률CPU가 실제 작업을 수행하는 비율최대화
처리량단위 시간당 완료되는 프로세스 수최대화
대기 시간Ready 큐에서 기다린 총 시간최소화
응답 시간요청 제출부터 첫 반응까지 시간최소화
반환 시간제출부터 완료까지 걸린 총 시간최소화

이 기준들은 한꺼번에 만족시키기 어렵습니다. 대화형 서비스는 응답 시간을, 배치 작업은 처리량과 반환 시간을 더 중시하므로, 정책은 목표에 맞춰 고릅니다.

스케줄링은 크게 두 부류로 나뉩니다. 비선점형은 실행 중인 프로세스가 스스로 CPU를 내놓을 때까지 기다리고, 선점형은 타이머로 실행 중인 프로세스를 강제로 멈출 수 있습니다. 선점형은 응답성이 좋은 대신 컨텍스트 스위칭이 자주 일어납니다.

스케줄링 알고리즘

대표 정책은 선택 규칙 하나씩으로 요약됩니다. 각 규칙이 어떤 문제를 낳는지가 비교의 핵심입니다.

  • FCFS(First Come First Served): 도착 순서대로 CPU를 줍니다. 긴 작업이 앞에 오면 뒤의 짧은 작업이 길게 대기하는 호위 효과(Convoy Effect)가 생깁니다.
  • SJF(Shortest Job First): 남은 실행 시간이 가장 짧은 프로세스를 먼저 실행합니다. 평균 대기 시간이 이론상 가장 짧지만, 실행 시간을 미리 알기 어렵고 긴 작업이 계속 밀리는 기아(Starvation)가 생깁니다.
  • 라운드로빈(Round Robin): 각 프로세스에 같은 타임 퀀텀(Time Quantum)을 주고 돌아가며 실행합니다. 퀀텀이 너무 작으면 스위칭 오버헤드가 커지고, 너무 크면 FCFS와 같아집니다. 흔히 10~100밀리초 범위로 둡니다.
  • 우선순위 스케줄링: 우선순위가 높은 프로세스를 먼저 실행합니다. 낮은 우선순위가 영영 실행되지 못하는 기아가 생기는데, 대기 시간이 길어질수록 우선순위를 점차 올리는 에이징(Aging)으로 완화합니다.

기아는 SJF와 우선순위 스케줄링이 공통으로 안고 있는 약점이고, 에이징은 그 약점을 메우는 보정 장치입니다.

현대 운영체제 스케줄러의 바탕은 MLFQ(Multilevel Feedback Queue)입니다. 우선순위가 다른 여러 Ready 큐를 두고, 프로세스의 행동을 보고 큐 사이를 옮깁니다.

위 그림처럼 CPU를 오래 쓰는 작업은 아래 큐로 내려가고, I/O로 자주 양보하는 대화형 작업은 위 큐에 남습니다. 작업 종류를 미리 선언하지 않아도 행동만 보고 분류한다는 점이 MLFQ의 장점입니다.

같은 작업을 세 정책으로

세 프로세스로 정책별 차이를 계산합니다. A는 도착 0에 실행 시간 5, B는 도착 1에 실행 3, C는 도착 2에 실행 1이 필요합니다. 라운드로빈 퀀텀은 2로 둡니다.

정책실행 구간
FCFSA(0-5), B(5-8), C(8-9)
SJF (비선점)A(0-5), C(5-6), B(6-9)
라운드로빈 (q=2)A(0-2), B(2-4), C(4-5), A(5-7), B(7-8), A(8-9)

반환 시간은 완료에서 도착을 뺀 값, 응답 시간은 첫 실행에서 도착을 뺀 값입니다. 위 구간으로 계산하면 아래 표가 나옵니다.

정책반환 시간 (A/B/C)평균 반환응답 시간 (A/B/C)평균 응답
FCFS5 / 7 / 76.330 / 4 / 63.33
SJF5 / 8 / 45.670 / 5 / 32.67
라운드로빈9 / 7 / 36.330 / 1 / 21.00

같은 입력인데 결과가 갈립니다. SJF는 평균 반환 시간이 가장 짧아 처리량 관점에서 유리하고, 라운드로빈은 평균 응답 시간이 가장 짧아 모든 프로세스가 빨리 첫 실행을 맞습니다. 반대로 라운드로빈에서 A의 반환 시간이 9까지 늘어난 것은, 짧은 작업에 자리를 자주 내준 대가입니다. 어떤 정책이 옳은지는 응답성과 처리량 중 무엇을 우선하느냐로 정해집니다.

리눅스 CFS

리눅스의 오랜 기본 스케줄러는 CFS(Completely Fair Scheduler)입니다. CFS는 가상 실행 시간(vruntime)을 기준으로 CPU를 가장 적게 쓴 프로세스에 먼저 차례를 줍니다. 고정된 큐 순서 대신 누가 덜 받았는지를 기준으로 삼아 공정성을 맞춥니다.

우선순위는 nice 값으로 조절합니다. nice 값이 낮을수록 가중치가 커지고, 같은 시간을 실행해도 vruntime이 천천히 올라 더 자주 선택됩니다. 다음은 리눅스 커널의 가중치 테이블 기준 값입니다.

nice 값가중치상대 CPU 점유
-20 (최고)88761최대
0 (기본)1024기본
+19 (최저)15최소

실무에서는 nice -n -5 node server.js처럼 서버 프로세스에 낮은 nice 값을 줘 CPU 우선순위를 높일 수 있습니다. 리눅스 6.6부터 기본 스케줄러는 EEVDF(Earliest Eligible Virtual Deadline First)로 바뀌었습니다. 다만 가상 실행 시간을 공정성의 잣대로 삼는 발상은 그대로 이어집니다.

정리

프로세스는 PCB로 추적되고 다섯 상태를 오가며, 상태 전이는 타이머 인터럽트나 I/O 같은 정해진 사건에서만 일어납니다. 컨텍스트 스위칭은 그 자체로는 일을 처리하지 않는 순수 오버헤드이므로, 스레드 풀처럼 전환 횟수를 줄이는 설계가 중요합니다. 스케줄링 정책은 응답 시간과 반환 시간 같은 기준 사이의 절충이며, 같은 입력도 FCFS·SJF·라운드로빈에서 서로 다른 결과를 냅니다. 리눅스 CFS와 그 후속인 EEVDF는 가상 실행 시간으로 이 절충을 공정성 쪽으로 풉니다. 어떤 정책이 맞는지는 시스템이 응답성과 처리량 중 무엇을 우선하는지에 달려 있습니다.