프로세스 vs 스레드 예시 정리
항목 | 프로세스 (Process) | 쓰레드 (Thread) |
정의 | 실행 중인 프로그램 | 프로세스 내에서 실행되는 작은 작업 단위 |
메모리 | 독립적인 메모리 공간 가짐 | 같은 프로세스 내에서 메모리 공유 |
통신 방식 | 프로세스 간 통신(IPC) 필요 (느림) | 같은 프로세스 내에서 데이터 공유 가능 (빠름) |
독립성 | 하나 죽어도 다른 프로세스 영향 없음 | 하나 죽으면 같은 프로세스 내 다른 스레드도 영향받을 수 있음 |
생성 비용 | 무거움 (새로운 메모리 할당 필요) | 가벼움 (메모리 공유하니까 부담 적음) |
실제 예시 | 크롬 탭 1개, 포토샵, 게임 실행 | 포토샵의 필터 적용, 게임 내 NPC AI |
팀 단위 예시 | 한 회사의 각 부서 (독립적, 서로 협업하려면 연락 필요) | 부서 내의 개별 직원들 (같은 공간에서 빠르게 협업 가능) |
집 예시 | 여러 가구가 사는 아파트 (독립적인 생활, 이웃과 소통 어려움) | 같은 집에 사는 가족 (방은 다르지만 공간 공유, 쉽게 대화 가능) |
크롬 vs 게임 프로세스 비교
상황 프로세스 개수 설명
크롬 탭 5개 | 5개 프로세스 | 크롬은 멀티 프로세스 아키텍처라서 탭마다 개별 프로세스로 실행됨 (독립적) |
게임 1개 실행 | 1개 프로세스 | 게임은 보통 하나의 프로세스로 실행되지만, 내부적으로 많은 스레드를 사용함 |
프로세스 유형
포그라운드 프로세스 - 사용자가 보는 공간에서 사용자와 상호작용,실행
백그라운드 프로세스 - 사용자가 보지 못하는 곳에서 실행
데몬 : 사용자와 별다른 상호작용 없이 주어진 작업만 수행하는 특별한 백그라운드 프로세스
윈도우에선 service라고 부름
메모리 영역(사용자 영역)
프로세스의 정보가 저장되는 메모리 영역
1.코드 영역(텍스트 영역)
- 실행 가능한 명령어가 저장되는 공간
- 읽기전용(read-only) / CPU가 읽고 실행할 명령어가 담겨 있어서 쓰기가 금지
2.데이터 영역
- 프로그램이 실행되는 동안 유지할 데이터가 저장되는 공간
- 저장 데이터 : 정적변수 , 전역변수
정적 할당 영역(코드영역,데이터 영역) - 프로그램 실행 도중 크기가 변하지 않음
동적 할당 영역(힙 영역, 스택 영역) - 크기가 변할 수 있음
📌 BSS(Block Started by Symbol) 영역이란?
BSS는 초기화되지 않은 전역 변수와 정적 변수가 저장되는 메모리 영역
----------------------
| Command Line Args | (프로그램 실행 시 인자)
----------------------
| Stack | (지역 변수, 함수 호출 정보)
----------------------
| Heap | (동적 할당 메모리)
----------------------
| BSS | (초기화되지 않은 전역/정적 변수)
----------------------
| Data | (초기화된 전역/정적 변수)
----------------------
| Code | (실행 코드, 텍스트 섹션)
----------------------
✅ 특징
- 초기값이 없는 전역 변수, 정적 변수(static) 저장.
- 실행 시 자동으로 0으로 초기화됨.
- 실행 파일 크기에 영향을 주지 않음 (실제로는 런타임에 메모리가 할당됨).
✅ 예제 코드
int global_var; // BSS 영역 (초기화되지 않음)
static int static_var; // BSS 영역 (초기화되지 않음)
int main() {
return 0;
}
✅ BSS vs DATA 차이점
- BSS: 초기값 없는 변수 → 실행 시 0으로 초기화
- DATA: 초기값 있는 변수 → 실행 파일에 저장됨
3.힙 영역
프로그램을 만드는 사용자(개발자)가 직접 할당 가능한 저장공간
프로그램 실행 도중 비교적 자유롭게 할당하여 사용 가능한 메모리 공간
—> 힙 영역에 메모리 공간 할당 시, 언젠가 반환해야 함. —> 메모리 누수 때문임
가비지 컬렉션(garbage collection) - 언어 자체적으로 힙 메모리 해제
4.스택 영역
일시적으로 사용할 값들이 저장되는 공간
저장되는 데이터
- 함수의 실행이 끝나면 사라지는 매개변수
- 지역변수
- 함수 복귀 주소
스택 트레이스 : 특정 시점에 스택 영역에 저장된 함수호출 정보
특정 시점이란?
- 에러가 발생한 순간
- 디버깅 도중 특정 라인에서 멈춘 순간
- 함수호출이 중첩된 상태에서 현재 위치를 기록하는 순간
이런 시점에 프로그램의 스택(함수 호출 정보) 을 기록한 것이 스택 트레이스(Stack Trace)다.
예시
Error: 문제가 생김!
at c (<anonymous>:7:9)
at b (<anonymous>:3:3)
at a (<anonymous>:2:3)
at <anonymous>:11:1
PCB와 문맥 교환
프로세스 제어 블록(PCB) | Process Control Block
운영체제가 프로세스를 관리하기 위해 저장하는 정보 덩어리
각 프로세스의 신분증.
운영체제는 프로세스를 실행하면서 관리해야 할 정보가 많은데 , 그걸 한군데 모아 놓은게 PCB다.
프로세스가 실행중이든, 대기중이든, 종료되든 간에 이 정보가 있어야 한다.
📌 PCB에 들어가는 정보
PCB에는 보통 아래 같은 정보들이 저장되어 있다.
- 프로세스 ID (PID)
- 각 프로세스를 구분하는 고유한 번호
- 프로세스 상태 (Process State)
- 실행 중(Running), 준비(Ready), 대기(Waiting) 등의 상태
- CPU 레지스터 값
- 프로세스가 사용하던 레지스터 값들 (문맥 교환할 때 저장 & 복원)
- 프로그램 카운터 (PC, Program Counter)
- 다음에 실행할 명령어 주소
- 메모리 관리 정보
- 해당 프로세스가 사용하는 메모리 주소, 스택, 힙 정보
- 파일 및 I/O 정보
- 프로세스가 열고 있는 파일, 사용 중인 입출력 장치 정보
- 우선순위 (Priority)
- 실행 순서를 결정할 때 사용됨
📌 PCB가 하는 일
✔ 문맥 교환(Context Switch) 할 때, 현재 프로세스 상태 저장 & 복원
✔ 여러 프로세스를 동시에 관리할 때, 누가 언제 실행될지 결정
✔ CPU 스케줄링 & 프로세스 관리의 핵심 역할
문맥이란?
프로세스의 수행을 재기하기 위해 기억해야 할 정보
해당 프로세스의 PCB에 명시된다.
문맥교환(Context Switching)이란?
CPU가 현재 실행중인 프로세스를 다른 프로세스로 변경하는 과정
ex) A작업 → B작업으로 변환할 때 A상태 저장하고 B 상태 불러오는 과정임!
CPU는 한번에 하나의 프로세스만 실행 가능
여러개 실행하려면 CPU가 빠르게 프로세스를 바궈가며 실행해야 해서 문맥교환이 필요함.
문맥교환 과정
- 현재 실행중인 프로세스 상태저장
- 새로운 프로세스의 상태 불러오기 (PCB에서 읽어옴)
- CPU가 새로운 프로세스 실행
이 과정이 빠르게 반복되면서 멀티태스킹처럼 보이게 하는 원리다.
🛠 문맥 교환이 발생하는 경우
✅ CPU 스케줄링 – 운영체제가 더 높은 우선순위 프로세스를 실행하려고 교체할 때
✅ 인터럽트(Interrupt) 처리 – 키보드 입력, 마우스 클릭, 시스템 콜 등 외부 이벤트가 발생할 때
✅ 입출력(IO) 대기 – 한 프로세스가 디스크 읽기 같은 작업으로 멈춰 있을 때, 다른 프로세스를 실행
문맥교환이 너무 자주 발생하면
캐시 미스가 발생할 가능성이 높아짐 → 실행할 프로세스의 내용을 가져오는 작업 빈번 → 큰 오버헤드
PCB는 커널 내에 프로세스 테이블 의 형태로 관리되는 경우가 많다.
좀비 프로세스 - 프로세스가 비정상 종료 되어 사용한 자원이 회수되었음에도 프로세스 테이블에 종료된 프로세스의 PCB가 남아 있는 경우
오픈 소스 S/W 운영체제 리눅스
소스코드가 공개된 S/W를 open soucre software라고 부르는데,
리눅스는 그 중에서도 많은 개발자들이 사용하는 오픈소스 s/w software다.
안드로이드 등 다양한 운영체제에 영향을 끼쳤고 오늘날 많은 서버 컴퓨터 환경에서 활용한다.
메모리에 적재된 프로세스들은 한정된 시간동안 번갈아가며 실행되면
이때 프로세스가 실행된다 = 운영체제에 의해 CPU의 자원을 할당받았다
타이머 인터럽트(타임아웃 인터럽트) : 시간이 끝났음을 알리는 인터럽트
프로세스 상태
하나의 프로세스는 여러 상태를 거치며 실행 됨
생성 → 준비 → 실행 → 대기 → 종료
- 생성 (New) → 프로세스가 생성되었지만 아직 실행 준비 중인 상태
- 준비 (Ready) → CPU 할당을 기다리는 상태 (실행 대기 중)
- 실행 (Running) → CPU를 할당받아 명령을 실행하는 상태
- 대기 (Waiting) → I/O 작업 등으로 CPU를 사용하지 않고 기다리는 상태
- 종료 (Terminated) → 실행이 끝나거나 강제 종료된 상태
블로킹 입출력 , 논블로킹 입출력
- 블로킹 I/O → 요청한 작업이 완료될 때까지 대기 (CPU가 놀게 됨)
- 논블로킹 I/O → 요청 후 바로 반환, 작업이 끝나지 않아도 다른 작업 수행 가능
멀티 프로세스와 멀티 쓰레드 (책 내용 기반)
멀티 프로세스 → 여러 개의 프로세스를 생성하여 병렬로 실행
- 각각 PID(프로세스 ID)값이 다르고 독립적으로 할당되어 다른 프로세스 영향 거의 X
- 장점 → 하나의 프로세스가 죽어도 다른 프로세스에 영향 없음
- 단점 → 메모리 사용량 많고, 프로세스 간 통신 비용이 큼
- 사용 사례: 크롬 브라우저 (각 탭을 별도 프로세스로 실행), 서버 애플리케이션 (예: Apache, Nginx), 백그라운드 서비스
멀티 쓰레드 → 하나의 프로세스 내에서 여러 스레드가 동시에 실행
- 하나의 쓰레드는 스레드ID,프로그램 카운터,레지스터 값, 스택 등으로 구성 됨
- 장점 → 메모리 공유로 자원 효율이 좋고 속도가 빠름
- 단점 → 하나의 스레드가 문제 생기면 전체 프로세스에 영향 가능
- 사용 사례: 게임 (그래픽, 사운드, AI 처리 분리), 채팅 앱 (메시지 수신 & UI 업데이트 동시에), 웹 서버 (하나의 요청마다 스레드 생성)
멀티 프로세스는 안정성이 중요할 때, 멀티 쓰레드는 속도가 중요할 때 사용
그럼 쓰레드와 프로세스는 각각 어디에 쓰는 것인가?
📌 프로세스 사용 사례
- 서로 독립적인 작업을 병렬로 수행해야 할 때
- 한 프로세스가 죽어도 다른 프로세스에 영향을 주지 않아야 할 때
- 메모리를 많이 사용해도 괜찮고, 안정성이 중요한 경우
📍사용 예시
- 웹 브라우저 (크롬, 파이어폭스) → 각 탭을 독립적인 프로세스로 실행
- 데이터베이스 서버 (MySQL, PostgreSQL) → 각 클라이언트 요청을 별도 프로세스로 처리
- 운영체제 백그라운드 서비스 → 독립적인 작업 (예: 프린트 스풀러, 시스템 업데이트)
📌 쓰레드 사용 사례
- 같은 자원을 공유하면서 빠르게 병렬 처리해야 할 때
- 메모리를 아껴야 하고, 응답 속도가 중요한 경우
- 자원 공유가 필요하지만 동기화 문제가 심하지 않은 경우
📍사용 예시
- 게임 엔진 → 그래픽 렌더링, AI 연산, 사운드 처리를 각각 스레드로 수행
- 웹 서버 (Nginx, Tomcat) → 여러 요청을 빠르게 처리하기 위해 스레드 사용
- 채팅 애플리케이션 (카카오톡, 디스코드) → UI 업데이트와 메시지 수신을 동시에 처리
가장 큰 차이점은 자원공유 여부다.
- 다른 프로세스들은 자원 공유 X
- 같은 프로세스를 사용하는 여러 스레드는 자원을 공유
- 스레드들은 동일한 주소 공간의 코드,데이터, 힙영역을 공유한다.
그래서 멀티 프로세스 환경 : 1 프로세스 문제 → 다른 프로세스 지장 거의 X
멀티 스레드 환경 : 1쓰레드 문제 → 프로세스 전체가 문제 될 수도 있음
+쓰레드 조인이 뭐냐?
쓰레드를 여러 개 만들었을 때 하나의 쓰레드가 끝날 때까지 다른 쓰레드가 기다리게 만드는 기능
A쓰레드가 B쓰레드 종료될 때까지 기다리는 것
- 순서보장
- 동기화 문제 방지
- 리소스 관리
멀티 쓰레드에서 실행 순서를 보장하고 싶을 때 사용
데이터 동기화나 리소스 관리에도 도움 됨
프로세스 간 통신
프로세스는 기본적으로 자원을 공유하지 않는다.
그렇지만 프로세스 간에도 자원을 공유하고 데이터를 주고 받을 수 있는 방법이 있는데
이를 프로세스 간 통신 (IPC , Inter-Process Communication) 라고 한다.
방법은 공유 메모리 , 메세지 전달이 있다.
공유 메모리 (Shared Memory)
- 특정 메모리 공간을 여러 프로세스가 공유
- 마치 자신의 메모리처럼 읽고 쓰면서 통신
- 장점: 속도가 빠름 (커널을 거치지 않음)
- 단점: 동기화(락, 세마포어) 필요
💡 예시:
- 생산자-소비자 모델 (한 프로세스가 데이터를 쓰고, 다른 프로세스가 읽음)
- 데이터베이스 캐시 (여러 프로세스가 같은 메모리 캐시 공유)
메시지 전달 (Message Passing)
- 프로세스끼리 커널을 통해 메시지를 주고받으며 통신
- 장점: 안전하고 동기화 필요 없음
- 단점: 속도가 느릴 수 있음
💡 예시:
- 소켓(Socket) 통신 → 클라이언트-서버 간 네트워크 메시지 전달
- 메시지 큐(Message Queue) → 대기열을 통해 프로세스 간 데이터 교환
- 파이프(Pipe) - 단방향 프로세스 간의 통신 도구
- 원격 프로시져 호출
파이프 (Pipe) - 메시지 전달의 한 방법
- 한 프로세스의 출력을 다른 프로세스의 입력으로 연결
- 주로 부모-자식 프로세스 간 통신에 사용됨
- 장점: 단순한 프로세스 간 데이터 전달 가능
- 단점: 한 방향 통신 (일반 파이프) or 양방향이어도 설정 필요 (네임드 파이프)
- 단방향 : 물길처럼 단방향 , A → B 라면 B → A는 안됨
- 양방향 : 데이터가 양쪽으로 이동 가능 (예 : A ↔ B)
- 지명(네임드) 파이프 또는 소켓을 사용해야 함
- 지명 파이프(Named Pipe, FIFO - First In First Out)
- 독립적인 프로세스 간에도 통신 가능
- 파일 시스템에서 이름 을 가지는 파이프
- 익명 파이프 (Anonymous Pipe)
- 부모-자식 프로세스간 사용 가능
- 독립적인 두개의 프로세스는 사용할 수 없음 (같은 프로세스 그룹 내에서만 사용가능)
💡 예시:
- 리눅스 명령어에서 사용:(ls -l의 출력을 grep이 입력으로 받음)
- ls -l | grep ".txt"
- 부모-자식 프로세스 간 데이터 주고받기
구분 단방향(1-way) 양방향(2-way)
익명 파이프 | O | X (기본적으로 불가능) |
지명 파이프 (FIFO) | O | O (설정하면 가능) |
구분 익명 파이프 지명 파이프 (FIFO)
부모-자식 통신 가능 | ✅ | ✅ |
독립된 프로세스 간 통신 가능 | ❌ | ✅ |
파일 시스템에 존재 | ❌ | ✅ |
속도 | 빠름 | 조금 느림 |
구현 방법 | pipe() | mkfifo() |
⚡ 즉, 부모-자식 간 간단한 통신 → 익명 파이프
⚡ 독립적인 프로세스 간 통신 → 지명 파이프(FIFO)
시그널(비동기)
시그널은 운영체제가 프로세스에게 특정 이벤트가 발생했음을 알리는 방식이다.
즉, 비동기적으로 프로세스에 전달되는 인터럽트(Interrupt) 같은 것.
- 누가 보낼 수 있나?
- 커널(운영체제) → 예: 오류 발생, 종료 요청 등
- 프로세스 → 예: kill 명령어로 다른 프로세스 종료
- 어떻게 동작하나?
- 시그널이 발생하면, 해당 프로세스는 기본 동작(Default Action) 을 수행하거나,
- 사용자 정의 핸들러(Signal Handler) 를 설정해서 직접 처리 가능
주요 시그널 종류 (리눅스 기준)
시그널 값 설명 기본 동작
SIGHUP | 1 | 터미널 종료 | 종료 |
SIGINT | 2 | 키보드 인터럽트 (Ctrl + C) | 종료 |
SIGQUIT | 3 | 키보드 종료 (Ctrl + ) | 코어 덤프 후 종료 |
SIGILL | 4 | 잘못된 명령 실행 | 코어 덤프 후 종료 |
SIGABRT | 6 | abort() 함수 호출 | 코어 덤프 후 종료 |
SIGFPE | 8 | 잘못된 연산 (0으로 나누기 등) | 코어 덤프 후 종료 |
SIGKILL | 9 | 강제 종료 (해제 불가능) | 종료 |
SIGSEGV | 11 | 잘못된 메모리 접근 (Segmentation Fault) | 코어 덤프 후 종료 |
SIGPIPE | 13 | 파이프가 깨짐 (Broken Pipe) | 종료 |
SIGALRM | 14 | alarm() 타이머 만료 | 종료 |
SIGTERM | 15 | 정상 종료 요청 (기본 종료 신호) | 종료 |
SIGCHLD | 17 | 자식 프로세스 종료 시 부모에게 전달 | 무시 |
SIGCONT | 18 | 일시 중지된 프로세스 재개 | 실행 재개 |
SIGSTOP | 19 | 프로세스 일시 정지 (Ctrl + Z, 해제 불가능) | 일시 정지 |
SIGTSTP | 20 | 터미널에서 일시 정지 (Ctrl + Z, 해제 가능) | 일시 정지 |
SIGTTIN | 21 | 백그라운드 프로세스가 입력 시도 | 일시 정지 |
SIGTTOU | 22 | 백그라운드 프로세스가 출력 시도 | 일시 정지 |
시그널 처리 방법
기본 동작(Default Action)
- 시그널이 발생하면 OS가 정해놓은 기본 동작을 수행함
- 예: SIGKILL → 강제 종료
- 예: SIGSEGV → 잘못된 메모리 접근 → 코어 덤프 후 종료
무시(Ignore)
- SIGCHLD 같은 일부 시그널은 기본적으로 무시됨
- SIGKILL 및 SIGSTOP은 무시할 수 없음
시그널 핸들러(Signal Handler)
- 시그널 핸들러는 특정 시그널(SIGINT, SIGTERM 등)이 프로세스에 전달될 때 실행되는 함수임.
📌 정리
- 시그널(Signal) → 프로세스에게 발생한 이벤트를 알리는 인터럽트 같은 것
- 기본 동작 → 종료, 코어 덤프, 무시, 일시 정지 등
- 주요 시그널
- SIGINT (Ctrl+C) → 종료
- SIGKILL (kill -9) → 강제 종료
- SIGSTOP (Ctrl+Z) → 일시 정지✅ 처리 방법 → 기본 동작 수행 / 무시 / 사용자 정의 핸들러 설정✅ 시그널 보내는 방법 → kill 명령어, raise(), kill() 함수
시그널마다 수행할 기본 동작이 정해져 있다.
대부분 프로세스를 종료 & 무시 & 코어덤프를 ****생성한다.
코어 덤프
프로세스가 비정상적으로 종료될 때, 해당 시점의 메모리 정보(레지스터, 스택, 힙 등) 를 파일로 저장하는 것.
- 코어 덤프 발생 시, "Segmentation fault (core dumped)" 또는 "Aborted (core dumped)" 출력됨
- Python은 일반적으로 메모리 보호 기능이 강하지만, ctypes 같은 저수준 접근 시 코어 덤프 발생 가능
- 스택 오버플로우, 잘못된 메모리 접근 등으로 인해 발생할 수 있음
✅ 예제 1: 잘못된 메모리 접근 (ctypes 이용)
import ctypes
# NULL 포인터 접근 (비정상 메모리 접근)
ctypes.string_at(0)
💥 실행 결과 (리눅스 환경)
Segmentation fault (core dumped)
'Study > CS' 카테고리의 다른 글
03 - 4 CPU 스케줄링 (0) | 2025.02.19 |
---|---|
03-3 동기화와 교착 상태 (0) | 2025.02.12 |
03-1 운영체제 큰그림 (0) | 2025.02.12 |
02-5 보조기억장치와 입출력 장치 (0) | 2025.02.05 |
02-4 메모리 (0) | 2025.02.05 |