본문으로 건너뛰기

안동민 개발노트

본문 시작

PCB와 컨텍스트 스위칭

PCB에 보존되는 실행 상태를 살펴보고 프로세스 전환이 레지스터·페이지 테이블·캐시에 만드는 비용을 분석합니다.

OS는 수십, 수백 개의 프로세스를 동시에 관리합니다.

각 프로세스의 상태, CPU 레지스터 값, 메모리 정보, 열린 파일 목록을 어딘가에 기록해야 합니다.

프로세스 A에서 B로 전환한 후, 다시 A로 돌아왔을 때 A가 이전에 실행하던 그 지점에서 정확히 이어서 실행되어야 하니까요.

이 정보를 담는 자료구조가 PCB(Process Control Block)이고, 프로세스를 전환하는 과정이 컨텍스트 스위칭(Context Switching)입니다.

PCB와 컨텍스트 스위칭

OS는 수십, 수백 개의 프로세스를 동시에 관리합니다. 각 프로세스의 상태, CPU 레지스터 값, 메모리 정보, 열린 파일 목록을 어딘가에 기록해야 합니다.

  1. 중단된 실행을 복원하는 순서

    PCB는 PID, 상태, 레지스터, 메모리 정보처럼 스케줄러가 프로세스를 다시 이어 실행하는 데 필요한 값을 담습니다. 2 컨텍스트 스위칭(Context Switching)은 CPU에서 실행 중인 프로세스를 다른 프로세스로 교체하는 과정입니다. 3 컨텍스트 스위칭은 순수한 오버헤드입니다. 4 OS는 프로세스들을 여러 큐(Queue)로 조직합니다.

  2. PCB의 구조

    PCB는 PID, 상태, 레지스터, 메모리 정보처럼 스케줄러가 프로세스를 다시 이어 실행하는 데 필요한 값을 담습니다.

  3. 컨텍스트 스위칭

    (Context Switching)은 CPU에서 실행 중인 프로세스를 다른 프로세스로 교체하는 과정입니다.

  4. 컨텍스트 스위칭의 오버헤드

    컨텍스트 스위칭은 순수한 오버헤드입니다.

  5. 프로세스 큐

    OS는 프로세스들을 여러 큐(Queue)로 조직합니다.

  6. 저장 누락과 전환 비용을 함께 점검

    PCB에 저장되는 값이 불충분하면 전환 뒤 같은 실행 지점으로 복귀할 수 없으므로 보존 범위가 핵심입니다. 컨텍스트 스위칭은 필요한 상태를 저장하고 복원하지만, 그 시간에는 사용자 작업을 직접 수행하지 못합니다. PCB의 구조 PCB는 OS가 프로세스를 관리하기 위해 유지하는 커널 수준의 데이터 구조입니다.


PCB의 구조

PCB는 OS가 프로세스를 관리하기 위해 유지하는 커널 수준의 데이터 구조입니다.

프로세스가 생성될 때 PCB가 할당되고, 종료될 때 PCB가 해제됩니다.

PCB는 프로세스의 신분증이자 상태 기록부입니다.

Linux 커널에서는 task_struct라는 거대한 구조체가 이 역할을 합니다.

Linux 커널 소스의 include/linux/sched.h에 정의되어 있으며, 수백 개의 필드를 가지고 있습니다.

크기가 수 KB에 달하는 복잡한 구조체입니다.

PCB에 저장되는 주요 정보를 범주별로 정리하면 다음과 같습니다.

식별 정보

  • PID(Process ID): 프로세스를 고유하게 식별하는 양의 정수. Linux에서 기본 최대값은 32768이며, /proc/sys/kernel/pid_max로 확인하고 변경할 수 있습니다.
  • PPID(Parent PID): 이 프로세스를 생성한 부모 프로세스의 PID.
  • UID/GID: 프로세스를 실행한 사용자(User)와 그룹(Group)의 ID. 파일 접근 권한, 시그널 전송 권한 등을 결정합니다.

CPU 상태 정보

  • 프로그램 카운터(PC/IP): 이 프로세스가 다음에 실행할 명령어의 메모리 주소.
  • CPU 레지스터 값: 범용 레지스터(RAX, RBX, RCX, ...), 스택 포인터(RSP), 베이스 포인터(RBP), 플래그 레지스터(RFLAGS) 등의 현재 값. 컨텍스트 스위칭 시 이 값들이 저장되고 복원됩니다.
  • FPU/SIMD 상태: 부동소수점 레지스터, SSE/AVX 레지스터의 값. 멀티미디어나 과학 계산 프로그램에서 중요합니다.

스케줄링 정보

  • 프로세스 상태: Ready, Running, Waiting, Stopped, Zombie 등.
  • 우선순위: 스케줄러가 CPU 할당 순서를 결정하는 데 사용하는 값. Linux에서는 nice 값(-2019)과 실시간 우선순위(099)가 있습니다.
  • CPU 사용 시간: 사용자 모드와 커널 모드에서 소비한 시간을 각각 기록합니다.

메모리 관리 정보

  • 페이지 테이블 포인터: 가상 주소를 물리 주소로 변환하는 페이지 테이블의 위치. x86에서는 CR3 레지스터에 저장됩니다.
  • 메모리 영역 정보: 텍스트, 데이터, 힙, 스택 각 영역의 시작/끝 주소와 접근 권한.

I/O 및 파일 정보

  • 파일 디스크립터 테이블: 프로세스가 열어 놓은 모든 파일에 대한 참조. 0(stdin), 1(stdout), 2(stderr)은 기본으로 열려 있습니다.
  • 현재 작업 디렉토리: pwd 명령어로 보는 경로.
  • 시그널 관련 정보: 등록된 시그널 핸들러, 대기 중인 시그널, 차단된 시그널 마스크.

Linux에서 프로세스의 PCB 정보를 확인하는 가장 쉬운 방법은 /proc/<PID>/ 디렉토리입니다.

가상 파일 시스템은 커널이 실시간으로 생성하는 정보를 파일 형태로 제공합니다.

프로세스 PCB 정보 확인
ls /proc/1/
# cmdline  environ  fd  maps  status  stat  ...

cat /proc/1/status
# Name:   systemd
# State:  S (sleeping)
# Pid:    1
# PPid:   0
# Uid:    0  0  0  0
# VmSize: 173048 kB
# VmRSS:  12940 kB
# Threads: 1

ls -l /proc/1/fd/
# 0 -> /dev/null (stdin)
# 1 -> /dev/null (stdout)
# 2 -> /dev/null (stderr)
# 3 -> socket:[12345]
# 4 -> /var/log/journal/...

VmSize는 프로세스의 가상 메모리 총 크기, VmRSS는 실제 물리 메모리에 상주하는 크기(Resident Set Size)입니다.

top이나 ps에서 보는 메모리 사용량이 바로 이 값입니다.


컨텍스트 스위칭

컨텍스트 스위칭(Context Switching)은 CPU에서 실행 중인 프로세스를 다른 프로세스로 교체하는 과정입니다.

"컨텍스트"란 프로세스가 실행을 재개하는 데 필요한 모든 CPU 상태(레지스터 값, PC, 스택 포인터 등)를 의미합니다.

프로세스 A가 실행 중인데, 타이머 인터럽트가 발생하여 프로세스 B로 전환해야 하는 상황을 단계별로 따라가 보겠습니다.

  1. 인터럽트 발생: 타이머 인터럽트가 CPU에 전달됩니다. CPU가 커널 모드로 전환됩니다.

  2. 현재 컨텍스트 저장: CPU의 현재 레지스터 값(PC, 범용 레지스터, 스택 포인터, 플래그 등)을 프로세스 A의 PCB(task_struct)에 저장합니다. 이 시점에서 A의 스냅샷이 PCB에 캡처됩니다.

  3. 스케줄링 결정: 커널의 스케줄러가 Ready Queue를 확인하여 다음에 실행할 프로세스(B)를 결정합니다. 5장에서 이 결정 알고리즘을 다룹니다.

  4. 메모리 공간 전환: B의 페이지 테이블로 전환합니다. x86에서는 CR3 레지스터에 B의 페이지 테이블 주소를 로드합니다. 이 순간 가상 주소 공간이 A의 것에서 B의 것으로 바뀝니다.

  5. 새 컨텍스트 복원: B의 PCB에서 저장된 레지스터 값들을 CPU에 복원합니다. PC가 B가 마지막으로 실행하던 명령어 주소로 설정됩니다.

  6. 사용자 모드 전환: 커널 모드에서 사용자 모드로 전환하여 프로세스 B의 실행을 재개합니다.

A의 입장에서는 마치 잠깐 멈추었다가 이어서 실행되는 것처럼 보입니다.

컨텍스트 스위칭이 발생했다는 사실 자체를 알지 못합니다.

이것이 가능한 이유는 PCB에 모든 상태가 완벽하게 저장되고 복원되기 때문입니다.


컨텍스트 스위칭의 오버헤드

컨텍스트 스위칭 비용은 저장보다 캐시가 식는 데서 커진다

레지스터 저장은 짧지만 스케줄러 실행, 주소 공간 교체, 캐시와 TLB 재가열이 누적되면 실제 지연이 커진다.

  1. register

    저장 register 현재 CPU 상태 기록

  2. scheduler

    선택 scheduler 다음 프로세스 결정

  3. address space

    교체 address space 페이지 테이블 전환

  4. cache/TLB

    재가열 cache/TLB 작업 집합 다시 적재

컨텍스트 스위칭은 순수한 오버헤드입니다.

전환하는 동안 CPU는 사용자의 어떤 작업도 수행하지 않습니다.

오버헤드는 크게 두 가지입니다.

직접적 오버헤드

레지스터를 저장하고 복원하는 시간, 스케줄링 결정에 걸리는 시간입니다.

일반적으로 한 번의 컨텍스트 스위칭에 수 마이크로초(μs)가 걸립니다.

현대 하드웨어에서는 1~10μs 정도입니다.

간접적 오버헤드 (캐시 오염)

이것이 더 큰 문제입니다.

프로세스 A가 실행되면서 L1/L2/L3 캐시에 A의 데이터가 채워져 있습니다.

B로 전환하면 B의 데이터가 캐시에 올라오면서 A의 데이터가 밀려납니다.

나중에 A로 돌아오면, 캐시에 A의 데이터가 없으므로 캐시 미스(Cache Miss)가 연쇄적으로 발생합니다.

워밍업 비용이 직접적 오버헤드보다 수십 배 클 수 있습니다.

또한 페이지 테이블 전환 시 TLB(Translation Lookaside Buffer)가 무효화됩니다.

TLB는 가상→물리 주소 변환의 캐시인데, 프로세스마다 페이지 테이블이 다르므로 전환 시 TLB를 비워야 합니다.

이후 메모리 접근마다 TLB 미스가 발생하여 페이지 테이블을 다시 탐색해야 합니다.

이 오버헤드를 줄이기 위해 현대 CPU는 ASID(Address Space Identifier)를 사용하여 TLB 항목에 프로세스 태그를 달아, 전환 시 TLB를 완전히 비우지 않아도 되게 합니다.

이 오버헤드들이 누적되면 체감 성능에 영향을 줍니다.

초당 1000번의 컨텍스트 스위칭이 발생하고, 한 번에 10μs가 걸린다면, CPU 시간의 1%가 컨텍스트 스위칭에만 소모됩니다.

캐시/TLB 오염까지 합치면 5~10%에 달할 수 있습니다.

이 때문에 스레드가 등장합니다.

같은 프로세스 내의 스레드 간 전환은 메모리 공간(페이지 테이블)을 바꿀 필요가 없으므로, TLB가 유효하게 유지되고 캐시 오염도 줄어들어 프로세스 간 전환보다 훨씬 가볍습니다.

4장에서 자세히 다룹니다.

컨텍스트 스위칭은 네 구간의 합으로 체감된다

문맥 저장, 커널 스케줄링, 새 문맥 복원, 캐시 재가열을 나누어 보면 짧은 작업을 너무 잘게 쪼갤 때 왜 느려지는지 보인다.

  1. save

    PC, register 저장

  2. schedule

    우선순위와 큐 확인

  3. restore

    다음 문맥 복원

  4. warm up

    cache miss 회복

컨텍스트 스위칭 횟수 확인
# 시스템 전체 컨텍스트 스위칭 횟수
vmstat 1
# procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
#  r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
#  1  0      0 3241284 145832 1284776  0    0     0     0  234  512 2  1  97  0  0
#                                                          ^^^  ^^^
#                                                          인터럽트 컨텍스트스위칭

# 특정 프로세스의 컨텍스트 스위칭 확인
cat /proc/<PID>/status | grep ctxt
# voluntary_ctxt_switches:    1523   (자발적: I/O 대기 등)
# nonvoluntary_ctxt_switches: 847    (비자발적: 타이머에 의한 선점)

voluntary_ctxt_switches는 프로세스가 I/O 등으로 자발적으로 CPU를 양보한 횟수이고, nonvoluntary_ctxt_switches는 타이머 인터럽트에 의해 강제로 빼앗긴 횟수입니다.

비자발적 전환이 매우 많다면 CPU 경쟁이 심하다는 의미입니다.


컨텍스트 스위칭을 볼 때는 레지스터 저장/복원만 생각하면 비용을 과소평가하기 쉽습니다.

실제 성능에는 캐시와 TLB가 다시 따뜻해지는 간접 비용까지 함께 반영됩니다.

컨텍스트 스위칭 비용

PCB에 레지스터를 옮기는 시간은 직접 비용이고, 캐시와 TLB가 다시 채워지는 시간은 숨어 있는 간접 비용입니다.

  1. 인터럽트 진입

    타이머나 I/O 이벤트가 커널 모드 진입을 만들고 현재 실행을 멈춥니다. 직접 비용

  2. 현재 상태 저장

    PC, SP, 범용 레지스터, FLAGS가 현재 프로세스 PCB에 기록됩니다. 직접 비용

  3. 다음 대상 선택

    스케줄러가 Ready Queue에서 우선순위와 정책에 맞는 프로세스를 고릅니다. 직접 비용

  4. 주소 공간 전환

    페이지 테이블이 바뀌며 TLB 항목이 무효화되거나 태그로 분리됩니다. 간접 비용 시작

  5. 캐시 워밍업

    새 프로세스 데이터가 캐시에 올라오기 전까지 메모리 접근 지연이 커집니다. 숨은 지연


프로세스 큐

OS는 프로세스들을 여러 큐(Queue)로 조직합니다.

큐는 프로세스의 현재 상태에 따라 분류됩니다.

Ready Queue(준비 큐): CPU를 사용할 준비가 된 프로세스들을 담은 큐입니다.

스케줄러가 이 큐에서 다음에 실행할 프로세스를 선택합니다.

실제로는 단순한 FIFO 큐가 아니라, 우선순위 큐, 멀티레벨 큐 등 다양한 구조가 사용됩니다.

Linux의 CFS(Completely Fair Scheduler)는 레드-블랙 트리를 사용하여 O(logN)O(\log N)에 다음 프로세스를 선택합니다.

Wait Queue(대기 큐): 특정 이벤트(디스크 I/O 완료, 네트워크 패킷 도착, 시그널 수신 등)를 기다리는 프로세스들의 목록입니다.

이벤트의 종류별로 별도의 큐가 존재합니다.

디스크 I/O를 기다리는 프로세스들의 큐, 네트워크 I/O를 기다리는 큐가 각각 있습니다.

이벤트가 발생하면 해당 큐의 프로세스(들)를 Ready Queue로 옮깁니다.

큐의 구현과 스케줄러의 알고리즘에 따라, 어떤 프로세스가 먼저 CPU를 받는지, 얼마나 오래 실행하는지, 얼마나 빨리 응답하는지가 결정됩니다.

이것이 5장에서 다룰 CPU 스케줄링의 핵심입니다.

PCB의 각 필드가 컨텍스트 스위칭의 어느 단계에서 쓰이는지 연결하면, 저장과 복원이 단순 복사가 아니라 실행 재개의 조건임을 이해하기 쉽습니다.

컨텍스트 스위칭 PCB 저장

PCB의 레지스터, 상태, 페이지 테이블 포인터, 스케줄링 정보가 저장과 복원 단계마다 정확히 쓰여야 프로세스가 끊긴 지점부터 이어집니다.

  1. 1
    커널 진입

    irq 타이머 인터럽트나 I/O 대기가 사용자 모드 실행을 멈춥니다.

  2. 2
    A의 CPU 상태 저장

    save PC, SP, FLAGS, 범용 레지스터가 A의 PCB에 기록됩니다.

  3. 3
    다음 PCB 선택

    pick Ready Queue에서 우선순위와 사용 시간을 보고 B를 고릅니다.

  4. 4
    주소 공간 교체

    mmu B의 페이지 테이블 포인터가 로드되며 가상 주소 해석 기준이 바뀝니다.

  5. 5
    B의 실행 재개

    restore B의 PCB에서 레지스터를 복원하고 사용자 모드로 돌아갑니다.

  6. 6
    레지스터와 PC

    CPU context 프로세스가 다음에 실행할 명령어와 스택 위치를 보존합니다.

  7. 7
    페이지 테이블 포인터

    memory CR3처럼 주소 공간을 가리키는 값이 전환 비용과 TLB 영향에 연결됩니다.

  8. 8
    상태와 우선순위

    schedule Ready인지 Waiting인지, 어떤 순서로 CPU를 받을지 결정합니다.

  9. 9
    파일과 시그널

    resource 열린 파일, 작업 디렉터리, 대기 시그널이 프로세스 실행 환경을 이룹니다.

다음 절에서는 프로세스가 어떻게 생성되고 종료되는지, fork()exec()의 실제 동작을 살펴보겠습니다.

PCB는 멈춘 실행을 다시 잇는 체크포인트다

PCB는 프로세스의 신분, CPU 문맥, 메모리 경계, 열린 자원을 저장하고 컨텍스트 스위칭은 이 값을 저장한 뒤 다음 프로세스 값을 복원한다.

  1. 누구의 실행인가

    ID PID, PPID, UID/GID로 관계와 권한을 판단한다. 상태 값으로 Ready, Running, Waiting을 구분한다.

  2. 어디서 멈췄는가

    CPU PC와 SP가 다음 실행 위치와 스택 위치를 보존한다. 레지스터 값이 계산 중이던 문맥을 잃지 않게 한다.

  3. 무엇을 볼 수 있는가

    MEM 페이지 테이블 포인터가 주소 공간을 바꾼다. 파일 디스크립터가 열린 파일과 소켓을 이어 둔다.

  4. 커널 진입

    타이머 인터럽트나 I/O 대기로 현재 실행이 멈춘다.

  5. 현재 PCB 저장

    PC, SP, 레지스터와 상태를 현재 프로세스 PCB에 쓴다.

  6. 다음 PCB 선택

    Ready Queue에서 우선순위와 시간 정책으로 대상을 고른다.

  7. 주소 공간 전환

    페이지 테이블이 바뀌며 TLB와 캐시 비용이 생긴다.

  8. 문맥 복원

    다음 PCB의 레지스터를 CPU에 올리고 사용자 모드로 돌아간다.

  9. Ready Queue

    ready CPU를 받을 수 있는 후보가 모이며 스케줄러가 여기서 고른다.

  10. Wait Queue

    wait I/O, 네트워크, 시그널 이벤트를 기다리다 완료되면 Ready로 돌아간다.

  11. 비용 판단

    cost 스레드는 주소 공간을 공유해 프로세스 전환보다 TLB 부담이 작다.

아래 다이어그램은 PCB에 저장되는 정보가 컨텍스트 스위칭 중 어디에서 쓰이는지를 핵심 질문과 판단 순서로 정리한 것입니다.