본문 바로가기
Study/CS

03-2 프로세스와 스레드

by dailycoding777 2025. 2. 12.

프로세스 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         |  (실행 코드, 텍스트 섹션)
----------------------

특징

  1. 초기값이 없는 전역 변수, 정적 변수(static) 저장.
  2. 실행 시 자동으로 0으로 초기화됨.
  3. 실행 파일 크기에 영향을 주지 않음 (실제로는 런타임에 메모리가 할당됨).

예제 코드

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에는 보통 아래 같은 정보들이 저장되어 있다.

  1. 프로세스 ID (PID)
    • 각 프로세스를 구분하는 고유한 번호
  2. 프로세스 상태 (Process State)
    • 실행 중(Running), 준비(Ready), 대기(Waiting) 등의 상태
  3. CPU 레지스터 값
    • 프로세스가 사용하던 레지스터 값들 (문맥 교환할 때 저장 & 복원)
  4. 프로그램 카운터 (PC, Program Counter)
    • 다음에 실행할 명령어 주소
  5. 메모리 관리 정보
    • 해당 프로세스가 사용하는 메모리 주소, 스택, 힙 정보
  6. 파일 및 I/O 정보
    • 프로세스가 열고 있는 파일, 사용 중인 입출력 장치 정보
  7. 우선순위 (Priority)
    • 실행 순서를 결정할 때 사용됨

📌 PCB가 하는 일

문맥 교환(Context Switch) 할 때, 현재 프로세스 상태 저장 & 복원

✔ 여러 프로세스를 동시에 관리할 때, 누가 언제 실행될지 결정

✔ CPU 스케줄링 & 프로세스 관리의 핵심 역할

문맥이란?

프로세스의 수행을 재기하기 위해 기억해야 할 정보

해당 프로세스의 PCB에 명시된다.

문맥교환(Context Switching)이란?

CPU가 현재 실행중인 프로세스를 다른 프로세스로 변경하는 과정

ex) A작업 → B작업으로 변환할 때 A상태 저장하고 B 상태 불러오는 과정임!

 

CPU는 한번에 하나의 프로세스만 실행 가능

여러개 실행하려면 CPU가 빠르게 프로세스를 바궈가며 실행해야 해서 문맥교환이 필요함.

문맥교환 과정

  1. 현재 실행중인 프로세스 상태저장
  2. 새로운 프로세스의 상태 불러오기 (PCB에서 읽어옴)
  3. 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