운영체제 학습 노트 목차

스레드 — 한 프로세스 안의 여러 손

앞 편에서 프로세스 전환은 주소 공간·TLB를 갈아 비싸다고 했다. 그런데 한 프로그램 안에서도 여러 일을 동시에 하고 싶을 때가 많다 — 웹 서버가 여러 요청을 한꺼번에, 브라우저가 렌더링하면서 다운로드. 매번 무거운 프로세스를 만들 순 없다. 그래서 한 프로세스 안에 여러 실행 흐름을 두는 게 스레드다. 스레드는 같은 집(프로세스)에 사는 여러 일꾼 같아서 — 빠르게 전환되고 자원을 나눠 쓰지만, 바로 그 공유 때문에 동시성의 모든 함정이 시작된다.

무엇을 나누고 무엇을 따로 갖나

스레드는 같은 프로세스의 주소 공간 안에서 산다. 그래서 무엇을 공유하고 무엇을 따로 갖는지가 핵심이다.

다이어그램 로딩 중…

코드·전역 데이터·힙(동적 할당 객체가 사는 곳)은 모든 스레드가 공유하고, *스택·레지스터·프로그램 카운터(PC)*만 스레드마다 따로 갖는다. 그래서 한 스레드가 만든 데이터를 다른 스레드가 곧바로 본다 — 통신이 공짜다. 하지만 바로 이 공유가"공유 가변 상태" 버그의 뿌리다. 두 스레드가 같은 힙 변수를 동시에 수정하면 데이터가 깨진다(다음 동시성 편). 즉 스레드의 *축복(쉬운 공유)*과 *저주(경쟁 조건)*는 같은 동전의 양면이다.

전환이 싸다 — 스레드의 가장 큰 장점

스레드의 결정적 이점은 전환 비용이다. 같은 프로세스 안의 스레드끼리 전환할 때는 — 주소 공간이 그대로TLB를 비울 필요가 없다. 프로세스 전환이 집을 통째로 옮기는 거라면, 스레드 전환은 같은 집에서 일꾼만 바꾸는 것이라 훨씬 가볍다(레지스터·스택 포인터·PC만 갈아 끼운다). (출처: Process vs Thread context switch.) 그래서 동시 작업이 많을수록 프로세스보다 스레드가 효율적이다.

누가 스레드를 스케줄하나 — 유저 vs 커널 스레드

여기서 미묘한 갈림길이 있다 — 스레드를 누가 관리하느냐다. 커널 레벨 스레드OS가 직접 안다 — 그래서 여러 코어에 분산되고, 한 스레드가 I/O로 블록돼도 다른 스레드는 계속 돈다(진짜 병렬). 대신 생성·전환에 커널 모드 전환 비용이 든다. 유저 레벨 스레드OS는 모른 채 라이브러리가 관리한다 — 전환이 시스템 콜 없이 빨라 가볍지만, 치명적 약점이 있다: OS가 한 덩어리(프로세스)로만 봐서, 한 스레드가 블로킹 시스템 콜을 부르면 프로세스 전체가 멈추고, 여러 코어도 못 쓴다. (출처: User vs kernel threads.)

두 마리 토끼 — M:N 스케줄링

이 둘의 장점을 합치려는 게 M:N 스케줄링많은(M) 유저 스레드를 적은(N) 커널 스레드에 얹는다. 가장 단순한 옛 방식인 *그린 스레드(M:1)*는 모든 유저 스레드가 한 OS 스레드 위라, 하나가 블록되면 전부 막혔다. 진짜 M:N은 다르다 — 한 유저 스레드가 블로킹 작업을 만나면, OS 스레드에서 떼어내고(unmount) 다른 유저 스레드를 그 OS 스레드에 올린다. 그래서 블로킹 코드를 그대로 써도 적은 OS 스레드로 수만 개의 동시성을 낸다 — 읽기 쉬운 블로킹 코드높은 동시성을 동시에 얻는 셈이다.

왜 이 페이지가 중요한가 — 스레드 모델의 선택이 동시성의 거의 모든 것을 좌우한다. 커널 스레드 풀을 몇 개 둘지는 "비싼 OS 스레드를 몇 개나 둘까"의 문제이고, 공유 가변 상태 버그는 "스레드가 힙을 공유"하는 이 구조에서 나오며, 전역으로 공유되는 객체에 요청별 상태를 두면 안 되는 이유도 — 여러 스레드가 그 객체를 동시에 건드리기 때문이다. 스레드를 이해하면 동시성 버그가 왜 나는지가 바닥부터 보인다.

정리하면, 스레드는 한 프로세스 안의 여러 실행 흐름으로 — 코드·데이터·힙은 공유하고 스택·레지스터만 따로 가져, 전환이 싸고(TLB flush 불필요) 통신이 공짜다. 대신 공유 때문에 경쟁 조건이 생긴다(다음 편). 커널 스레드는 진짜 병렬이나 무겁고, 유저 스레드는 가볍지만 블로킹·멀티코어에 약하며, *M:N(경량 스레드)*이 둘의 장점을 합쳐 싼 블로킹으로 높은 동시성을 낸다. 이 스레드들을 CPU에 어떻게 번갈아 올릴지 정하는 게 — 다음 편 스케줄링이다.

프로세스 — 주소공간·fork/exec·컨텍스트 스위치CPU 스케줄링 — 선점·라운드로빈·MLFQ·CFS