메모리 구조로 이해하는 프로세스와 스레드
출처: https://m.youtube.com/watch?v=gQ4c6IzhU9Q&ra=m · 코딩하는기술사 · 7:20 · 2026-01-06
요지
- 프로세스는 운영체제로부터 자원을 할당받는 작업의 단위이며, 각 프로세스는 독립된 가상 주소 공간을 가진다.
- 스레드는 프로세스 안에서 명령을 실행하는 흐름의 단위다.
- 같은 프로세스의 스레드들은 코드·데이터·힙 영역을 공유하지만, 스택과 실행 상태는 스레드별로 가진다.
- 프로세스 사이의 데이터 교환에는 IPC가 필요하지만, 스레드는 공유 메모리를 통해 데이터를 쉽게 교환할 수 있다.
- 스레드 전환은 일반적으로 프로세스 전환보다 가볍지만, 공유 상태 때문에 경쟁 조건과 교착 상태가 발생할 수 있다.
- 멀티프로세스와 멀티스레드는 속도만으로 선택하지 말고 격리, 통신 비용, 작업 특성, 장애 영향 범위를 함께 고려해야 한다.
개요
프로세스와 스레드의 차이는 단순히 “프로그램과 그 안에서 실행되는 것”으로 설명하기보다 메모리와 자원의 소유 관계로 설명해야 정확하다. 프로세스는 독립된 주소 공간과 운영체제 자원을 가지는 경계이고, 스레드는 그 주소 공간 안에서 실제 명령을 수행하는 실행 흐름이다.
이 구분은 동시성 구조를 설계할 때 중요하다. 프로세스를 사용하면 장애와 메모리를 강하게 격리할 수 있지만 통신과 전환 비용이 커진다. 스레드는 자원을 효율적으로 공유하지만, 같은 데이터를 동시에 변경할 때 별도의 동기화가 필요하다.
배경 / 사전 지식
실행 파일 자체는 저장 장치에 놓인 정적인 명령과 데이터다. 사용자가 실행 파일을 시작하면 운영체제는 실행에 필요한 주소 공간과 자원을 마련하고 이를 프로세스로 관리한다. 하나의 프로그램에서 여러 프로세스를 만들 수도 있으며, 하나의 프로세스 안에도 여러 스레드가 존재할 수 있다.
일반적인 프로세스 주소 공간은 다음 영역으로 설명할 수 있다.
- 코드 영역: 실행할 기계어 명령
- 데이터 영역: 전역 변수와 정적 변수
- 힙 영역: 실행 중 동적으로 할당한 객체와 메모리
- 스택 영역: 함수 호출 정보, 매개변수, 지역 변수
여기서 “공유한다”는 말은 여러 실행 흐름이 같은 메모리 위치에 접근할 수 있다는 뜻이다. “독립적이다”는 말은 한쪽의 일반적인 메모리 접근이 다른 쪽의 주소 공간으로 직접 이어지지 않는다는 뜻이다.
핵심 개념
프로세스: 자원과 격리의 단위
프로세스는 운영체제로부터 주소 공간, 열린 파일 같은 자원을 할당받는 작업의 단위다. 각 프로세스는 원칙적으로 독립된 가상 주소 공간을 가지므로 다른 프로세스의 코드·데이터·힙·스택에 직접 접근할 수 없다.
이 격리 덕분에 한 프로세스의 오류가 다른 프로세스의 메모리를 바로 훼손하기 어렵다. 반면 프로세스끼리 데이터를 주고받으려면 파이프, 소켓, 메시지 큐, 공유 메모리 같은 IPC(Inter-Process Communication)가 필요하다.
스레드: 실행 흐름의 단위
스레드는 프로세스 안에서 스케줄링되고 명령을 실행하는 흐름이다. 같은 프로세스에 속한 스레드는 코드·전역 데이터·힙을 공유하므로 동일한 객체와 자원에 효율적으로 접근할 수 있다.
각 스레드는 독립적인 함수 호출을 수행해야 하므로 자신의 스택을 가진다. 또한 현재 실행 위치를 나타내는 프로그램 카운터와 레지스터 상태 등 스케줄링에 필요한 실행 문맥도 스레드별로 관리된다.
공유 영역과 독립 영역
집에 비유하면 프로세스는 독립된 집이고, 그 안의 스레드들은 함께 사는 사람들이다. 거실과 주방에 해당하는 코드·데이터·힙은 함께 사용하지만, 개인 방에 해당하는 스택과 실행 문맥은 각자 가진다.
| 구분 | 프로세스 사이 | 같은 프로세스의 스레드 사이 |
|---|---|---|
| 주소 공간 | 원칙적으로 독립 | 공유 |
| 코드·데이터·힙 | 직접 공유하지 않음 | 공유 |
| 스택 | 프로세스 및 스레드별 독립 | 스레드별 독립 |
| 데이터 교환 | IPC 필요 | 공유 메모리에 직접 접근 가능 |
| 장애 격리 | 상대적으로 강함 | 상대적으로 약함 |
컨텍스트 스위칭
컨텍스트 스위칭은 CPU가 실행 대상을 바꾸기 위해 현재 실행 상태를 저장하고 다음 실행 상태를 복원하는 과정이다. 프로세스가 바뀌면 주소 공간과 관련된 메모리 관리 상태까지 달라질 수 있으므로, 같은 프로세스 안에서 스레드만 바꾸는 경우보다 일반적으로 비용이 크다.
다만 “스레드는 언제나 빠르다”는 절대 법칙은 아니다. 실제 비용은 운영체제, 하드웨어, 캐시 상태, 작업량, 동기화 경쟁 등에 따라 달라진다.
작동 원리
- 프로그램을 실행하면 운영체제가 프로세스의 가상 주소 공간과 자원을 만든다.
- 프로세스의 주 스레드가 생성되어 코드의 시작점부터 명령을 실행한다.
- 추가 스레드를 만들면 각 스레드에 별도의 스택과 실행 문맥이 배정된다.
- 모든 스레드는 같은 프로세스의 코드·데이터·힙과 열린 자원에 접근할 수 있다.
- 운영체제 스케줄러는 실행 가능한 스레드 중 하나를 CPU에 배정한다.
- 실행 대상이 바뀌면 기존 스레드의 레지스터와 실행 위치를 저장하고 다음 스레드의 상태를 복원한다.
- 여러 스레드가 공유 데이터를 동시에 변경한다면 연산 순서가 엇갈릴 수 있으므로 락, 세마포어, 원자적 연산 등의 동기화가 필요하다.
- 별도 프로세스끼리 데이터를 교환할 때는 IPC를 사용하며, 운영체제가 통신과 접근 경계를 중재한다.
코드 예시
다음 Python 코드는 스레드는 같은 리스트를 공유하지만 별도 프로세스의 변경은 부모 프로세스의 리스트에 직접 반영되지 않는다는 점을 보여 준다.
from multiprocessing import Process
from threading import Thread
def append_value(values, value):
values.append(value)
def main():
thread_values = []
threads = [
Thread(target=append_value, args=(thread_values, value))
for value in range(3)
]
for thread in threads:
thread.start()
for thread in threads:
thread.join()
print("스레드 실행 후:", sorted(thread_values))
process_values = []
process = Process(target=append_value, args=(process_values, 99))
process.start()
process.join()
print("프로세스 실행 후 부모의 리스트:", process_values)
if __name__ == "__main__":
main()
일반적인 출력은 다음과 같다.
스레드 실행 후: [0, 1, 2]
프로세스 실행 후 부모의 리스트: []
스레드들은 같은 프로세스의 힙에 놓인 thread_values를 공유하므로 변경 결과를 함께 볼 수 있다. 반면 자식 프로세스는 독립된 주소 공간에서 process_values를 변경하므로 부모의 리스트에는 그 변경이 나타나지 않는다. 프로세스 사이에서 값을 공유하려면 큐, 파이프 또는 명시적인 공유 메모리 같은 IPC를 사용해야 한다.
이 예시는 메모리 소유 관계를 보여 주기 위한 것이다. 여러 스레드가 복합적인 공유 상태를 갱신하는 실제 코드에서는 연산의 원자성을 가정하지 말고 적절한 동기화 수단을 사용해야 한다.
함정·실수
- 프로세스를 프로그램 자체라고 표현하면 실행 파일과 실행 중인 인스턴스를 혼동한다. 프로세스는 실행 중이며 운영체제가 자원을 관리하는 단위라고 설명한다.
- 스레드가 모든 메모리를 공유한다고 말하면 부정확하다. 코드·데이터·힙은 공유하지만 스택과 실행 문맥은 스레드별로 독립적이다.
- 스택이 독립적이므로 스레드가 완전히 안전하다고 오해하기 쉽다. 스택의 지역 변수가 공유 객체를 참조하면 여러 스레드가 결국 같은 힙 객체를 변경할 수 있다.
- 공유 메모리가 빠르다는 이유만으로 동기화를 생략하면 경쟁 조건, 데이터 손상, 가시성 문제가 생길 수 있다.
- 여러 락을 일관되지 않은 순서로 획득하면 서로 상대의 락을 기다리는 교착 상태가 발생할 수 있다.
- 프로세스 전환이 항상 스레드 전환보다 정확히 일정한 만큼 느리다고 단정하면 안 된다. 비용의 크기는 실행 환경과 워크로드에 따라 달라진다.
- 멀티스레드가 언제나 멀티프로세스보다 빠르다고 가정하면 안 된다. 락 경쟁이 심하거나 강한 장애 격리가 필요하면 프로세스가 더 적합할 수 있다.
베스트 프랙티스
- 면접에서는 정의, 메모리 공유 구조, 통신 방식, 전환 비용의 순서로 답하면 논리가 명확해진다.
- 서로 격리해야 하는 작업, 한 작업의 장애가 다른 작업으로 번지면 안 되는 경우에는 멀티프로세스를 우선 검토한다.
- 동일한 대규모 데이터를 자주 공유하고 작업 간 협력이 많은 경우에는 멀티스레드를 검토하되 공유 상태를 최소화한다.
- 입출력 대기가 많은 작업에는 스레드나 비동기 실행이 유용할 수 있다. CPU 계산이 많은 작업은 언어 런타임의 제약까지 확인한 뒤 멀티프로세스나 병렬 실행 모델을 선택한다.
- 가능한 경우 불변 데이터, 메시지 전달, 작업 큐를 사용해 직접 공유하고 변경하는 상태를 줄인다.
- 공유 상태가 필요하면 락의 보호 범위와 획득 순서를 문서화하고, 임계 구역을 짧게 유지한다.
- 프로세스와 스레드 선택은 처리량뿐 아니라 메모리 사용량, IPC 비용, 동기화 복잡도, 장애 격리, 운영 편의성을 함께 측정해 결정한다.
면접 답변은 다음과 같이 정리할 수 있다. “프로세스는 운영체제로부터 자원을 할당받는 작업의 단위이고, 스레드는 프로세스 안에서 실행되는 흐름의 단위입니다. 프로세스는 독립된 주소 공간을 가지지만 같은 프로세스의 스레드는 코드·데이터·힙을 공유하고 스택은 각각 가집니다. 따라서 스레드는 자원 공유와 실행 전환이 비교적 가볍지만 공유 상태를 동기화해야 합니다.”
참고
- IPC(Inter-Process Communication): 프로세스 사이의 통신 수단
- 컨텍스트 스위칭: CPU가 실행 대상을 교체하며 실행 문맥을 저장·복원하는 과정
- 영상에서 후속 학습 주제로 멀티프로세스와 멀티스레드의 선택 기준, 브라우저의 프로세스 구조, 스레드 동기화, 자원 경쟁과 교착 상태를 언급한다.