프로세스와 쓰레드: 자원 격리와 실행 단위의 차이
프로세스와 쓰레드의 메모리 구조, 자원 할당, Context Switch, IPC, 안정성 차이를 실무 관점에서 비교한다.
2026-08-14 · 최초 발행 2026-01-03
같은 프로그램을 나누는 방식
프로세스는 실행 중인 프로그램 인스턴스이자 자원 할당의 기본 단위다. 각각 독립된 메모리 공간과 자원을 보유하는 무거운 실행 개체로 동작한다.
쓰레드는 프로세스 안에서 실제 실행 흐름을 구성하는 기본 단위다. Code, Data, Heap을 프로세스와 공유하면서 필요한 최소 자원만 따로 갖는 경량 실행 개체다.
이 구분은 성능만의 문제가 아니다. 프로세스는 격리와 안정성을 얻는 대신 생성과 전환, 통신에 비용이 들고, 쓰레드는 빠른 공유를 얻는 대신 동기화와 오류 전파를 감당해야 한다.
메모리를 분리할지, 일부를 함께 쓸지
프로세스는 Code(Text), Data, Heap, Stack을 각각 독립적으로 가진다. Data에는 전역 변수와 Static 변수가 포함되고, Heap은 동적 할당에 사용되며, Stack은 지역 변수와 함수 호출을 담는다. 다른 프로세스의 메모리에 접근할 수 없고 가상 메모리를 통해 격리된다.
쓰레드에서는 Code(Text), Data, Heap이 공유 영역이다. 반면 Stack, Register, Program Counter(PC)는 쓰레드마다 따로 유지된다. 이 구조는 메모리를 절약하고 데이터 공유를 빠르게 만들지만, 공유 데이터에 대한 동기화가 필요해진다.
자원 소유 범위가 만드는 차이
프로세스에는 독립 메모리 공간, CPU 시간, 파일 디스크립터 테이블, 페이지 테이블, 열린 파일 목록, 시그널 핸들러가 할당된다. 운영체제는 이를 프로세스 단위로 배정하고, 프로세스가 종료되면 자원을 자동으로 회수한다.
쓰레드가 독립적으로 보유하는 것은 Stack(일반적으로 1~2MB), Register Set, Program Counter(PC), Thread Local Storage(TLS)다. Code, Data, Heap과 파일 디스크립터, 시그널 핸들러, 작업 디렉토리는 프로세스 내부에서 공유한다.
| 자원 | 프로세스 | 쓰레드 |
|---|---|---|
| Code (Text) | 독립 | 공유 |
| Data (전역 변수) | 독립 | 공유 |
| Heap | 독립 | 공유 |
| Stack | 독립 | 독립 |
| Register | 독립 | 독립 |
| 파일 디스크립터 | 독립 | 공유 |
| 페이지 테이블 | 독립 | 공유 |
| Program Counter | 독립 | 독립 |
전환 비용은 무엇을 교체하느냐에 달려 있다
프로세스 Context Switch에서는 모든 레지스터, 페이지 테이블(CR3 레지스터), 메모리 맵, 파일 디스크립터, PCB(Process Control Block) 전체를 저장하고 복원한다. 현재 상태를 저장한 뒤 페이지 테이블을 교체하고, TLB 플러시와 캐시 무효화를 거쳐 다음 프로세스 상태를 복원한다.
이 전환에는 수십 마이크로초가 걸리고 캐시 미스와 TLB 미스가 증가한다.
쓰레드 Context Switch는 레지스터, Stack Pointer(SP), Program Counter(PC), 쓰레드 로컬 저장소를 대상으로 한다. 현재 쓰레드의 레지스터를 저장하고 다음 쓰레드의 레지스터를 복원한 뒤 Stack Pointer를 바꾼다. 메모리 맵을 유지하고 캐시 일부를 유지할 수 있어 비용은 수 마이크로초 수준이다.
쓰레드 전환은 10~100배 빠르며, 전환이 빈번할수록 성능 차이가 커진다.
IPC와 공유 메모리 접근의 대가
서로 다른 프로세스는 IPC를 통해 통신한다. Pipe는 단방향 데이터 흐름에, Message Queue는 메시지 기반 통신에, Socket은 네트워크 통신에, Signal은 비동기 이벤트 알림에 사용한다. Shared Memory는 가장 빠른 방식이지만 동기화가 필요하다.
IPC는 공유 메모리를 제외하면 대체로 데이터 복사가 필요하고 커널을 거치므로 오버헤드가 발생한다. 대신 프로세스 간 경계를 유지해 안전하고 격리된 통신을 제공한다.
쓰레드끼리는 전역 변수, Heap, 직접 메모리 접근으로 데이터를 주고받는다. 복사가 필요 없어 빠르지만 Mutex, Semaphore, Condition Variable, Read-Write Lock 같은 동기화 수단이 필수다.
| 구분 | 프로세스 간 통신 (IPC) | 쓰레드 간 통신 |
|---|---|---|
| 방법 | Pipe, Queue, Socket | 공유 메모리 접근 |
| 속도 | 느림 | 빠름 |
| 데이터 복사 | 필요 (대부분) | 불필요 |
| 오버헤드 | 큼 | 작음 |
| 동기화 | 선택적 | 필수 |
| 안전성 | 높음 | 낮음 (주의 필요) |
생성과 종료에서 드러나는 무게
프로세스는 fork와 exec 방식으로 생성할 수 있다.
pid_t pid = fork();
if (pid == 0) {
execve("/bin/ls", argv, envp);
}
이 과정에서는 부모 프로세스 PCB를 복사하고, 새 메모리 공간과 페이지 테이블을 만들며, Copy-On-Write 방식으로 자원을 복사한 뒤 새로운 PID를 할당한다. 생성 비용은 수 밀리초 수준이며 메모리 할당과 초기화 비용이 포함된다.
프로세스 종료 시에는 다음과 같이 종료하고 모든 자원을 해제하며, 열린 파일을 닫고 메모리를 반환한다.
exit(0);
쓰레드는 pthread_create로 생성할 수 있다.
pthread_t thread;
pthread_create(&thread, NULL, thread_func, arg);
Stack을 할당하고(일반적으로 1~2MB), 쓰레드 제어 블록(TCB)을 생성한 뒤 Register를 초기화하고 쓰레드 ID를 할당한다. 생성 비용은 수십 마이크로초로 낮으며 프로세스의 1/10 ~ 1/100이다.
종료할 때는 Stack을 해제하고 TCB를 제거한다.
pthread_exit(NULL);
| 구분 | 프로세스 | 쓰레드 |
|---|---|---|
| 생성 시간 | 수 밀리초 | 수십 마이크로초 |
| 생성 비용 | 높음 | 낮음 (10~100배 빠름) |
| 메모리 할당 | 전체 메모리 공간 | Stack만 |
| 초기화 | 전체 자원 | 최소 자원 |
오류가 미치는 범위
프로세스는 완전한 메모리 격리를 제공한다. 한 프로세스의 오류가 다른 프로세스에 영향을 주지 않으므로 시스템 안정성이 높고, 메모리 보호, 권한 분리, 샌드박스 구성이 가능하다. 프로세스가 크래시해도 독립적으로 복구할 수 있어 전체 시스템에 미치는 영향을 최소화한다.
쓰레드는 메모리를 공유하기 때문에 격리 범위가 제한된다. 한 쓰레드의 오류가 전체 프로세스에 영향을 줄 수 있으며, 쓰레드 크래시는 전체 프로세스 종료로 이어질 수 있다. Race Condition, Deadlock, Livelock 같은 동기화 문제도 디버깅을 복잡하게 만든다.
| 구분 | 프로세스 | 쓰레드 |
|---|---|---|
| 격리 | 완전 격리 | 부분 격리 |
| 오류 영향 | 독립적 | 전체 프로세스에 영향 |
| 안정성 | 높음 | 낮음 |
| 보안 | 강함 | 약함 |
| 디버깅 | 쉬움 | 어려움 |
| 복구 | 독립적 복구 | 전체 재시작 필요 |
작업 경계에 맞춰 고르기
독립적으로 실행해야 하는 작업, 격리가 필요한 작업, 서로 다른 프로그램 실행, 보안이 중요한 작업에는 프로세스가 맞는다. 웹 브라우저 탭(Chrome), 작업자 프로세스를 사용하는 프로세스 풀, 마이크로서비스 아키텍처, 샌드박스 환경이 그 예다.
동일 프로그램 안에서 병렬 작업을 수행하거나 빠른 통신과 자원 공유가 필요한 경우에는 쓰레드가 적합하다. 웹 서버의 요청 처리 쓰레드, UI와 백그라운드 작업을 분리한 GUI 애플리케이션, 렌더링·물리·AI를 나눈 게임 엔진, 쿼리를 처리하는 데이터베이스가 이에 해당한다.
| 구분 | 프로세스 | 쓰레드 |
|---|---|---|
| 독립 작업 | 적합 | 부적합 |
| 병렬 작업 | 가능 (오버헤드 큼) | 적합 |
| 자원 공유 | 어려움 | 쉬움 |
| 격리 필요 | 적합 | 부적합 |
| 빠른 전환 | 부적합 | 적합 |
| 멀티코어 | 활용 가능 | 활용 가능 |
프로세스는 독립 작업과 격리를, 쓰레드는 병렬 작업과 자원 공유를 위한 선택지다. 필요한 보호 경계와 공유 범위를 먼저 정하면 두 실행 단위의 트레이드오프가 분명해진다.