CPU 에서 시작 해 보자. 성능 코어 8 + 효율 코어 4. 메인보드 의 *커패시터수명 을 결정한다. *메모리휘발성 이지만 수십 년 가는 반도체. 프로그램디스크 위의 파일. 그런데 — 파일어떻게 동작 하는가. 디스크 의 *바이트 더미어떻게 *키 입력 에 반응 하고 네트워크 패킷 을 받아 *DB 에 쓰는 동적 존재* 가 되는가.

그 변환점프로세스. 정적 파일동적 실행경계. 그리고 *프로세스 안 에서 스레드진짜 일코어 에 던진다.

이 글은 CPU · 메인보드 · 메모리 · 프로그램 · 프로세스 · 스레드 · 코어서로 다른 추상화 수준7 개 개념왜 한 사슬 로 묶이는지, 그 사슬 의 중심 매듭 인 *프로세스정확히 무엇 인지 정리한다.


TL;DR — 한 줄 결론

프로세스OS 가 *디스크 의 파일 (프로그램) 에게 주소 공간 + 핸들 + 권한발급해 *살아 움직이게 만든 실행 컨테이너. 프로세스 자체일하지 않는다그 안의 *스레드코어 의 *시간 슬라이스얻어 명령어를 실행한다. 프로세스격리 단위 (메모리/권한), 스레드스케줄 단위 (CPU 시간), 코어물리 실행 단위. 셋 의 분리현대 OS 의 *멀티태스킹 의 본질.


1. 학습 사슬 의 *전체 그림 — *왜 이 7 개 가 한 줄에 묶이는가**

질문 자 의 사슬을 다시 그려 보자.

[ CPU ]                      ← 물리. 실리콘.
   ↓                         (회로 위에서 명령어를 실제로 실행)
[ 메인보드 ]                  ← 물리. 커패시터·전원·버스.
   ↓                         (CPU·메모리·디스크 를 연결)
[ 메모리 (RAM) ]              ← 물리. 휘발성 D-RAM.
   ↓                         (실행 중인 코드/데이터의 작업대)
[ 프로그램 (디스크 위 파일) ]    ← 정적. ELF / Mach-O / PE.
   ↓
[ 프로세스 (실행 중 인 인스턴스) ] ← OS 가 만든 컨테이너 ★ 이 글의 중심
   ↓
[ 스레드 ]                    ← 프로세스 안의 흐름
   ↓
[ 코어 / 논리 프로세서 ]        ← CPU 의 물리/논리 실행 슬롯

핵심 통찰 : 위 4 개 (CPU·보드·메모리·프로그램)하드웨어 / 정적 이다. 아래 3 개 (프로세스·스레드·코어)실행 시 동적. 프로세스그 경계 에서 정적 파일동적 실행 컨텍스트번역 한다.

다른 표현으로 — 프로그램요리책, 프로세스주방 의 한 자리 에서 재료를 늘어놓고 *지금 요리 중인 상태. 스레드그 자리 의 *손 (왼손·오른손), 코어불판. 주방OS.


2. 프로그램 vs 프로세스 — *왜 굳이 *둘로 나누는가**

2.1 *프로그램 — *디스크 위 의 *죽은 바이트 더미**

/usr/bin/vi바이너리 에디터 로 열어 보면 :

7F 45 4C 46 02 01 01 00 ...   (ELF magic)
.text   : 기계어 명령어 (실제 실행될 코드)
.rodata : 상수 문자열, 임베디드 데이터
.data   : 초기화된 전역 변수
.bss    : 초기값 0 인 전역 변수 (디스크에는 크기만)
.symtab : 디버그용 심볼 테이블

이건 그냥 파일. 클릭 해도 아무 일도 안 일어남. 디스크 의 *수십 KB조용히 *그대로 *있는 상태.

2.2 *프로세스 — *그 파일이 *살아 움직이는 상태**

vi foo.txt 라고 셸 에 입력 하면 — fork() + execve() 시스템 콜로 OS 에 요청. OS 가 다음 을 만든다 :

  1. PID — 프로세스 식별자 (예 : 73482)
  2. 가상 주소 공간0x0 ~ 0xFFFFFFFFFFFFFFFF별도 4 PB 평면 (64-bit)
  3. 메모리 매핑 — 위 ELF 의 .text → 코드 영역, .data → 데이터 영역, bss → 0 으로 채워진 영역RAM 위 페이지 에 매핑
  4. 힙·스택동적 할당 / 함수 호출 프레임 용 영역 새로 잡힘
  5. 파일 디스크립터 테이블stdin/stdout/stderr 세 개로 시작
  6. 신호 핸들러SIGINT, SIGTERM 등 어떻게 처리 할 지
  7. 권한어떤 사용자 의 *uid/gid 로 동작*
  8. PCB (Process Control Block) — 위 정보 를 OS 가 정리해 두는 *커널 내부 구조체

vi 라는 프로그램 은 디스크 에 그대로 존재 한다. 하지만 PID 73482이 순간 만 의 *살아 있는 실행 인스턴스. 같은 vi세 번 띄우면 세 개의 프로세스 가 생긴다 — PID 가 모두 다르다. 모두 *같은 디스크 파일 에서 시작했지만 각자 다른 *메모리 상태 를 가진 완전히 분리된 우주.

“여러 개 가능!” — 질문 자 의 메모 가 정확히 *이 지점. 프로그램 1 개 → 프로세스 N 개 가능 한 것이 멀티프로세스 OS 의 출발점.


3. 프로세스 의 *메모리 레이아웃 — *RAM 위 의 *내 영토**

위 의 가상 주소 공간 을 좀 더 자세히 보자. 64-bit Linux 기준 :

0xFFFF_FFFF_FFFF_FFFF  ┌────────────────────────┐
                       │  커널 공간 (kernel)     │  ← 프로세스 가 *직접 못 봄*
0x0000_8000_0000_0000  ├────────────────────────┤
                       │  공유 라이브러리         │  ← libc, libssl 등 mmap
                       ├────────────────────────┤
                       │  스택 (stack) ↓         │  ← 함수 호출, 지역 변수
                       │                        │
                       │  ...                   │
                       │                        │
                       │  힙 (heap) ↑           │  ← malloc / new / GC
                       ├────────────────────────┤
                       │  BSS  (0 으로 초기화)    │
                       │  data (초기값 있는 전역) │
                       │  text (코드 — 읽기전용)  │
0x0000_0000_0040_0000  └────────────────────────┘
0x0000_0000_0000_0000     null 페이지 (segfault 트랩)

몇 가지 짚어 둘 점 :

  • 각 프로세스 가 *자기 만의 0x0 ~ 끝 을 가진다* — 마치 *온 우주 가 자기 것 인 듯 보임. 실제로는 *MMU 가 *가상 → 물리 페이지 매핑* 으로 겹쳐 보이게 *교통 정리.
  • text 영역읽기 전용. 코드 가 자기 자신을 못 고침. 그래서 같은 프로그램 의 두 프로세스 가 *물리 RAM 의 *같은 페이지 를 공유. (이게 프로그램 N 배 띄워도 *RAM 이 N 배 안 늘어남 의 정체)
  • 스택서로 마주 보고 자란다. 둘이 만나면 *out of memory.
  • 공유 라이브러리물리 RAM 한 장모든 프로세스 가 공유. 그래서 libc 한 번 로드 면 *시스템 전체 절약.

3.1 메모리 의 *진짜 의미 — *RAM 위에 *내가 들어가 있는가**

질문 자 의 “메모리는 휘발성 - 반영구” 메모 — 반쯤 맞음. 정확히는 :

  • D-RAM 셀 의 *커패시터전하 로 0/1 을 기억. 전원 끊김 = 즉시 망각 (휘발성).
  • 반도체 자체수십 년 의 *하드웨어 수명. 그래서 RAM 칩반영구적.
  • 프로세스 의 *메모리 영역그 RAM 의 *일부 페이지 들현재 점유 중 인 상태. *프로세스 종료 = 그 페이지 들 이 *OS 의 free list 로 반납.

프로세스 = “내가 RAM 의 *몇 GB 페이지 를 *점유 중이다” 라는 *OS 의 약속.


4. 프로세스 안 의 *스레드실제 일 의 *주체**

여기 가 질문 자 의 “수백 개 가능!” 가 가리키는 곳.

4.1 *스레드 = 같은 주소 공간 안 의 *흐름**

프로세스주소 공간 을 가지고, 그 안 에 *스레드 가 1 개 ~ 수천 개 산다.

구분 프로세스 간 스레드 간
주소 공간 완전 분리 완전 공유
힙 / 전역 변수 분리 공유
스택 분리 각자 별도
파일 디스크립터 분리 (fork 직후 복사) 공유
권한 (uid/gid) 분리 가능 공유
통신 비용 비싸다 (IPC, 소켓, shm) 싸다 (그냥 같은 변수 읽기)
한쪽 죽을 때 남은 쪽 안전 프로세스 통째로 죽음

핵심 결과 :

  • 스레드 가 *빠른 이유변수 하나 공유 = 그냥 같은 메모리 주소 읽기. 프로세스 간 공유 라면 커널 거쳐 *복사 필요.
  • 스레드 가 *위험한 이유같은 메모리두 손 이 동시에 쓰는 순간 race condition. mutex / atomic / volatile 같은 동기화 도구 가 필요.
  • 프로세스 가 *느리지만 안전한 이유주소 공간 분리한 쪽 의 *segfault다른 쪽 에 *전혀 영향 없음.

4.2 현실 의 *스레드 수 — *수십 ~ 수천**

# macOS / Linux 에서 특정 프로세스 의 스레드 수
$ ps -M <PID>          # macOS
$ ps -T -p <PID>       # Linux
$ cat /proc/<PID>/status | grep Threads   # Linux

# 시스템 전체 스레드 수
$ ps -eLf | wc -l       # Linux

예 시 (내 맥 어디서 든 ) :

프로세스 평소 스레드 수
kernel_task 200 ~ 500
WindowServer 50 ~ 100
Chrome (탭 별) 각 10 ~ 30
IntelliJ IDEA 200 ~ 400
Slack (Electron) 60 ~ 150
Spring Boot 서버 50 ~ 300 (Tomcat thread pool + GC + …)

“왜 *Spring Boot 1 개 띄웠는데 *스레드 가 100 개 인가”* 의 답 — Tomcat NIO 풀 (200) + GC (4) + JIT (2~4) + 백그라운드 작업 (수십) 의 합. 프로세스 1 개 안에 *작은 도시.

4.3 JVM 의 *특수성 — *언어 가 정한 *스레드 모델**

Java언어 차원 에서 스레드first-class 로 본다. Thread t = new Thread(...)OS 의 *네이티브 스레드바로 매핑. 그래서 JVM 프로세스 안 에 *200 ~ 400 스레드일상.

Go 는 다르다 — goroutine수만 개 만들어도 OS 스레드는 코어 수 만큼. 언어 런타임M:N 스케줄링.

Node.js 는 더 다르다 — 메인 스레드 1 개 + libuv 풀 (4 ~ 128) + V8 GC. 대부분 *이벤트 루프 1 개 가 다 함.

같은 OS / 같은 프로세스 모델 위에서 언어가 어떤 *동시성 추상화얹는가 의 차이*.


5. 코어 와 의 만남스레드 가 *진짜 실행 되는 순간**

스레드 가 *명령어코어 의 실행 파이프라인 에 던지는 순간* — 그게 실행.

5.1 코어 의 *시간 분할 — *Round-Robin + 우선순위**

대부분 의 OS 스케줄러 :

  1. 준비된 스레드 들우선순위 큐 에 줄 세움.
  2. 코어 1 개비면 *가장 높은 우선순위 의 *준비된 스레드 를 꺼냄.
  3. 시간 슬라이스 (예 : 1 ~ 10 ms) 동안 실행.
  4. 시간 다 쓰면 *큐 뒤로 보내고 다음 스레드컨텍스트 스위치.

컨텍스트 스위치비용수 ~ 수십 마이크로초. 왜 비싼가 :

  • 현재 스레드 의 *모든 레지스터 (수십 개) 를 *PCB 에 저장
  • 다음 스레드 의 *레지스터 를 *PCB 에서 복원
  • MMU 의 *TLB (Translation Lookaside Buffer) 가 *무효화다음 메모리 접근 들 이 *느려짐
  • L1/L2 캐시식음이전 스레드 의 *hot data다음 스레드 에 게 무의미

“코어 수 < 스레드 수” 인데 *왜 *모두 *동시에 도는 듯 한가* — 코어 가 *초당 수백 번 *스위치 하며 공평하게 나눠씀. 체감상 *동시 실행.

5.2 P-core / E-core — *Apple Silicon 의 *비대칭 멀티코어**

질문 자 의 “성능 코어 8 + 효율 코어 4”이게 *Apple 의 *Performance / Efficiency core 분리.

  • P-core고성능 / 고전력. Xcode 빌드 / 비디오 인코딩 / JIT 같은 CPU 집약 작업.
  • E-core저전력 / 적당한 성능. Spotlight 인덱싱 / 로그 수집 / 음악 재생 / 화면 슬립 같은 백그라운드.

macOS QoS (Quality of Service) API스레드 가 *자신의 우선순위OS 에 알린다. 높은 QoS = P-core 우선, 낮은 QoS = E-core. 결과내 키 입력은 P-core, 백그라운드 폴링 은 E-core. 팬 안 돌고 배터리 절약.

Linux대규모 서버 CPU (예 : AMD EPYC) — 대신 *NUMA (Non-Uniform Memory Access) 노드 단위 분리. *스레드 가 *어느 NUMA 노드 에 묶일 것 인가성능 의 결정 요소.

5.3 논리 프로세서 (Hyper-Threading / SMT)

질문 자 의 “코어/논리 프로세서” 표현 이 정확.

  • 물리 코어 1 개2 개의 논리 프로세서 (= 하드웨어 스레드)OS 에 보임Intel Hyper-Threading.
  • 하나의 코어 의 *명령어 파이프라인놀고 있을 때 *두 번째 스레드 의 명령그 빈자리 를 채움.
  • 완전한 2 배 성능 은 아님. 실효 1.2 ~ 1.4 배.
  • Apple M 시리즈Hyper-Threading 안 씀. 물리 = 논리.

그래서 *htop 에서 내 맥 의 코어 가 12 개 로 보이고 Intel 코어 6 개 CPU12 개로 보이는데 *실제 처리량 은 다름.


6. 실전 — *지금 *내 맥 에 *떠 있는 우주 보기**

6.1 프로세스 목록 - 가장 단순한 시작

$ ps -ef | head        # 모든 프로세스 (옛날 방식)
$ ps aux | head        # 메모리·CPU 포함 (BSD 방식)
$ pgrep -fl java       # java 관련 프로세스 만
$ pstree -p            # 부모-자식 트리 (Linux)

핵심 컬럼 :

컬럼 의미
PID 프로세스 식별자
PPID 부모 프로세스 의 PID — 모든 프로세스 가 *어떤 프로세스 에서 fork
UID 누구 권한 으로 도는가
%CPU / %MEM 자원 점유율
STAT 상태 — R (실행), S (대기), Z (좀비), T (정지)
TIME 누적 CPU 시간 (실제 시간 아님)

6.2 프로세스 의 *해부macOS 의 *Activity Monitor / *Linux 의 *htop**

htop (Linux) / top (macOS) :

  • CPU 코어 별 사용률각 코어 가 *얼마나 일 중인가
  • 프로세스 별 % CPU / RES (실제 RAM) / VIRT (가상 메모리)
  • RES실제 *물리 RAM 차지. 진짜 메모리 압박 의 척도.
  • VIRT주소 공간 의 *예약 크기. 보통 훨씬 크다대부분 미사용. 놀라지 말 것.

6.3 스레드 단위 *까지 들여다 보기

# Linux : 특정 프로세스 의 *모든 스레드*
$ ps -T -p <PID>

# Linux : 스레드 별 CPU 사용률
$ top -H -p <PID>

# Java : JVM 안 의 스레드 덤프
$ jstack <PID>
$ jcmd <PID> Thread.print

# macOS : sample 로 스택 샘플링
$ sample <PID> 5         # 5 초간 샘플

jstack 한 번 떠보면 — Java 프로세스 안 의 *200 ~ 400 스레드각자 무슨 일그 순간 하고 있는지 콜 스택 으로 보임. 디버깅 의 *결정적 도구.

6.4 프로세스 의 *디스크 / 네트워크 시야*

# 어떤 파일 / 소켓 을 *열고 있는가*
$ lsof -p <PID>

# 어떤 시스템 콜 을 *부르고 있는가* (Linux)
$ strace -p <PID>

# macOS 대응
$ dtrace -n 'syscall:::entry /pid == <PID>/ { @[probefunc] = count(); }'

프로세스 가 *무엇 을 만지는가전모열린 파일 + 소켓 + 메모리 매핑 + 시스템 콜. 이게 *블랙박스 가 아니라 *완전 투명Unix 의 철학.


7. 프로세스 *생명 주기 — *태어남 ~ 죽음**

   fork()          execve()         (실행)         exit()       wait()
부모 ──────► 자식 ──────► 새 프로그램 ──────► 종료 ──────► 부모 가 회수
              (자식)        (자식)        (좀비)        (정리됨)

7.1 fork() — 자기 복제

부모 가 자신 의 *복사본 자식 프로세스 를 만든다. 주소 공간 / 파일 디스크립터 / 권한 모두 복사. PID 만 다름. 실은 *Copy-on-Write물리 RAM 은 공유 하다가 쓸 때만 분기.

7.2 execve() — 변신

방금 만든 자식 이 자기 자신을 *다른 프로그램 으로 덮어 쓴다. 주소 공간 의 모든 내용 이 *새 ELF 로 교체. PID 는 유지. 셸 의 *명령어 실행바로 이 흐름fork + execve짝궁.

7.3 exit() / wait() — 죽음과 회수

자식 이 끝나면 exit code최종 자원 사용량 만 남기고 PCB 가 좀비 상태. 부모 가 *wait()읽어 가야 진짜 사라짐. 부모 가 까먹 으면좀비 누적 (의미 있는 자원은 아니지만 PID 테이블 차지).

Docker / k8s 에서 *PID 1 의 *zombie reaping왜 중요 한가* — 컨테이너 의 init 프로세스가 *자식 좀비를 *부지런히 회수 안 하면 PID 고갈.

7.4 시그널 — 프로세스 간 의 비동기 통신

  • kill -9 <PID>SIGKILL. 프로세스 가 거부 못 함. 강제 종료.
  • kill -15 <PID>SIGTERM. 프로세스 가 *우아하게 정리 할 기회. *기본.
  • kill -2 <PID>SIGINT. Ctrl+C 와 같음.
  • kill -1 <PID>SIGHUP. 설정 다시 읽어. 데몬 들 이 *이걸 *재설정 신호 로 자주 씀*.

시그널 핸들러프로세스 의 *비동기 인터럽트 처리. 잘 짠 프로세스SIGTERM 받으면 연결 끊고 *버퍼 비우고 *우아한 종료. 못 짠 프로세스kill -9 받아야만 죽는다.


8. 현실 적 응용 — *이 사슬 이 *어디 에서 *작동 하는가**

8.1 Spring Boot 서버 한 대프로세스 1 개 의 작은 도시

  • JVM 프로세스 1 개 (PID 가 1 개).
  • 그 안 에 *Tomcat NIO 풀 200 스레드 + GC 4 스레드 + JIT 4 스레드 + Hikari 풀 30 스레드 + @Async 풀 N 개.
  • 총 *250 ~ 300 스레드. 대부분 *blocked / waiting. 실제 동시 실행코어 수 만큼.
  • 내 맥 코어 12 개동시 12 스레드 실행. 나머지는 대기.
  • 요청 폭주 시Tomcat 풀 이 *고갈 → 요청 대기 큐응답 지연. 프로세스 가 *죽지는 않음 — *느려질 뿐.

8.2 *k8s 의 *Pod = 프로세스 의 *현대 적 포장**

Pod = 1 개 이상 의 컨테이너 = 1 개 이상 의 *Linux 프로세스 + cgroup + namespace.

  • namespace프로세스 의 *시야 격리자신 만의 PID 1 / 네트워크 / 마운트.
  • cgroup프로세스 의 *자원 한도CPU 1 코어 / RAM 2 GB 제한.

k8s 의 *대단함별 게 아니라 *프로세스 의 *7 가지 OS 추상화 (namespace) + cgroup + 선언적 배포 의 조합. *내려가 보면 *결국 *프로세스.

8.3 *IntelliJ 가 *왜 무거운가**

  • JVM 프로세스 1 개 (idea) 가 200 ~ 400 스레드.
  • 그 옆에 *fsnotifier (네이티브 프로세스) + Gradle 데몬 (별도 JVM 프로세스) + Kotlin 컴파일 데몬.
  • 총 *5 ~ 9 프로세스. 각자 별도 주소 공간. 합쳐서 *RAM 4 ~ 8 GB.

“왜 *프로세스 한 개 짜리 *VSCode 보다 IntelliJ 가 무거운가”프로세스 + 스레드 모델 의 *근본적 차이. VSCodeElectron / Node 의 *얇은 단일 흐름. IntelliJ진짜 JVM 안 에 수백 스레드. 기능 의 깊이 = 자원 의 깊이.


9. 학습 사슬 다시 읽기 — 결론

질문 자 의 사슬 을 오늘 정리 의 *언어로 *다시 쓰면 :

무엇 시간 척도 우리가 직접 만지는가
CPU 물리 실리콘 (성능 코어 + 효율 코어) ns ❌ (하드웨어)
메인보드 연결 회로 + 전원 + 버스 수명 = 수년
RAM D-RAM 셀 의 *전하 상태 ns ~ µs ❌ (OS 가 관리)
프로그램 디스크 위 의 *바이너리 파일 영구 (수정 전까지) ✅ (컴파일 결과물)
프로세스 OS 가 *RAM + 권한 + 핸들 을 묶어 살린 인스턴스 초 ~ 시간 ~ 일 ✅ (실행/종료)
스레드 프로세스 안 의 *실행 흐름 µs ~ ms ✅ (생성/제어)
코어 (논리) CPU 의 *물리/논리 실행 슬롯 ns (사이클) ❌ (스케줄러가 매핑)

프로세스 가 *경계. *그 위우리 가 *만지는 추상화, 그 아래OS 와 HW 가 *알아서 하는 영역. 그 사이 에서 프로세스 의 *PCB모든 정보 의 *허브 로 작동*.

“여러 개 가능!”프로세스 도, 스레드 도. 그 여러 개코어 수완전히 별개로 *논리적으로 *수백 ~ 수천 가능 한 이유 — OS 의 *시간 분할 + 가상 메모리 의 마법*.

“실제 처리”코어. 나머지 는 모두 *그 코어 의 시간 을 어떻게 잘게 나눠 쓰는가추상화 들.

이 사슬을 손에 잡힌 채시스템 을 보는 눈그게 *시니어 의 *기본 시각. 이 위에 *멀티프로세스 모델 / 멀티스레드 모델 / Reactive / Coroutine동시성 의 모든 발전얹힌다.


10. 다음 으로 *권 하는 읽기 의 *방향**

  • 프로세스 의 *생성 / 종료 의 깊이* — Operating Systems : Three Easy Pieces (무료 PDF)
  • 컨텍스트 스위치진짜 비용 측정perf stat, bpftrace
  • Linux 컨테이너 / cgroup / namespaceDocker 내부 의 *프로세스 격리 메커니즘
  • JVM 스레드 모델 의 미래Project Loom 의 *Virtual Thread (Java 21+)
  • Reactive / 코루틴 의 *프로세스 - 스레드 - 태스크3 단 계 모델Kotlin Coroutine, Spring WebFlux, Node 이벤트 루프

지금 시점 의 권장셸 에서 *ps, top, htop, lsof습관적으로 보자. 프로세스 의 우주 를 *손에 익히는 것 만큼 시스템 직관늘리는 길없다.