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

컨텍스트 스위칭

프로세스스케줄링컨텍스트스위칭

CPU 코어 하나는 한 순간에 하나의 실행 흐름(프로세스나 스레드)만 실행합니다. 그런데 컴퓨터에는 훨씬 많은 작업이 동시에 돌고 있는 것처럼 보입니다. 운영체제가 CPU를 작업들 사이에서 번갈아 넘겨 주기 때문입니다. 이렇게 CPU가 실행하던 작업에서 다른 작업으로 바뀌는 일을 컨텍스트 스위칭(context switching, 문맥 교환)이라고 부릅니다. 작업이 나중에 멈춘 곳에서 이어서 실행되려면 그 작업의 실행 상태, 곧 프로그램 카운터와 레지스터 값 등을 어딘가에 저장해 두어야 합니다. 운영체제는 이 상태를 프로세스의 PCB(스레드는 보통 TCB라 부릅니다)에 저장하고, 다음에 실행할 작업의 상태를 복원합니다.

컨텍스트 스위칭은 멀티태스킹을 가능하게 하는 대신 비용이 듭니다. 전환하는 동안에는 사용자 작업이 실행되지 못하고, 전환 뒤에는 이전 작업 위주로 채워져 있던 캐시와 TLB가 새 작업에 맞지 않아 한동안 느려질 수 있습니다. 이 토픽에서는 상태를 저장하고 복원하는 과정, 시간 조각 길이에 따라 전환 비용의 비율이 달라지는 모습, 프로세스 사이 전환과 스레드 사이 전환의 차이를 봅니다.

핵심 개념

컨텍스트

작업을 나중에 이어서 실행하는 데 필요한 정보입니다. 프로그램 카운터, 스택 포인터를 포함한 CPU 레지스터 값과 프로세스 상태, 메모리 관리 정보 등이 PCB에 담깁니다.

핵심 개념

저장과 복원

전환할 때 운영체제는 지금 실행하던 작업의 레지스터 값을 그 작업의 PCB에 저장하고, 다음에 실행할 작업의 PCB에서 값을 꺼내 CPU에 채웁니다. 그러면 CPU는 그 작업이 멈췄던 곳부터 이어서 실행합니다.

핵심 개념

전환이 일어나는 때

할당된 시간이 끝났을 때, 입출력 같은 이벤트를 기다리며 스스로 CPU를 내놓을 때, 더 높은 우선순위의 작업이 준비되었을 때 일어납니다. 입출력 대기로 스스로 내놓는 경우는 비선점 스케줄링에서도 일어나고, 시간 만료와 우선순위 선점은 선점 스케줄링에서만 일어납니다.

핵심 개념

직접 비용과 간접 비용

직접 비용은 상태를 저장하고 복원하는 시간과 다음 작업을 고르는 스케줄러 시간입니다. 간접 비용은 전환 뒤 캐시와 TLB가 새 작업에 맞지 않아 생기는 성능 저하입니다.

핵심 개념

주소 공간 전환

다른 프로세스로 바꾸면 그 프로세스의 가상 주소 공간으로 바꿔야 해서 페이지 테이블 전환이 필요합니다. 같은 프로세스의 스레드는 주소 공간을 공유하므로 이 일이 없습니다.

핵심 개념

자발적 전환과 비자발적 전환

작업이 입출력 등 자원을 기다리며 CPU를 내놓은 전환이 자발적, 시간 조각이 끝나거나 더 높은 우선순위 작업에 밀려난 전환이 비자발적입니다. 리눅스는 이 둘의 횟수를 따로 세어 보여 줍니다.

전환 자체는 일을 하지 않습니다 — 저장과 복원, 스케줄링에 쓰는 시간은 사용자에게 유용한 계산을 하지 않는 시간입니다.
시간 조각은 응답성과 비용의 균형입니다 — 짧으면 여러 작업이 빨리 번갈아 응답하지만 전환이 잦아 비용의 비율이 커지고, 길면 비용은 줄지만 다른 작업이 기다립니다.
스레드 전환이 보통 더 가볍습니다 — 같은 프로세스의 스레드끼리 바꾸면 주소 공간을 바꿀 필요가 없습니다.
스레드를 무작정 늘리면 전환만 늘어납니다 — 코어보다 실행 가능한 스레드가 훨씬 많으면 전환이 잦아지고 실제 일하는 시간이 줄 수 있습니다.
이 시각화에서 확인할 것 — "저장과 복원" 탭에서 레지스터 값이 PCB로 저장되고 복원되는 과정을, "전환 비용" 탭에서 시간 조각에 따른 사용자 작업 시간의 비율을, "프로세스 vs 스레드" 탭에서 두 전환이 해야 하는 일의 차이를 STEP으로 확인하세요.

컨텍스트 스위칭의 배경

여러 프로그램을 동시에 올려 두고 CPU를 번갈아 쓰게 하는 다중 프로그래밍과 시분할 시스템은 한 CPU로 여러 작업을 진행하는 것처럼 보이게 합니다. 이를 가능하게 하는 기본 동작이 컨텍스트 스위칭입니다. 전환이 얼마나 빠른가는 하드웨어가 레지스터를 저장하고 복원하는 방식과 운영체제의 구현에 달려 있습니다.

숫자로 확인하기

저장과 복원 — 프로세스 A의 PC가 0x4010, B의 PC가 0x8200이라면 전환 뒤 CPU의 PC는 0x8200이 되어 B가 멈춘 곳부터 이어집니다. 값은 예시입니다.
전환 비용의 비율(가정값) — 전환 한 번에 1의 시간이 든다고 하고 시간 33 동안 시간 조각을 2로 하면 사용자 작업은 22/33(66.7%), 5로 하면 28/33(84.8%), 10으로 하면 30/33(90.9%)입니다. 조각이 짧을수록 전환 비율이 커집니다(마지막 조각은 남은 시간만큼만 실행합니다).
해야 할 일의 수 — 이 토픽의 예에서 프로세스 전환은 항목 4개(상태 저장·복원, 커널 개입, 주소 공간 전환, TLB 처리), 같은 프로세스 안의 스레드 전환은 2개입니다.

자주 하는 오해

전환은 공짜다 — 전환 중에는 사용자 작업이 실행되지 못하고, 이후 캐시와 TLB 효율도 떨어집니다. 잦은 전환은 처리량을 깎습니다.
시간 조각은 짧을수록 좋다 — 응답은 빨라지지만 전환 횟수가 늘어 비용의 비율이 커집니다.
스레드 전환은 비용이 없다 — 주소 공간 전환이 없어 가벼울 뿐, 레지스터 저장과 복원, 스케줄링, 캐시 영향은 여전히 있습니다.
컨텍스트 스위칭은 멀티코어에서만 의미가 있다 — 코어가 하나여도 여러 작업이 번갈아 실행되려면 필요합니다. 코어가 여러 개여도 코어보다 작업이 많으면 각 코어에서 계속 일어납니다.

면접에서 이렇게 물어요

면접 질문

컨텍스트 스위칭이 무엇인가요?

CPU가 실행하던 작업에서 다른 작업으로 바뀌는 일입니다. 실행하던 작업의 상태(PC, 레지스터 등)를 PCB에 저장하고 다음 작업의 상태를 복원해 이어서 실행합니다.

면접 질문

왜 오버헤드가 생기나요?

전환 중에는 사용자 작업이 실행되지 못하고, 전환 뒤에는 캐시와 TLB의 내용이 새 작업에 맞지 않아 한동안 성능이 떨어지기 때문입니다.

면접 질문

프로세스 전환과 스레드 전환의 차이는?

같은 프로세스의 스레드는 주소 공간을 공유해 페이지 테이블 전환과 그에 따른 TLB 처리가 필요 없어서 보통 더 가볍습니다.

면접 질문

컨텍스트 스위칭을 줄이려면?

코어 수에 맞춰 스레드 수를 제한하고(스레드 풀), 불필요한 락 경합과 잦은 블로킹을 줄이며, 시간 조각을 지나치게 짧게 잡지 않는 방법이 있습니다.

실제 시스템에서는

리눅스의 관찰 — proc 파일 시스템의 /proc/PID/status는 voluntary_ctxt_switches와 nonvoluntary_ctxt_switches를 보여 주고, vmstat의 cs 열은 초당 컨텍스트 스위치 수를 보여 줍니다.
주소 공간 식별자 — 일부 CPU는 TLB 항목에 주소 공간 식별자를 붙여, 주소 공간이 바뀌어도 TLB를 통째로 비우지 않게 합니다. x86의 PCID와 Arm의 ASID가 그런 장치입니다.
사용자 수준 스레드 — 코루틴이나 그린 스레드처럼 런타임이 사용자 공간에서 실행 흐름을 바꾸는 방식은 커널 개입 없이 전환해 비용이 더 작을 수 있습니다.

함께 보면 좋은 토픽

프로세스 — 상태가 저장되는 PCB와 프로세스 상태를 봅니다.
스레드 — 주소 공간을 공유하는 실행 흐름이 어떻게 다른지 봅니다.
CPU 스케줄링 — 언제 어떤 작업으로 전환할지 정하는 방식을 봅니다.
가상 메모리 — 주소 공간과 TLB가 무엇인지 봅니다.
시스템 콜 — 사용자 모드와 커널 모드 사이의 전환을 봅니다.

참고 자료

교재 — Silberschatz 외, "Operating System Concepts"의 프로세스 스케줄링과 컨텍스트 스위치 절, Tanenbaum, Bos, "Modern Operating Systems"
파이썬 — resource 모듈

개념 코드 — 상태 저장과 복원 (단순화)

// 실제 커널은 이 부분을 어셈블리로 구현한다. 개념만 보여 주는 단순화한 코드.
struct pcb {
    unsigned long pc;        // 프로그램 카운터
    unsigned long sp;        // 스택 포인터
    unsigned long regs[16];   // 범용 레지스터
    int state;               // READY, RUNNING, WAITING ...
};

void context_switch(struct pcb *prev, struct pcb *next) {
    save_cpu_state(prev);       // CPU 레지스터 → prev의 PCB
    prev->state = READY;      // 선점된 경우. 입출력 대기로 내놓으면 WAITING
    switch_address_space(next); // 다른 프로세스일 때만 페이지 테이블 교체
    next->state = RUNNING;
    load_cpu_state(next);       // next의 PCB → CPU 레지스터, 이후 next가 이어서 실행
}

전환의 핵심은 두 동작입니다. 지금 CPU에 있는 레지스터 값을 prev의 PCB에 저장하고, next의 PCB에 있던 값을 CPU에 채워 넣으면 next가 멈췄던 곳부터 이어서 실행됩니다. 주소 공간 전환은 다른 프로세스로 바꿀 때만 일어나고, 같은 프로세스의 스레드끼리 바꿀 때는 건너뜁니다. 이 차이가 스레드 전환이 보통 더 가벼운 이유입니다. 위 코드는 이해를 위한 개념도이며, 실제 운영체제는 아키텍처마다 저장해야 할 레지스터 집합과 전환 순서가 다르고 이 구간을 어셈블리로 작성합니다.

리눅스 — 컨텍스트 스위치 횟수 확인

# 시스템 전체: 1초마다 출력, cs 열이 초당 컨텍스트 스위치 수
vmstat 1

# 스레드 단위 누적 횟수 (PID는 메인 스레드)
grep ctxt /proc/1234/status
# voluntary_ctxt_switches:     120      (스스로 CPU를 내놓은 전환)
# nonvoluntary_ctxt_switches:  35       (시간이 끝나거나 우선순위 높은 작업에 선점된 전환)

vmstat의 cs 열은 시스템 전체에서 초당 일어난 컨텍스트 스위치 수입니다. 값이 평소보다 크게 늘었다면 실행 가능한 스레드가 코어보다 많거나 락 경합으로 블로킹이 잦은지 의심해 볼 수 있습니다. /proc/PID/status의 두 값은 PID가 가리키는 메인 스레드의 누적 횟수이고 스레드별 값은 /proc/PID/task/TID/status에서 보며, 자발적 전환이 많으면 입출력이나 락 대기가 많다는 뜻이며 비자발적 전환이 많으면 CPU를 두고 다른 작업과 경쟁한다는 신호입니다. 위 숫자는 설명을 위한 예이고 PID와 값은 시스템마다 다릅니다.

C · Python — 내 프로세스의 전환 횟수

/* C: getrusage로 자발적/비자발적 전환 횟수 */
#include <sys/resource.h>
struct rusage ru;
getrusage(RUSAGE_SELF, &ru);
printf("voluntary=%ld involuntary=%ld\n", ru.ru_nvcsw, ru.ru_nivcsw);

# Python: 같은 값을 resource 모듈로
import resource
ru = resource.getrusage(resource.RUSAGE_SELF)
print(ru.ru_nvcsw, ru.ru_nivcsw)

getrusage는 프로세스가 지금까지 겪은 컨텍스트 스위치 횟수를 돌려줍니다. ru_nvcsw는 자발적 전환, ru_nivcsw는 비자발적 전환의 횟수입니다. 작업 전후에 값을 한 번씩 읽어 차이를 보면 그 작업 중 몇 번 전환되었는지 알 수 있어, 스레드 수를 바꿔 가며 비교하는 실험에 쓸 수 있습니다. 이 두 필드는 리눅스 같은 유닉스 계열에서 제공되며, 모든 플랫폼에서 의미 있는 값이 채워지는 것은 아닙니다.

Q1

컨텍스트 스위칭 때 운영체제가 저장하고 복원하는 것은?

나중에 이어서 실행할 수 있어야 합니다.
컨텍스트 스위칭은 실행 중이던 작업의 레지스터·프로그램 카운터 등 실행 상태를 PCB(스레드는 TCB)에 저장하고, 다음에 실행할 작업의 상태를 복원해 CPU가 그 작업을 이어서 실행하게 하는 과정입니다.

Q2

컨텍스트 스위칭이 성능 오버헤드가 되는 이유는?

전환 자체는 아무 유용한 일도 하지 않습니다.
전환 작업 자체는 사용자에게 유용한 일이 아니고, 전환 후에는 이전 작업 위주로 채워졌던 캐시와 TLB의 효율이 떨어져 한동안 성능이 낮아집니다.

Q3

같은 프로세스 안의 스레드 간 전환이 서로 다른 프로세스 간 전환보다 보통 가벼운 이유는?

같은 프로세스의 스레드들은 같은 메모리를 봅니다.
같은 프로세스의 스레드는 주소 공간을 공유하므로 페이지 테이블 교체와 그에 따른 TLB 무효화 같은 비용이 들지 않아, 프로세스 간 전환보다 가볍습니다.