[CS] 프로세스와 스레드

눈치없어·2025년 2월 17일

프로세스와 메모리 구조

메모리에는 컴퓨터가 실행되는 순간부터 다양한 프로세스(Process) 가 메모리에 적재되어 실행 됨


프로세스의 유형

1️⃣ 포그라운드 프로세스(Foreground Process)

  • 사용자가 직접 보고 상호작용하는 프로세스 (예: 웹 브라우저, 게임 등)

2️⃣ 백그라운드 프로세스(Background Process)

  • 사용자가 보지 못하는 곳에서 실행되는 프로세스 (예: 백업 프로그램, 자동 업데이트 등)

3️⃣ 데몬(Daemon) 프로세스

  • 백그라운드에서 특정 작업을 수행하는 특별한 프로세스
  • 윈도우 운영체제에서는 서비스(Service) 라고 부름

프로세스의 메모리 영역(메모리 영역 중 사용자 영역)

1️⃣ 코드 영역

  • 실행 가능한 명령어가 저장되는 공간
  • 텍스트 영역 이라고도 불림
  • CPU가 읽고 실행할 명령어가 담겨 있으며, 읽기 전용 으로 설정됨

2️⃣ 데이터 영역

  • 프로그램이 실행되는 동안 유지되는 데이터가 저장됨
  • 전역 변수정적 변수가 포함됨
  • 프로그램 실행 중 크기가 변하지 않음 → 정적 할당 영역

BSS 영역
일반적으로 프로세스의 메모리 영역은 크게 코드 영역, 데이터 영역, 힙 영역, 스택 영역으로 구분하지만, 실제로는 BSS 라는 영역도 추가로 구분하는 경우가 있음
BSS 영역은 데이터 영역과 유사하지만 초기화 여부가 다름. 프로그램을 사용하는 동안 유지할 데이터 중 초깃값이 있는 정적변수나 전역변수와 같이 초깃값이 있는 데이터는 데이터 영역에 저장되고, 초깃값이 없는 데이터는 BSS 영역에 저장

3️⃣ 힙 영역

  • 프로그램 실행 도중 동적으로 할당되는 메모리 공간
  • 개발자가 직접 메모리를 할당하고, 사용이 끝나면 반환해야 함
  • 메모리를 반환하지 않으면 메모리 누수 발생 가능
  • 일부 언어(Java, Python 등)는 가비지 컬렉션 으로 자동 해제

4️⃣ 스택 영역

  • 함수 호출 시 임시 데이터를 저장하는 공간
  • 매개변수, 지역 변수, 함수 복귀 주소 저장
  • 함수 실행이 끝나면 자동으로 메모리에서 제거됨
  • 스택 트레이스(Stack Trace)
    - 스택에 저장된 함수 호출 정보를 추적하는 과정
    - 예외 발생 시 문제의 원인을 찾는 데 도움을 줌
    - 디버깅에 유용하게 활용
// 스택 트레이스 예시

Exception in thread "main" java.lang.NullPointerException
    at com.example.myproject.Test.getThis(Test.java:16)
    at com.example.myproject.Test2.getThat(Test2.java:25)
    at com.example.myproject.Bootstrap.main(Bootstrap.java:14)
// NullPointerException 발생 위치를 파일명과 줄 번호로 추적 가능

PCB와 문맥 교환

운영체제가 메모리에 적재된 다수의 프로세스를 관리하려면, 각 프로세스를 식별할 정보가 필요
이 역할을 수행하는 것이 프로세스 제어 블록(PCB, Process Control Block)


프로세스 제어 블록 (PCB)

  • 프로세스와 관련된 다양한 정보를 저장하는 구조체
  • 새로운 프로세스가 생성되면 PCB가 커널 영역에 추가되고, 프로세스가 종료되면 PCB가 제거됨
  • 여러 PCB들은 프로세스 테이블(Process Table) 의 형태로 관리됨

📌 PCB에 저장되는 정보

항목설명
프로세스 ID (PID)프로세스 식별 번호
프로세스 상태실행(Running), 대기(Waiting), 종료(Terminated) 등
CPU 레지스터 값프로그램 카운터, 스택 포인터, 레지스터 값 등
메모리 정보프로세스가 차지하는 메모리 주소 영역
파일 및 I/O 정보프로세스가 사용 중인 파일과 입출력 장치

프로세스 실행과 PCB 관리

운영체제는 새로운 프로세스를 실행하면 PCB를 생성하고, 프로세스가 종료되면 PCB를 제거

  • 프로세스 실행 시 (PCB 추가)
    - 새로운 프로세스가 실행되면 PCB가 생성되어 프로세스 테이블에 추가됨
    - 운영체제는 PCB를 통해 프로세스를 관리

  • 프로세스 종료 시 (PCB 제거)
    - 프로세스가 종료되면 운영체제는 PCB를 제거
    - 사용하던 자원을 해제하고 프로세스 테이블에서 삭제

프로세스가 비정상 종료되어 사용한 지원이 회수되었음에도 프로세스 테이블에 종료된 프로세스의 PCB가 남아 있는 경우가 있음. 이러한 비정상 종료 상태를 좀비프로세스 라고함


프로세스 스케줄링과 타이머 인터럽트

운영체제는 여러 프로세스를 번갈아 실행해야 함
이때 CPU를 얼마나 오래 사용할지 제어하는 기법CPU 스케줄링

  • 타이머 인터럽트 (Timer Interrupt)
    - 운영체제가 프로세스에게 CPU를 할당할 시간을 제한하는 장치
    - 일정 시간이 지나면 타이머 인터럽트 발생 → CPU를 다른 프로세스로 전환

예시:
1️⃣ 프로세스 A가 실행됨
2️⃣ 일정 시간이 지나 타이머 인터럽트 발생
3️⃣ 운영체제가 CPU를 프로세스 B에게 넘김 (문맥 교환 발생)


문맥 교환 (Context Switching)

  • CPU를 다른 프로세스로 전환하는 과정
  • 이전 프로세스의 실행 상태(문맥)를 저장하고, 새로운 프로세스의 문맥을 복원
  • 운영체제는 PCB에 문맥을 저장하고 복구하면서 프로세스를 교체

📌 문맥 교환 과정

1️⃣ 현재 실행 중인 프로세스의 문맥을 PCB에 저장
2️⃣ 새로운 프로세스의 PCB에서 문맥을 복구
3️⃣ CPU가 새로운 프로세스를 실행

문맥(Context)
프로그램 카운터, CPU 레지스터 값, 프로세스 상태 등 프로세스 실행을 위한 정보

📌 문맥 교환의 단점

  • 문맥 교환이 자주 발생하면 성능 저하 (오버헤드 증가)
  • 캐시 미스(Cache Miss) 발생 가능
    → 메모리에서 데이터를 다시 불러오는 과정이 많아짐
  • 프로세스 실행 시간이 줄어들어 비효율적 운영 가능

프로세스의 상태

운영체제는 PCB 를 통해 프로세스의 상태를 관리
하나의 프로세스는 실행 과정에서 여러 가지 상태를 거치며 운영체제에 의해 제어 됨

1️⃣ 생성 상태 (New)

  • 프로세스가 생성되고 있는 상태
  • 운영체제가 메모리에 프로세스를 적재하고, PCB를 할당하는 단계
  • 생성 상태를 거친 후, 준비 상태(Ready)로 전환

2️⃣ 준비 상태 (Ready)

  • CPU를 할당받을 준비가 된 상태
  • 현재 실행이 가능한 상태지만 CPU를 할당받지 못해 대기 중
  • CPU가 사용 가능해지면 실행 상태(Running)로 전환

    디스패치(Dispatch): 준비 상태의 프로세스가 **CPU를 할당받아 실행 상태로 전환되는 과정

3️⃣ 실행 상태 (Running)

  • CPU를 할당받아 실행 중인 상태
  • 하지만 CPU 사용 시간(Time Slice)이 제한됨
  • 두 가지 이유로 실행 상태가 종료됨:
    - 1️⃣ 타이머 인터럽트 발생 → 준비 상태(Ready)로 전환
    - 2️⃣ 입출력 요청 등으로 실행 불가능한 상태 → 대기 상태(Blocked)로 전환

4️⃣ 대기 상태 (Blocked)

  • 입출력 작업 요청, 자원 확보 대기 등으로 실행이 불가능한 상태
  • 예: 디스크에서 데이터를 읽는 동안 CPU가 기다려야 할 때
  • 입출력 작업이 완료되면 다시 준비 상태(Ready)로 전환

5️⃣ 종료 상태 (Terminated)

  • 프로세스가 실행을 마치고 종료된 상태
  • 운영체제가 PCB를 제거하고, 사용했던 자원을 정리

0️⃣ 프로세스 상태 전이 요약

  • 새로운 프로세스 생성 → 준비 상태로 이동
  • CPU를 할당받으면 실행 상태로 이동 (디스패치)
  • 타이머 인터럽트 발생 시 다시 준비 상태로 이동
  • 입출력 요청 시 대기 상태로 이동
  • 입출력 작업이 끝나면 다시 준비 상태로 이동
  • 실행이 완료되면 종료 상태로 이동

멀티프로세스와 멀티스레드

프로그램을 동시에 실행하는 방법에는 멀티프로세스(Multi-Process)와 멀티스레드(Multi-Thread) 방식이 있음
각 방식은 자원 공유 여부와 실행 방식에서 차이를 보임


멀티프로세스 (Multi-Process)

  • 하나의 프로그램을 여러 개의 독립적인 프로세스로 실행하는 방식
  • 각 프로세스는 독립적인 메모리 공간과 자원을 사용함
  • 크롬 브라우저의 여러 탭이 각각의 프로세스로 실행되는 것이 대표적인 예

📌 멀티프로세스의 특징

독립적 실행: 각 프로세스는 독립적으로 실행되며, 다른 프로세스에 영향을 거의 주지 않음
안정성 보장: 하나의 프로세스에서 오류가 발생해도 다른 프로세스에는 영향이 없음
비효율적인 메모리 사용: 각 프로세스가 별도의 메모리를 사용하므로 메모리 소비가 큼
높은 컨텍스트 스위칭 비용: 프로세스 간 전환(Context Switching) 비용이 큼

예시: 웹 브라우저의 여러 탭이 각각 독립적인 프로세스로 실행됨


멀티스레드 (Multi-Thread)

  • 하나의 프로세스 내부에서 여러 개의 스레드를 실행하는 방식
  • 스레드는 동일한 주소 공간(Code, Data, Heap)을 공유하지만, 개별적인 Stack을 가짐
  • 멀티스레드는 프로세스의 자원을 공유하여 빠르고 효율적임

📌 멀티스레드의 특징

자원 공유: 코드, 데이터, 힙 영역을 공유하여 메모리 사용이 효율적
빠른 실행: 프로세스 간 통신(IPC) 없이 자원 공유 가능하여 속도가 빠름
동기화 문제 발생 가능: 여러 스레드가 같은 자원을 접근하면서 데이터 충돌 발생 가능
안정성이 낮음: 하나의 스레드가 오류를 일으키면 전체 프로세스가 영향을 받을 수 있음

예시: 웹 브라우저에서 여러 개의 탭이 하나의 프로세스에서 동작하는 경우


멀티스레드 예제 (Java)

자바에서 멀티스레드를 활용하여 foo(), bar(), baz() 함수를 실행하는 코드 작성
각 함수는 현재 실행 중인 프로세스 ID(PID)와 스레드 ID(Thread ID) 를 출력

class MyThread extends Thread {
    private String functionName;

    public MyThread(String functionName) {
        this.functionName = functionName;
    }

    @Override
    public void run() {
        System.out.println("Function: " + functionName + 
                           " | Process ID: " + ProcessHandle.current().pid() + 
                           " | Thread ID: " + Thread.currentThread().getId());
    }
}

public class MultiThreadExample {
    public static void main(String[] args) {
        Thread thread1 = new MyThread("foo");
        Thread thread2 = new MyThread("bar");
        Thread thread3 = new MyThread("baz");

        thread1.start();
        thread2.start();
        thread3.start();
    }
}

// 결과
// Function: foo | Process ID: 12345 | Thread ID: 12
// Function: bar | Process ID: 12345 | Thread ID: 13
// Function: baz | Process ID: 12345 | Thread ID: 14
// 각 스레드는 동일한 프로세스 ID(PID)를 가지지만, 개별적인 스레드 ID를 가짐

프로세스 간 통신

기본적으로 프로세스는 자원을 공유하지 않지만,
운영체제는 프로세스 간 데이터를 주고받을 수 있는 방법을 제공
이를 프로세스 간 통신(IPC: Inter-Process Communication) 이라고 함

IPC 방식은 크게 공유 메모리메시지 전달 방식으로 나뉨


공유 메모리

여러 프로세스가 공통으로 사용할 수 있는 메모리 영역을 제공하는 방식
이를 통해 프로세스들이 데이터를 커널을 거치지 않고 직접 주고받을 수 있음

📌 공유 메모리의 특징

빠른 속도: 프로세스가 직접 메모리에 접근하기 때문에 통신 속도가 매우 빠름
효율적 데이터 공유: 프로세스가 같은 메모리 영역을 사용하여 대량의 데이터를 쉽게 공유 가능
동기화 문제 발생 가능: 여러 프로세스가 동시에 같은 메모리를 변경하면 데이터 충돌 발생 가능 (레이스 컨디션)
추가적인 동기화 기법 필요: 세마포어(Semaphore)나 뮤텍스(Mutex)와 같은 동기화 도구 필요


메시지 전달

프로세스가 데이터를 커널을 통해 주고받는 방식
공유 메모리와 다르게, 프로세스가 직접 메모리를 공유하지 않으며 커널이 메시지 송수신을 관리

📌 메시지 전달의 특징

안전한 데이터 교환: 프로세스 간 직접적인 자원 공유가 없어 충돌 위험이 낮음
운영체제가 동기화 관리: 커널이 메시지를 중재하므로 레이스 컨디션 등의 문제 최소화
속도가 느림: 커널을 거쳐야 하므로 공유 메모리보다 속도가 상대적으로 느림
메시지 크기 제한: 운영체제에서 제공하는 메시지 큐 크기가 제한될 수 있음


시그널

특정 프로세스에게 이벤트가 발생했음을 알리는 비동기적인 방식의 IPC
프로세스가 시그널을 받으면 이벤트를 처리하는 핸들러(Signal Handler)를 실행한 후 다시 원래 작업을 수행

📌 시그널의 특징

비동기 방식: 특정 이벤트가 발생하면 즉시 프로세스에 알림을 보낼 수 있음
간단한 IPC 구현 가능: 가벼운 시스템 호출을 통해 IPC 수행
데이터 전달이 어려움: 시그널은 단순한 이벤트 알림으로, 복잡한 데이터를 직접 전달하기 어려움
프로세스가 시그널을 처리하지 않으면 기본 동작 수행 (예: SIGKILL은 강제 종료)

시그널의 기본 동작과 코어 덤프
시그널마다 수행할 기본 동작이 정해져 있음. 대부분은 프로세스를 종료하거나 무시하거나 코어 덤프를 생성함.
코어덤프(coredump): 주로 비정상적 으로 종료하는 경우에 생성되는 파일


참고: 북스터디 - 이것이 취업을 위한 컴퓨터 과학이다 (Chapter 3-2)

profile
dock 사이즈 다르잖아

0개의 댓글