이전에 면접 준비하며 준비했던 내용 공유합니다
운영체제 기본 개념
1. 하드웨어 (Hardware)
- 중앙 처리 장치(CPU): 명령어 해석 및 실행.
- 메모리(Memory): 프로그램과 데이터를 저장.
- 입출력 장치(I/O devices): 사용자와 시스템 간 데이터 교환.
1. 부트스트랩 (Bootstrap)
- 저장 위치: ROM(Read-Only Memory)에 저장.
- 역할:
- 시스템 전원이 켜지면 실행됨.
- 운영체제의 커널(kernel) 을 찾아 주 메모리(RAM) 에 적재.
- 결과: 커널이 실행되면서 시스템 초기화가 진행됨.
2. 커널과 시스템 프로세스
- 커널(Kernel): 운영체제의 핵심 부분, 자원 관리와 제어 담당.
- 시스템 프로세스 / 시스템 데몬(Daemon):
- 커널 수행 중 계속 동작.
- 백그라운드에서 시스템 기능(예: 스케줄링, 로그 관리, 네트워크 서비스 등)을 지원.
3. 주 메모리 (RAM)
- 특징: 재기록 가능한 메모리.
- 용도:
- 실행 중인 프로그램과 데이터를 저장.
- CPU가 직접 접근 가능.
- 전원이 꺼지면 내용이 사라지는 휘발성 메모리.
4. 컴퓨터 구조
(1) 폰 노이만 구조 (Von Neumann Architecture)
- 명령어와 데이터를 같은 버스(경로)를 통해 전달.
- 특징: 구조 단순, 범용 컴퓨터에 널리 사용.
- 단점: 명령어와 데이터가 한 버스를 공유해 병목 현상(폰 노이만 병목) 발생 가능.
(2) 하버드 아키텍처 (Harvard Architecture)
- 명령어와 데이터를 서로 다른 버스에서 전달.
- 특징: 명령어 읽기와 데이터 접근을 동시에 수행 가능 → 병목 최소화.
- 주로 임베디드 시스템, DSP(Digital Signal Processor) 등에서 사용.
5. 입출력 구조 (I/O Structure)
1) 인터럽트 기반 I/O (Interrupt-Driven I/O)
- 동작 방식:
- CPU가 I/O 장치에 작업을 지시하고 다른 일을 수행.
- I/O 장치가 작업을 끝내면 인터럽트(Interrupt) 신호를 CPU에 보냄.
- CPU는 인터럽트 서비스 루틴(ISR)을 실행하여 결과를 처리.
- 장점: CPU가 불필요하게 대기하지 않음.
- 단점: 인터럽트가 자주 발생하면 오버헤드 증가.
2) 직접 메모리 접근 (DMA, Direct Memory Access)
- 동작 방식:
- CPU가 I/O 장치에 메모리 전송을 직접 하지 않고, DMA 컨트롤러에게 위임.
- DMA 컨트롤러가 메모리와 I/O 장치 간 데이터를 직접 전송.
- 전송이 완료되면 CPU에 인터럽트를 보내 알림.
- 장점: CPU 개입 최소화 → 고속 데이터 전송 가능 (예: 디스크 ↔ 메모리).
- 활용: 대량 데이터 전송이 필요한 경우 (예: 영상, 네트워크, 디스크 입출력).
2. 운영체제 (Operating System, OS)
- 목적: 사용자가 시스템을 쉽게 사용할 수 있도록 설계.
- 특징:
- 자원 사용 자체에는 신경 쓰지 않음.
- 자원 할당자(Resource Allocator): CPU, 메모리, I/O 자원들을 적절히 배분.
- 제어 프로그램(Control Program): 프로그램 실행과 하드웨어 동작을 제어.
- 발생 원인: CPU 외부의 하드웨어 장치가 CPU에게 서비스를 요청할 때 발생
- 예: 키보드 입력, 디스크 I/O 완료, 네트워크 패킷 도착
- 특징
- 비동기적으로 발생 (CPU가 어떤 명령을 실행 중이든 외부 장치에서 신호를 보냄)
- CPU는 현재 실행 중이던 명령을 끝내고 인터럽트 서비스 루틴(ISR)으로 점프
- 주로 외부 이벤트 처리를 위해 사용
- 예시:
- 키보드에서 키가 눌림 → 인터럽트 발생 → 운영체제 커널이 입력 버퍼에 저장
2. 트랩(Trap, 예외 Exception)- 발생 원인: CPU 내부에서 실행 중인 프로그램이 원인이 되는 상황
- 예: 0으로 나누기, 잘못된 메모리 접근, 시스템 콜 호출
- 특징
- 동기적으로 발생 (특정 명령 실행 결과로 즉시 발생)
- 크게 두 종류로 나뉨:
- 예외(Exception): 오류 상황 (Divide by zero, Page fault 등)
- 트랩(Trap, 소프트웨어 인터럽트): 의도적으로 발생시켜 운영체제 서비스 호출 (System call)
- 예시:
- int 0x80 (리눅스 시스템 콜) → 커널로 진입
- 잘못된 포인터 접근 → Segmentation fault
3. 차이점 요약구분 인터럽트(Interrupt) 트랩 / 예외(Trap, Exception)발생 원인 하드웨어 장치 (외부) CPU 내부 명령 실행 결과 (소프트웨어) 발생 시점 비동기적 (언제든 발생 가능) 동기적 (명령 실행 직후 발생) 주 용도 외부 이벤트 처리 오류 처리, 운영체제 서비스 호출 예시 키보드 입력, 디스크 I/O 완료 0 나누기, 페이지 폴트, 시스템 콜
👉 정리하면,- 인터럽트는 외부 장치가 CPU를 부르는 "하드웨어 호출"
- 트랩/예외는 프로그램 실행 도중 CPU 내부에서 발생하는 "소프트웨어 호출/오류"
- CPU 내부에 모드 비트(Mode Bit) 라는 플래그가 있음.
- 1 → 사용자 모드(User Mode)
- 0 → 커널 모드(Kernel Mode, Supervisor Mode)
2. 사용자 모드 (User Mode)- 일반 응용 프로그램이 동작하는 모드
- 제한된 명령어만 실행 가능
- 입출력(I/O) 명령, 메모리 보호를 무시하는 명령 등은 금지
- 잘못된 동작이 발생하면 **트랩(예외)**을 발생시켜 운영체제가 처리
3. 커널 모드 (Kernel Mode)- 운영체제(OS) 커널이 실행되는 모드
- 모든 명령어 실행 가능 (특권 명령 포함)
- 입출력 제어, 타이머 관리, 메모리 관리, 인터럽트 제어 등
- 시스템 전체에 영향을 줄 수 있는 연산은 커널 모드에서만 수행
4. 모드 전환- 부팅 시: 항상 커널 모드에서 시작 (운영체제가 제어권을 가짐)
- 시스템 콜, 트랩, 인터럽트 발생 시: 하드웨어가 자동으로 커널 모드로 전환
- 커널이 작업을 마친 후: 다시 사용자 모드로 전환하여 응용 프로그램 실행
5. 이중 모드 운영의 목적- 보호(Protection): 사용자 프로그램이 임의로 하드웨어 자원(I/O, 메모리 등)을 제어하지 못하게 막음
- 안정성(Stability): 오류가 발생해도 운영체제가 제어권을 가짐 → 시스템 전체가 멈추는 것 방지
- 보안(Security): 악의적인 프로그램이 커널 수준 권한을 직접 사용하는 것을 차단
- 메모리 사용량 추적, 누구에 의해 사용되고 있는지
- 어떤 프로세스를 적재하고 제거할 것인지
- 필요에 따라 메모리 공간을 할당하고 회수
- 1. 모드 비트 (Mode Bit)
- 현재의 운영체제는 인터럽트 구동식이다
3. 응용 프로그램 (Application Programs)
- 사용자 요구를 직접 해결하는 소프트웨어.
- 예시: 워드 프로세서, 웹 브라우저 등.
4. 사용자 (Users)
- 응용 프로그램을 활용해 컴퓨터를 사용하는 주체.
👉 요약하면, 하드웨어가 기본 자원, 운영체제가 이를 관리, 응용 프로그램이 사용자 목적을 달성, 그리고 사용자가 최종적으로 시스템을 활용하는 구조입니다.
운영체제 서비스
운영체제 서비스 (Operating System Services)
운영체제는 사용자와 하드웨어 사이에서 여러 **서비스(services)**를 제공하여 편의성, 효율성, 보안성을 보장합니다.
1. 사용자 인터페이스 제공 (User Interface)
- CLI(Command Line Interface): 터미널 기반 명령어 입력 (예: Bash, PowerShell)
- GUI(Graphical User Interface): 아이콘/윈도우 기반 그래픽 환경 (예: Windows, macOS)
- 터치 인터페이스: 모바일 OS에서 활용
2. 프로그램 수행 (Program Execution)
- 프로그램을 메모리에 적재하고 실행
- 실행 중인 프로그램의 중단, 재개, 종료 기능 제공
- 예: fork(), exec() 시스템 콜
3. 입출력 연산 (I/O Operations)
- 사용자 프로그램은 직접 장치 제어 불가 → OS가 대신 수행
- 키보드 입력, 디스크 읽기/쓰기, 네트워크 송수신 등
- 장치 드라이버를 통해 하드웨어 추상화
4. 파일 시스템 조작 (File-System Manipulation)
- 파일 생성, 삭제, 읽기/쓰기, 접근 권한 제어
- 디렉토리 구조 관리
- 예: open(), read(), write(), close() 시스템 콜
5. 통신 (Communication)
- 프로세스 간 통신(IPC, Inter-Process Communication) 기능 제공
- 공유 메모리(shared memory): 동일 메모리 영역에 접근
- 메시지 전달(message passing): 커널을 통해 메시지 송수신
- 예: 파이프(pipe), 소켓(socket), 큐(queue)
6. 오류 탐지 (Error Detection)
- CPU, 메모리, I/O 장치 동작 중 발생하는 오류 감지 및 처리
- 잘못된 명령 실행, 메모리 접근 위반(segmentation fault), 디스크 읽기 오류 등
- 오류 발생 시 트랩을 발생시켜 운영체제가 개입
7. 자원 할당 (Resource Allocation)
- CPU 시간, 메모리, I/O 장치, 파일 등 한정된 자원을 효율적으로 분배
- 스케줄링(scheduling) 기법 활용 (FCFS, RR, Priority 등)
📌 정리
운영체제 서비스는 크게 **사용자 편의를 위한 서비스(UI, 실행, I/O, 파일, 통신)**와 **시스템 보호 및 효율성을 위한 서비스(오류 탐지, 자원 할당)**로 나눌 수 있습니다.
System Call (시스템 콜)
- 정의: 사용자 프로그램이 운영체제의 커널이 제공하는 기능(입출력, 프로세스 제어, 메모리 관리 등)을 요청하는 인터페이스
- 역할: 사용자 모드 → 커널 모드로 안전하게 전환하여 특권 명령 실행 가능하게 해줌
- 예시
- read(), write(), open(), close() (파일 조작)
- fork(), exec(), wait() (프로세스 관리)
- socket(), connect(), send(), recv() (통신)
2. API (응용 프로그래밍 인터페이스)
- 정의: 응용 프로그램이 운영체제, 라이브러리, 프레임워크 기능을 쉽게 사용하도록 만든 함수/메서드 집합
- 역할: 프로그래머가 직접 system call을 다루지 않고, 고수준 함수 호출로 편리하게 접근할 수 있게 함
- 예시
- C의 stdio.h 라이브러리 → 내부적으로 read()/write() 시스템 콜 호출
- Java의 FileInputStream → OS의 파일 입출력 system call 사용
3. 관계
- System Call: 운영체제 커널 기능에 직접 접근하는 인터페이스
- API: 응용 프로그램이 쉽게 사용할 수 있도록 제공되는 함수 인터페이스 (보통 system call을 내부적으로 호출)
👉 즉,
- API는 프로그래머 친화적인 인터페이스
- System Call은 운영체제 친화적인 인터페이스
통신(IPC)
메세지 전달, 공유 메모리
1. 메시지 전달 (Message Passing)
- 개념: 프로세스들이 커널(운영체제)을 매개로 하여 메시지를 주고받음
- 방식
- send(destination, message)
- receive(source, message)
- 특징
- 프로세스 간에 메모리를 공유하지 않음
- 분산 시스템에서도 사용 가능 (네트워크 기반 통신)
- 장점: 단순하고, 프로세스 간 강한 독립성 보장
- 단점: 커널 개입이 필요해 속도가 느릴 수 있음
- 예시: 파이프(pipe), 소켓(socket), 메시지 큐(message queue)
2. 공유 메모리 (Shared Memory)
- 개념: 운영체제가 특정 메모리 영역을 여러 프로세스가 공동으로 접근할 수 있도록 허용
- 특징
- 한 번 설정되면 커널 개입 없이 메모리 접근 가능
- 가장 빠른 IPC 방식
- 주의점: 동시에 접근할 때 데이터가 꼬이지 않도록 동기화 기법 필요 (세마포어, 뮤텍스)
- 예시: POSIX shared memory (shm_open, mmap)
3. 비교 정리
구분 메시지 전달 (Message Passing) 공유 메모리 (Shared Memory)
| 데이터 교환 방식 | 커널을 통해 메시지 송수신 | 특정 메모리 영역을 공동 사용 |
| 속도 | 상대적으로 느림 (커널 개입 필요) | 빠름 (직접 메모리 접근) |
| 독립성 | 프로세스 간 강한 독립성 유지 | 프로세스 간 결합도 높음 |
| 동기화 필요성 | 커널이 동기화 보장 | 개발자가 직접 동기화 관리 필요 |
| 활용 예시 | 파이프, 소켓, 메시지 큐 | shm, mmap, DB 공유 메모리 캐시 |
✅ 정리
- 메시지 전달: 운영체제 커널을 통해 안전하게 통신, 하지만 속도는 느림.
- 공유 메모리: 속도가 빠르지만 동기화 문제 해결을 개발자가 직접 해야 함.
프로세스
메모리에서 적재되어 실행중인 프로그램
작업을 할당받는 단위
- 프로세스(Process): 실행 중인 프로그램. 자원(메모리 공간, 파일, 핸들 등)을 할당받는 단위
- → 커널이 PCB(Process Control Block)로 관리: PID, 상태, 스케줄링 정보, 메모리 정보(페이지 테이블), 열려 있는 파일 등.
- 스레드(Thread): 프로세스 안의 실행 흐름. CPU 스케줄링의 단위
- → 각 스레드는 **자신만의 스택, 레지스터 집합, 프로그램 카운터(PC)**를 갖고, 코드·데이터·힙은 프로세스 내 다른 스레드와 공유.
프로세스의 전형적 메모리 구역(가상 메모리 레이아웃)
아래 순서는 보통의 개념도(낮은 주소 → 높은 주소)입니다.
- 텍스트(코드) 섹션 (text / code)
- 기계어 명령이 저장되는 구역 (보통 읽기 전용, 공유 가능)
- 여러 프로세스가 같은 실행 파일을 돌릴 때 코드 페이지를 공유해 메모리 절약 가능
- 데이터 섹션 (data)
- 초기값이 있는 전역/정적 변수가 위치 (예: int g = 10;)
- 프로세스마다 개별 복사본을 가짐(스레드 간 공유)
- BSS 섹션 (bss)
- 초기값이 없는 전역/정적 변수 (예: int g2;)
- 로더가 0으로 초기화하여 시작
- 힙(Heap)
- 동적 할당 영역 (malloc/free, new/delete)
- 런타임에 필요해질수록 “위로”(주소 증가 방향) 확장되는 것이 관례
- 여러 스레드가 공유 → 동시성 안전을 위해 할당기 내부에 락/아레나 등 활용
- 스택(Stack)
- 함수 호출 프레임이 쌓이는 영역: 복귀 주소, 매개변수, 지역변수, 저장된 레지스터 등
- 일반적으로 “아래로”(주소 감소 방향) 성장
- 스레드마다 독립 스택을 가짐 (스택 크기는 생성 시 정해지는 경우가 많음)
- (플랫폼에 따라) mmap/공유 라이브러리 영역
- mmap, 공유 객체(.so/.dll) 등이 매핑되는 가변 영역
개념도 (단순화)
낮은 주소
[ 텍스트 ] → [ 데이터 ] → [ BSS ] → [ 힙 ↑ ] … [ mmap 등 ] … [ ↓ 스택 ]
높은 주소
프로그램 카운터(PC, Instruction Pointer)
- 다음에 실행할 명령의 주소를 가리키는 레지스터.
- 스레드마다 하나씩 존재(컨텍스트 스위치 시 PC/레지스터 저장·복원).
- 함수 호출 시: 호출 명령이 복귀 주소를 스택에 저장, PC는 호출 대상 주소로 변경 → ret 시 스택의 복귀 주소로 PC 복원.
스택 자세히
- 포함 내용:
- 복귀 주소(return address), 매개변수(arguments), 지역변수, 저장된 레지스터(예: 프레임 포인터/링크 레지스터)
- 단위: 스택 프레임(함수 호출 1회에 해당)
- 특징/주의:
- Stack Overflow: 너무 깊은 재귀, 큰 지역배열 등으로 보호 페이지 침범
- 스택은 매우 빠름(연속 메모리 + 단순 푸시/팝) / 수명은 블록/스코프에 종속
힙 자세히
- 동적 수명: 필요할 때 할당(allocate), 더 이상 필요 없으면 해제(free)
- 특징/주의:
- 메모리 누수(Leak): 해제를 잊으면 장기 실행 시 문제
- Double Free / Use-After-Free: 이미 해제한 블록 재사용 → 치명적 버그
- Fragmentation: 조각화로 인한 큰 블록 할당 실패 가능
- 멀티스레드 환경: 동시성 이슈(락 경합, 캐시 라인 경합 등) → 현대 할당기는 아레나/슬랩 등으로 완화
데이터 섹션 & BSS 요약
- 데이터(data): 초기값 있는 전역/정적(실행 파일 안에 초기값 저장)
- BSS: 초기값 없는 전역/정적(로더가 0으로 초기화)
- 공통: 프로세스 단위로 소유(스레드들이 공유)
텍스트 섹션 요약
- 실행 코드 저장
- 보통 읽기 전용 + 실행 가능, 프로세스 간 공유 가능한 페이지로 매핑 돼 메모리 절약
1. 프로세스 상태 (Process States)
운영체제는 프로세스를 관리하기 위해 여러 상태로 구분합니다. 대표적인 상태는 다음과 같습니다:
- New (생성)
- 프로세스가 생성되고 있는 상태
- PCB가 만들어지고, OS가 메모리/자원 할당 준비
- 아직 실행은 시작되지 않음
- Ready (준비완료)
- 실행할 준비가 끝났지만 CPU를 배정받지 못한 상태
- 메모리와 필요한 자원은 확보 완료
- 스케줄러가 CPU를 배정하면 곧 실행 상태로 전환됨
- Running (실행)
- CPU를 점유하고 명령어를 수행 중인 상태
- 단일 코어에서는 한 시점에 하나의 프로세스만 실행 가능
- 실행 중 인터럽트나 시간 할당량 만료 시 다시 준비 상태로 전환
- Waiting (대기, Blocked)
- I/O 요청, 이벤트 발생 대기 등으로 CPU가 아닌 외부 조건을 기다리는 상태
- 예: 디스크 입출력 완료, 사용자 입력 등
- 조건이 만족되면 준비 상태로 전환됨
- Terminated (종료)
- 프로세스 실행이 끝난 상태
- PCB와 자원이 정리(해제)됨
2. PCB (Process Control Block, 프로세스 제어 블록)
운영체제가 프로세스를 관리하기 위해 유지하는 핵심 데이터 구조.
각 프로세스마다 PCB가 1개씩 존재하며, 프로세스의 “신분증/이력카드” 같은 역할을 함.
PCB에 포함되는 정보
- 프로세스 상태
- New, Ready, Running, Waiting, Terminated 중 현재 상태
- 프로그램 카운터 (Program Counter, PC)
- 다음 실행할 명령어의 주소 저장
- CPU 레지스터 정보
- 범용 레지스터, 스택 포인터, PSW(Program Status Word) 등
- 컨텍스트 스위칭 시 저장/복원되어 실행 재개 가능
- 스케줄링 정보
- 우선순위, 스케줄링 큐 포인터, CPU 사용 시간 등
- 메모리 관리 정보
- 페이지 테이블, 세그먼트 테이블, 메모리 경계 레지스터 등
- 해당 프로세스가 접근 가능한 메모리 영역 기록
- 계정 및 자원 정보
- 파일 핸들, 입출력 장치 상태, 소유자 ID, 사용 권한 등
3. 정리 그림 (흐름도)
프로세스 상태 전이 예시
[New]
↓
[Ready] ← (I/O 완료) ← [Waiting]
↓ ↑
(스케줄링) (시간 종료/인터럽트)
↓ ↑
[Running]
↓
[Terminated]
👉 요약:
- 프로세스 상태는 생성 → 준비 → 실행 → 대기/종료로 전이되며, CPU·I/O 스케줄링에 따라 바뀜.
- PCB는 프로세스의 모든 관리 정보를 담는 자료구조로, 컨텍스트 스위칭과 스케줄링에 필수적임.
쓰레드
작업을 실행하는 단위
프로세스 안의 실행 흐름.
CPU 스케줄링의 단위
→ 각 스레드는 **자신만의 스택, 레지스터 집합, 프로그램 카운터(PC)**를 갖고,
코드·데이터·힙은 프로세스 내 다른 스레드와 공유
TCB(Thread Control Block)에 포함되는 정보
스레드별로 필요한 최소 정보만 따로 가짐:
- 스레드 ID (TID)
- 프로그램 카운터(PC): 다음 실행할 명령어
- 레지스터 집합: 현재 CPU 레지스터 상태
- 스택 포인터: 각 스레드는 자기만의 스택을 가짐
- 스케줄링 정보: 우선순위, 상태(Running, Ready, Waiting 등)
- (운영체제에 따라) 신호 처리 정보, TLS(Thread-Local Storage) 등
프로세스 스케줄링 (Process Scheduling)
운영체제는 동시에 여러 프로세스가 실행되는 환경에서, CPU를 누구에게 줄 것인지를 결정해야 합니다.
이를 담당하는 것이 **프로세스 스케줄러(Process Scheduler)**입니다.
- 목표:
- CPU 및 시스템 자원의 효율적 활용
- 사용자에게 빠른 응답 제공 (대화형 시스템)
- 공평성과 우선순위 보장
- 잡 큐(Job Queue):
- 시스템에 들어온 모든 프로세스가 대기하는 큐
- 여기서 스케줄러가 선택해 실행 대기열(Ready Queue)로 보냄
2. 스케줄러의 종류
(1) 장기 스케줄러 (Long-Term Scheduler, Job Scheduler)
- 역할: 어떤 프로세스를 메모리에 올려 실행할지 결정
- 특징:
- 실행할 **잡 큐(Job Pool)**에서 프로세스를 선택 → 메모리 적재
- 실행 프로세스의 전체 수를 제어하여 CPU와 I/O의 균형 유지
- 효과:
- 입출력 중심 프로세스와 CPU 중심 프로세스를 적절히 혼합하여 자원 활용 극대화
- 예시: 배치 시스템(batch system)에서 주로 사용됨
- 현대의 시분할 시스템에서는 장기 스케줄러가 거의 사용되지 않음 (대부분 시스템이 자동으로 처리)
(2) 단기 스케줄러 (Short-Term Scheduler, CPU Scheduler)
- 역할: **실행 준비(Ready Queue)**에 있는 프로세스 중, 누구에게 CPU를 줄지 결정
- 실행 시점: 매우 짧은 시간 단위로 동작 (ms 단위)
- 효과: 시스템 반응성과 CPU 활용률에 직접적 영향
- 프로세스 특성 고려:
- 입출력 중심 프로세스(I/O-bound process)
- CPU는 짧게 쓰고, I/O 요청이 잦음
- 빨리 실행해서 I/O 장치가 놀지 않도록 해주는 것이 중요
- CPU 중심 프로세스(CPU-bound process)
- 계산 위주, CPU를 오래 사용
- I/O 요청이 적음
- ⇒ 두 종류를 적절히 섞어야 전체 자원 활용이 좋아짐
- 입출력 중심 프로세스(I/O-bound process)
(3) 중기 스케줄러 (Medium-Term Scheduler) – 선택적
- 역할: 프로세스 실행을 일시 중단(suspend) → 메모리에서 내보냈다가 → 나중에 복귀
- 이 과정을 **스와핑(Swapping)**이라 함
- 효과:
- 메모리 부족 시 부하를 줄이고, 다중 프로그래밍 정도를 제어
- 우선순위 있는 프로세스가 자원을 빨리 확보할 수 있게 함
3. 스와핑 (Swapping)
- 정의: 프로세스를 **보조기억장치(디스크)**로 내보냈다가, 필요할 때 다시 메모리로 불러오는 과정
- 특징:
- 프로세스는 원래 실행되던 시점부터 재개됨 (PCB에 상태 저장)
- 메모리 공간 확보 및 CPU 이용률 최적화
- 장점: 메모리 관리 유연성 증가, 다중 프로그래밍 향상
- 단점: 디스크 I/O 오버헤드 발생 → 잦으면 성능 저하
4. 정리 표
스케줄러 종류 동작 위치 역할 실행 빈도 주요 목적
| 장기 스케줄러 | 잡 큐 → 메모리 | 어떤 프로세스를 실행시킬지 선택 | 느림 (분/초 단위) | CPU/IO 밸런스 유지, 다중 프로그래밍 정도 제어 |
| 단기 스케줄러 | 준비 큐 → CPU | 다음 실행할 프로세스 선택 | 매우 빠름 (ms 단위) | 응답속도 개선, CPU 활용 극대화 |
| 중기 스케줄러 | 메모리 ↔ 디스크 | 프로세스 일시 중단/재개 (스와핑) | 상황에 따라 | 메모리 회수, 우선순위 관리 |
👉 요약:
- 장기 스케줄러: 잡 큐에서 메모리로 올릴 프로세스 선택 (CPU vs I/O 균형).
- 단기 스케줄러: Ready Queue에서 CPU를 줄 프로세스 선택 (밀리초 단위, 즉각적).
- 중기 스케줄러: 메모리 ↔ 디스크로 스와핑, 부하 조절.
프로세스 생성 (Process Creation)
운영체제에서 새로운 프로세스가 생성될 때, 일반적으로 다음 단계가 일어납니다.
- 부모 프로세스(Parent Process)가 자식 프로세스(Child Process)를 생성
- Unix 계열: fork() 시스템 콜 사용
- Windows 계열: CreateProcess() API 사용
- 부모와 자식의 실행 관계
- 병행 실행(Concurrent Execution): 부모는 계속 실행, 자식은 독립적으로 실행
- 부모가 대기(Wait): 부모는 wait()를 호출하여 자식이 끝날 때까지 기다림
- 프로세스 주소 공간
- 자식은 **부모의 복사본(Copy)**을 가짐 (코드, 데이터, 스택 등)
- 하지만 별도의 PID(Process ID) 부여 → 독립된 실행 단위
- 자식이 원하면 exec() 계열 호출로 새로운 프로그램을 덮어씀
2. 프로세스 종료와 관련 개념
(1) 정상 종료
- 자식 프로세스가 exit() 호출 시 종료
- 운영체제는 **종료 상태(exit status)**를 부모에게 전달
- 부모는 wait()를 호출해 종료 상태를 회수 → 자식 PCB가 해제됨
(2) 좀비 프로세스 (Zombie Process)
- 정의: 자식이 종료되었지만, 부모가 wait()를 호출하지 않아 PCB가 해제되지 않은 상태
- 특징:
- 실행은 끝났지만 PCB는 그대로 남음 → 프로세스 테이블에 기록 유지
- 상태는 “Z” (Zombie)로 표시
- 다수의 좀비가 생기면 프로세스 테이블 고갈 문제 발생
(3) 고아 프로세스 (Orphan Process)
- 정의: 부모가 먼저 종료되어 부모가 없는 자식 프로세스
- 처리 방법:
- Unix 계열에서는 **init 프로세스(PID 1)**가 고아 프로세스를 자동으로 인수 → 정상적으로 실행/종료 가능
- 따라서 고아 프로세스는 시스템에 큰 문제는 일으키지 않음
3. 그림으로 정리
부모 fork()
├─ 부모 계속 실행
└─ 자식 프로세스 생성
├─ 부모와 병행 실행
├─ 부모가 wait() 호출 → 정상 회수
├─ 부모가 회수 안 함 → [좀비 프로세스]
└─ 부모 먼저 종료 → [고아 프로세스]
✅ 요약:
- 자식은 부모의 복사본으로 생성되지만, 독립된 PID를 가짐.
- 부모와 자식은 병행 실행 가능, 또는 부모가 wait()로 자식 종료까지 대기 가능.
- 좀비 프로세스: 자식 종료 후 부모가 회수하지 않음 → PCB만 남음.
- 고아 프로세스: 부모가 먼저 종료 → init 프로세스가 대신 관리.
프로세스 간 통신(IPC, Inter-Process Communication)
- 필요성:
- 다중 프로세스 환경에서 데이터 교환, 동기화, 협력 수행을 위해 사용
- OS는 안전성과 효율성을 위해 여러 가지 IPC 메커니즘을 제공
2. IPC 모델
(1) 공유 메모리 시스템 (Shared Memory)
- 개념: 두 프로세스가 같은 메모리 영역을 공유하여 데이터를 주고받음
- 특징:
- 가장 빠른 통신 방법 (커널 개입 최소화, 단순 메모리 접근)
- 하지만 동기화 문제(레이스 컨디션) 발생 가능 → 세마포어, 뮤텍스 필요
- 예시: 생산자-소비자 문제, 대규모 데이터 교환
(2) 메시지 전달 시스템 (Message Passing)
- 개념: 운영체제가 제공하는 커널 버퍼를 통해 프로세스들이 메시지를 송수신
- 특징:
- 커널을 거쳐야 해서 상대적으로 느림
- 동기화 문제는 OS가 관리 → 구현 간단, 안전성 높음
- 방식:
- 직접 통신: 프로세스들이 서로의 ID를 알고 직접 메시지 교환
- 간접 통신: 메일박스(mailbox), 포트(port) 같은 추상화된 채널 이용
3. 주요 IPC 기법
(1) 파이프 (Pipe)
- 개념: 두 프로세스 간 단방향 통신 채널
- 특징:
- 한쪽은 쓰기(write), 다른 쪽은 읽기(read) 전용
- 부모-자식 프로세스 간 자주 사용
- 익명 파이프: 같은 계열 프로세스 간
- 명명된 파이프(Named Pipe, FIFO): 무관한 프로세스 간도 가능
(2) 메시지 큐 (Message Queue)
- 개념: 커널이 제공하는 큐(Queue) 자료구조를 사용
- 특징:
- 비동기적 메시지 전달 가능
- 메시지 우선순위 지정 가능
(3) 소켓 (Socket)
- 개념: 네트워크 기반 IPC 기법
- 특징:
- 동일 시스템 내 프로세스뿐 아니라, 다른 시스템 간 통신도 가능
- TCP/UDP 기반으로 동작 → 클라이언트-서버 모델에 적합
(4) RPC (Remote Procedure Call)
- 개념: 원격 프로세스의 함수를 마치 로컬 함수처럼 호출하는 기법
- 특징:
- 네트워크/분산 시스템에서 자주 사용
- 호출 측은 네트워크 세부사항을 몰라도 함수 호출처럼 사용 가능
- 내부적으로는 메시지 전달, 직렬화, 소켓 통신 등으로 구현
(5) 기타 IPC
- 세마포어 (Semaphore): 동기화 및 상호 배제를 위한 카운터 기반 메커니즘
- 공유 파일 (Shared File): 파일을 매개로 데이터 교환, 단 느리고 동기화 필요
4. 정리 표
방식 특징 장점 단점 활용 예
| 공유 메모리 | 메모리 영역 직접 공유 | 빠름 | 동기화 필요 | 생산자-소비자 버퍼 |
| 메시지 전달 | 커널이 메시지 송수신 관리 | 구현 단순, 안전 | 느림 | 분산 환경 프로세스 간 통신 |
| 파이프 | 단방향 스트림 | 간단, 부모-자식 간 통신 용이 | 단방향 제한 | Unix 파이프(` |
| 메시지 큐 | 커널의 큐 이용 | 비동기, 우선순위 지원 | 커널 자원 한정 | IPC 채팅, 로깅 |
| 소켓 | 네트워크 기반 | 원격 통신 가능 | 구현 복잡 | 웹 서버-클라이언트 |
| RPC | 원격 함수 호출 | 추상화, 편리 | 내부 구현 복잡 | 분산 시스템, 마이크로서비스 |
✅ 요약:
- IPC 모델: 공유 메모리 vs 메시지 전달
- 주요 기법: 파이프, 메시지 큐, 소켓, RPC 등
- 선택 기준: 데이터 크기, 속도 요구, 안정성, 프로세스 관계(같은 시스템 vs 분산 시스템)
스레드(Thread)의 정의
- CPU 이용의 기본 단위
- 프로세스 내에서 실행되는 실행 흐름 단위를 의미합니다.
- 프로세스는 최소 1개의 스레드를 가지며, 멀티스레딩을 통해 하나의 프로세스에서 여러 스레드가 병렬로 실행될 수 있습니다.
스레드의 구성 요소
스레드마다 독립적으로 가지는 부분:
- Thread ID : 스레드 고유 식별자
- PC(Program Counter) : 명령어의 실행 위치
- 레지스터 집합 : 연산에 필요한 임시 데이터 저장
- 스택(Stack) : 함수 호출, 지역 변수 저장
스레드가 같은 프로세스 내에서 공유하는 부분:
- 코드(Code) 영역
- 데이터(Data) 영역
- 운영체제 자원 (파일 핸들, 소켓 등)
스레드의 종류
- 사용자 스레드 (User Thread)
- 사용자 수준 라이브러리에서 지원하는 스레드
- 커널이 직접 인식하지 못하고, 하나의 프로세스 단위로 스케줄링됨
- 장점: 생성/전환 속도가 빠르고, 오버헤드가 적음
- 단점: 하나의 스레드가 커널 호출로 블록되면, 전체 프로세스가 블록될 수 있음
- 커널 스레드 (Kernel Thread)
- 운영체제 커널이 직접 관리하는 스레드
- 커널이 스케줄링하므로 다중 CPU 활용 가능
- 단점: 생성/전환 시 시스템 콜이 필요해 비용이 크다
👉 정리하면, 스레드 = 프로세스 내 실행 흐름의 최소 단위이고,
**독립적인 실행 상태(PC, 레지스터, 스택)**는 따로 가지지만,
코드/데이터/자원은 같은 프로세스 내 스레드끼리 공유한다는 특징이 있습니다.
fork()와 스레드
- POSIX 표준에 따르면 fork()는 호출한 스레드만 복사합니다.
- 즉:
- 부모 프로세스 → 여러 스레드 존재 가능
- 자식 프로세스 → fork를 호출한 그 스레드만 존재 (다른 스레드는 복사되지 않음)
- 이유: 모든 스레드를 그대로 복제하면 동기화 상태, 락(lock) 보유 상태 등이 복잡해져서 일관성을 깨뜨릴 수 있기 때문이에요.
👉 따라서, fork() 후에는 자식 프로세스는 단일 스레드 상태로 시작하고, 필요하면 exec()를 호출해 새 프로그램으로 덮어씌우는 경우가 많습니다.
🔹 exec()
- exec 계열 함수(execl, execv, execve 등)는 현재 프로세스 전체를 새로운 프로그램으로 대체합니다.
- 프로세스 메모리 공간, 코드, 데이터, 스택이 모두 교체되고, 기존 스레드는 모두 사라집니다.
- 즉:
- 호출한 프로세스의 PID는 유지됨
- 실행 이미지는 완전히 새 프로그램으로 바뀜
- 남는 스레드 없음 → 새 프로그램은 항상 단일 스레드로 시작
✅ 정리하면:
- fork() → 호출한 스레드만 복사 (자식은 단일 스레드 상태).
- exec() → 전체 프로세스를 대체 (모든 스레드 사라지고 새 프로그램 시작).
🔹 스레드 풀(Thread Pool)
✅ 개념
- 프로그램 시작 시 일정 개수의 스레드를 미리 생성해두고, 이 스레드들을 작업(Task) 큐에 있는 일을 처리하는 데 재사용하는 방식.
- 즉, 매번 새로운 스레드를 만들고 없애는 대신, **재활용(Recycling)**하는 구조.
✅ 동작 방식
- 스레드 풀 초기화 → 정해진 개수의 워커 스레드(worker thread) 생성.
- 클라이언트/사용자가 작업을 요청 → 작업이 **작업 큐(task queue)**에 들어감.
- 대기 중이던 워커 스레드가 큐에서 작업을 꺼내 실행.
- 작업 완료 후 → 스레드는 종료되지 않고 다시 큐를 감시하며 다음 작업 대기.
✅ 장점
- 생성/제거 오버헤드 감소: 스레드를 매번 만들지 않고 재사용하므로 성능 향상.
- 동시성 제어 용이: 풀 크기를 제한하면 동시에 실행되는 스레드 수를 조절 가능 → CPU 과부하 방지.
- 응답 시간 단축: 요청이 들어올 때마다 바로 실행할 스레드가 준비되어 있음.
❌ 단점
- 풀 크기 설정 어려움: 너무 작으면 병목, 너무 크면 문맥 전환 오버헤드 발생.
- 장시간 블로킹 작업 문제: 워커 스레드가 오래 점유되면 다른 작업이 밀릴 수 있음.
- 복잡성 증가: 큐 관리, 예외 처리, 동기화 관리가 필요.
🔹 동기(Synchronous) vs 비동기(Asynchronous)
- 동기(Sync)
- 요청한 작업이 끝날 때까지 호출한 쪽이 결과를 기다림.
- 제어 흐름이 작업 완료 시점과 맞춰져 있음.
- 예: read() 호출 → 데이터 다 읽을 때까지 반환하지 않음.
- 비동기(Async)
- 요청한 작업을 백그라운드에서 수행하고, 호출한 쪽은 즉시 반환받음.
- 결과는 나중에 이벤트, 콜백, Future/Promise 등을 통해 알림.
- 예: aio_read() → 바로 return, 읽기 완료되면 알림.
👉 즉, “동기는 결과 반환을 기다리고, 비동기는 기다리지 않는다”는 표현은 정확합니다 ✅
🔹 블로킹(Blocking) vs 논블로킹(Non-blocking)
- 블로킹(Blocking)
- 호출한 함수가 즉시 결과를 줄 수 없으면 → 호출한 스레드를 멈추고 대기.
- 제어권을 커널/라이브러리 쪽에 넘겨주고, 작업이 끝날 때까지 안 돌려줌.
- 예: read(fd, buf, size)에서 읽을 데이터가 없으면, 데이터가 올 때까지 멈춤.
- 논블로킹(Non-blocking)
- 호출한 함수가 즉시 결과를 줄 수 없으면 에러/특정 코드(EAGAIN 등)를 반환.
- 즉, 제어권을 바로 돌려줌.
- 예: read(fd, buf, size) 호출 시 읽을 게 없으면 1 즉시 반환.
👉 따라서 “블로킹은 제어권을 넘기고, 논블로킹은 제어권을 넘기지 않는다”는 표현은 살짝 부정확해요.
정확히는:
- 블로킹 → 제어권을 넘겨주고 작업 완료까지 반환 안 함.
- 논블로킹 → 제어권을 넘겨주긴 하지만, 결과 없으면 즉시 반환해서 호출자가 다시 사용할 수 있음.
🔹 한눈에 정리 (표)
구분 동기 비동기
| 블로킹 | 작업 끝날 때까지 기다림 (ex. read()) | 콜백/알림을 쓰지만, 호출 자체는 블로킹됨 (드묾) |
| 논블로킹 | 즉시 반환, 호출자가 반복 확인해야 함 (ex. read() + 반복) | 즉시 반환 + 완료되면 알림/콜백 (ex. 이벤트 기반 I/O) |
✅ 정리하면:
- 동기/비동기 → “작업 완료 통보 방식”
- 블로킹/논블로킹 → “호출 시 제어권 반환 여부”
CPU 스케줄링
CPU를 프로세스 들 간에 교환함
일반적으로 프로세스 스케줄링을 뜻함
CPU 스케줄러 (CPU Scheduler)
CPU가 유휴 상태가 될 때마다 **운영체제(OS)**는 **준비 완료 큐(Ready Queue)**에 있는 프로세스들 중 하나를 골라 실행합니다.
이 과정은 **단기 스케줄러(Short-term Scheduler)**가 담당합니다.
- 역할: 메모리 내에서 실행 준비가 된 프로세스 중 하나를 선택 → CPU에 할당
- 목표: CPU 이용률 극대화, 처리량 증가, 대기 시간·응답 시간 최소화, 공정성 보장
1. 스케줄링 종류
① 비선점 스케줄링 (Non-preemptive Scheduling)
- 한 프로세스가 CPU를 할당받으면 자신이 종료되거나 대기 상태로 전환될 때까지 CPU를 계속 사용
- 다른 프로세스는 기다려야 함
- 장점: 컨텍스트 스위칭 오버헤드가 적음
- 단점: 응답 시간이 길어질 수 있음
- 예시 알고리즘
- FCFS (First Come First Serve)
- SJF (Shortest Job First)
- HRN (Highest Response Ratio Next)
② 선점 스케줄링 (Preemptive Scheduling)
- 실행 중인 프로세스가 있더라도 우선순위가 더 높은 프로세스가 도착하면 CPU를 빼앗아 올 수 있음
- 장점: 응답 시간이 짧아져 대화형 시스템에 적합
- 단점: 잦은 컨텍스트 스위칭으로 오버헤드 증가, 교착 상태(deadlock)나 기아(starvation) 발생 가능
- 에이징(Aging) 기법으로 낮은 우선순위 프로세스의 기아 문제를 완화
- 예시 알고리즘
- SRTF (Shortest Remaining Time First)
- Priority Scheduling
- Round Robin (RR)
- Multilevel Queue / Feedback Queue
2. 디스패처 (Dispatcher)
- 정의: 단기 스케줄러가 선택한 프로세스에게 실제로 CPU 제어권을 넘겨주는 모듈
- 주요 역할
- 컨텍스트 스위칭 (context switching)
- 사용자 모드 전환
- 프로그램의 올바른 위치(PC, 레지스터)에서 실행 재개
- 디스패처 지연 (Dispatcher Latency)
- 하나의 프로세스에서 다른 프로세스로 CPU 제어가 넘어가는 데 걸리는 시간
- → 너무 길면 시스템 성능 저하
📌 정리하면,
- 단기 스케줄러: 어떤 프로세스가 CPU를 쓸지 선택
- 디스패처: 실제로 CPU를 넘겨주는 실행자
- 비선점/선점 여부에 따라 응답성과 공정성이 달라짐
임계구역 문제
임계구역 문제 (Critical Section Problem)
여러 프로세스가 공유 자원(메모리, 파일, I/O 장치 등)에 접근할 때 **경쟁 조건(Race Condition)**을 피하기 위해 필요한 규칙을 정의한 것. 이를 위해 운영체제는 임계구역에 대한 접근을 제어하는 프로토콜을 설계해야 함.
1. 요구 조건 (3가지 조건)
- 상호 배제 (Mutual Exclusion)
- 한 번에 하나의 프로세스만 임계구역에 진입할 수 있어야 함.
- 다른 프로세스가 이미 임계구역에 있으면, 나머지는 대기해야 함.
- 진행 (Progress)
- 임계구역에 들어가려는 프로세스가 없을 경우, 들어갈 프로세스를 결정하는 데 불필요한 지연이 없어야 함.
- 즉, CPU가 놀고 있는데도 임계구역 진입을 막으면 안 됨.
- 한정된 대기 (Bounded Waiting)
- 특정 프로세스가 임계구역 진입을 무한정 기다리게 두어서는 안 됨.
- 언젠가는 반드시 임계구역에 들어갈 수 있어야 한다는 공정성 보장 조건.
커널의 선점 여부
운영체제의 커널이 프로세스를 언제까지 실행시키는지와 관련 있음.
1. 비선점형 커널 (Non-preemptive Kernel)
- 커널 모드에 들어간 프로세스는 스스로 CPU를 양보하거나 작업이 끝날 때까지 계속 실행.
- 장점: 동기화가 상대적으로 단순 → 임계구역 충돌 발생 확률 낮음.
- 단점: 한 프로세스가 오래 점유하면 다른 프로세스 대기 시간이 길어짐 → 시스템 반응성 저하.
2. 선점형 커널 (Preemptive Kernel)
- 커널 모드에서 실행 중이더라도 운영체제가 강제로 CPU를 빼앗아 다른 프로세스 실행 가능.
- 장점: 시스템 반응성 ↑ (특히 실시간 시스템에 적합).
- 단점: 임계구역에서 선점되면 동기화 문제 발생 → 세마포어, 뮤텍스, 모니터 같은 동기화 도구 필요.
✅ 요약
- 임계구역 문제 → 상호배제, 진행, 한정된 대기 조건 충족 필요.
- 비선점형 커널은 단순하지만 비효율적, 선점형 커널은 효율적이지만 동기화 기법 필요.
피터슨 알고리즘
피터슨 알고리즘 (Peterson’s Algorithm)
- 정의: 두 개의 프로세스가 하나의 공유 자원을 안전하게 사용할 수 있도록 보장하는 소프트웨어 기반 임계구역 해결 알고리즘.
- 아이디어: "상호 배제"를 위해 플래그 변수 + turn 변수를 이용해 락(Locking) 개념을 구현.
1. 기본 구조
- flag[2]: 프로세스가 임계구역에 들어가고 싶다는 의사를 표시 (true/false)
- turn: 두 프로세스 중 누구 차례인지를 알려주는 변수
// 프로세스 i의 코드 (i = 0 or 1)
do {
flag[i] = true; // 임계구역 진입 의사 표시
turn = j; // 상대방 차례로 설정
while (flag[j] && turn == j); // 상대방이 원하고 차례이면 대기
// ---- 임계구역 시작 ----
critical_section();
// ---- 임계구역 끝 ----
flag[i] = false; // 임계구역 나옴
remainder_section();
} while (true);
2. 특징 (3대 조건 충족 여부)
- 상호 배제 (Mutual Exclusion)
- 두 프로세스가 동시에 임계구역에 진입할 수 없음.
- flag와 turn 조합으로 보장.
- 진행 (Progress)
- 임계구역을 원하지 않는 프로세스는 다른 프로세스의 진입을 방해하지 않음.
- 한정된 대기 (Bounded Waiting)
- 한 프로세스가 무한정 기다리지 않도록 보장 (turn 변수가 번갈아 기회를 줌).
3. 장단점
- 장점
- 순수 소프트웨어적 방법 (하드웨어 지원 필요 없음).
- 임계구역 문제의 3대 조건을 모두 만족.
- 단점
- 두 프로세스 환경에서만 동작 (N개 프로세스는 불가능).
- 현대의 멀티코어 환경에서는 메모리 재정렬/캐시 동기화 문제 때문에 실제로는 쓰이지 않음.
- 대신 하드웨어 기반 Test-and-Set, Compare-and-Swap 같은 명령어나 세마포어, 뮤텍스, 모니터를 사용.
4. 락킹과의 관계
- Peterson 알고리즘은 락(Lock) 개념을 소프트웨어적으로 구현한 초기 방식.
- 단일 CPU 환경에서는 "인터럽트 금지" 방식으로도 해결 가능했지만, 멀티코어·멀티프로세서 환경에서는 하드웨어 락 지원 없이는 Peterson 알고리즘이 깨질 수 있음.
👉 정리: Peterson 알고리즘은 교과서적 중요성이 크고, 실제 시스템에서는 하드웨어 명령어나 동기화 도구가 더 많이 사용됨.
뮤텍스 락 (Mutex Lock)
- Mutual Exclusion의 줄임말.
- 임계구역(critical section)에 진입하기 전에 **락(lock)**을 획득해야 하고, 임계구역에서 나오면서 반드시 **락을 해제(unlock)**해야 함.
- 한 시점에는 오직 하나의 프로세스/스레드만 락을 보유할 수 있음 → 상호 배제 보장.
1. 기본 원리
// Pseudo code
acquire(lock); // 임계구역 들어가기 전에 락 획득
critical_section();
release(lock); // 임계구역 빠져나올 때 락 반환
- acquire(): 락을 얻을 수 있을 때까지 기다림.
- release(): 락을 다른 프로세스가 사용할 수 있도록 반환.
2. 장점
- 구현이 단순하고 직관적.
- 선점형 커널 환경에서도 안전하게 임계구역 보호 가능.
- 현대 운영체제에서 스레드 동기화 기본 도구로 널리 사용됨.
3. 단점
- 바쁜 대기 (Busy Waiting, Spin Lock)
- 프로세스가 락을 얻을 때까지 계속 루프를 돌며 기다림 → CPU 낭비.
- 예:
- while (lock == 1); // 다른 스레드가 락 반환할 때까지 계속 반복
- 데드락(Deadlock) 가능
- 락을 해제하지 못하거나, 여러 락을 교착 상태로 요청할 경우 발생.
- 우선순위 역전(Priority Inversion)
- 낮은 우선순위 프로세스가 락을 보유하면, 높은 우선순위 프로세스도 기다려야 함.
4. 개선 방법
- 세마포어(Semaphore): 대기 상태를 큐에 넣고 블록(block) → 바쁜 대기 해결.
- 모니터(Monitor): 고수준 언어에서 동기화 지원.
- 혼합 기법: 짧은 시간은 스핀 락, 오래 걸리면 블록 (하이브리드 락).
✅ 요약
- 뮤텍스 락은 상호 배제를 보장하는 가장 단순한 방법.
- 하지만 기본 구현은 바쁜 대기(Spin Lock) 문제를 가진다.
- 실제 시스템에서는 세마포어나 모니터 등과 함께 개선된 형태로 사용된다.
세마포어 (Semaphore)
- 1965년 Dijkstra가 제안한 동기화 기법.
- 공유 자원에 대한 접근을 제어하기 위해 **정수 변수 S와 두 개의 원자적 연산(wait, signal)**을 사용.
- 커널이 제공하는 원자적 연산이기 때문에 동시 실행 환경에서도 안전하게 동작.
1. 두 가지 연산
- wait (P 연산)
- 자원을 얻기 전 검사
- 사용 가능 자원이 없으면 대기
- wait(S) { while (S <= 0); // 바쁜 대기 (Spin) or 블록 S--; }
- signal (V 연산)
- 자원 사용이 끝난 후 반환
- signal(S) { S++; }
2. 세마포어의 종류
- 이진 세마포어 (Binary Semaphore)
- 값이 0 또는 1만 가짐.
- 사실상 뮤텍스 락과 동일하게 동작.
- 임계구역 보호에 사용.
- 계수 세마포어 (Counting Semaphore)
- 값이 0 이상 정수.
- 동시에 여러 개의 프로세스가 공유 자원에 접근할 수 있도록 허용.
- 예: DB 연결 풀, 프린터 3대 → 초기값 S=3.
3. 특징
✅ 장점
- 뮤텍스보다 일반적: 여러 자원 동시 관리 가능.
- 바쁜 대기 문제 해결 가능: 대기 중인 프로세스를 큐에 넣어 블록시키고, signal 시 깨움.
⚠️ 단점
- 프로그래밍 실수 위험 (wait/signal 불일치 → 데드락, 기아 문제).
- 관리가 어렵기 때문에 고수준 언어에서는 **모니터(Monitor)**가 더 선호됨.
4. Mutex vs Semaphore 비교
구분 뮤텍스(Mutex) 세마포어(Semaphore)
| 자원 수 | 1개만 보호 | N개 자원까지 보호 가능 |
| 값 | 0/1 (binary) | 0 이상 정수 |
| 소유권 | 스레드가 소유 (owner만 unlock 가능) | 소유 개념 없음 |
| 사용 용도 | 임계구역 보호 | 자원 개수 제어, 프로세스 동기화 |
| 구현 난이도 | 단순 | 상대적으로 복잡 |
✅ 요약
- 세마포어 = 정수 변수 + wait/signal 연산
- 이진 세마포어 = 뮤텍스
- 계수 세마포어 = 여러 자원 관리 가능
- 하지만 프로그래밍 복잡성 때문에 실무에서는 주로 뮤텍스 + 조건변수, 모니터를 사용
1. 모니터(Monitor)란?
- 고수준 언어에서 제공하는 동기화 도구
- 세마포어처럼 직접 wait/signal을 다루는 대신, 언어 차원에서 임계구역 진입/대기/신호를 관리해줌.
- 즉, 프로그래머가 동기화 로직을 일일이 짜는 대신, 모니터가 자동으로 상호배제를 보장해 줌.
➡️ 세마포어의 단점(코드 복잡성, wait/signal 불일치 → 교착상태 가능)을 해결하기 위한 추상화 기법.
2. 모니터의 구성 요소
- 공유 변수 (Shared Variables)
- 모니터 내부에서만 접근 가능한 자원(데이터 구조).
- 프로시저 (Procedures)
- 공유 변수를 접근할 수 있는 루틴.
- 임계구역은 이 루틴 안에서만 존재하며, 자동으로 상호배제 보장.
- 조건 변수 (Condition Variables)
- 모니터 안에서 프로세스 동기화를 위해 사용.
- wait() : 현재 프로세스를 조건 대기 큐에 넣고, 다른 프로세스에게 제어권 넘김.
- signal() : 대기 중인 프로세스를 깨움.
3. 모니터 동작 방식
- 한 번에 하나의 프로세스만 모니터 내부 실행 가능.
- 다른 프로세스가 들어오면 자동으로 블록됨 (상호배제 보장).
- 조건 변수로 대기/신호를 제어 → 세마포어 wait/signal과 유사하지만 자동 관리됨.
4. 예시 (생산자-소비자 문제)
monitor ProducerConsumer {
int buffer[N];
int count = 0;
condition notFull, notEmpty;
procedure insert(item) {
if (count == N) wait(notFull); // 버퍼가 꽉 찼으면 대기
buffer[count++] = item;
signal(notEmpty); // 소비자에게 알림
}
procedure remove() {
if (count == 0) wait(notEmpty); // 버퍼가 비었으면 대기
item = buffer[--count];
signal(notFull); // 생산자에게 알림
return item;
}
}
- 생산자는 insert() 호출 → 버퍼 꽉 차면 wait(notFull).
- 소비자는 remove() 호출 → 버퍼 비면 wait(notEmpty).
- 모니터는 상호배제 + 동기화를 자동 관리.
5. 장단점
✅ 장점
- 상호배제 자동 보장 → 프로그래머가 실수로 놓칠 위험 감소.
- 코드 가독성 높고, 동기화 오류 줄어듦.
- 고급 언어(Java, C#, Python 등)에서 널리 지원 (synchronized, lock 구문 등).
⚠️ 단점
- 구현이 복잡해 하드웨어/저수준 언어(C)에서는 직접 지원 어렵다.
- 잘못된 조건 변수 사용 시 기아 가능성.
6. 현대 운영체제와 모니터
- Java → synchronized 블록, wait() / notify()
- C# → lock, Monitor.Wait() / Monitor.Pulse()
- Python → threading.Condition
즉, 세마포어는 OS 수준의 원시적 도구라면,
모니터는 언어/런타임 수준의 고수준 동기화 도구라 할 수 있음.
✅ 요약
- 모니터는 세마포어보다 추상화된 동기화 도구.
- 프로세스/스레드가 동시에 모니터 안에 들어올 수 없도록 상호배제를 자동 보장.
- 조건 변수(wait, signal)를 통해 세밀한 동기화 제어 가능.
교착상태 & 기아
1. 교착상태 (Deadlock)
- 정의: 프로세스들이 서로가 가진 자원을 기다리며 무한 대기 상태에 빠진 것.
- 예: P1은 프린터를 가지고 플로터를 기다리고, P2는 플로터를 가지고 프린터를 기다리는 경우.
발생 조건 (Coffman의 4가지 조건 – 모두 만족해야 발생)
- 상호 배제 (Mutual Exclusion)
- 자원은 한 번에 한 프로세스만 사용 가능.
- 점유 대기 (Hold and Wait)
- 최소 하나의 자원을 점유한 채로 다른 자원을 기다림.
- 비선점 (No Preemption)
- 할당된 자원은 강제로 빼앗을 수 없음.
- 순환 대기 (Circular Wait)
- 프로세스들이 원형으로 서로가 가진 자원을 기다림.
2. 기아 (Starvation)
- 정의: 특정 프로세스가 우선순위 문제나 자원 할당 정책 때문에 무한히 자원을 얻지 못하고 기다리는 상태.
- 예: 우선순위 스케줄링에서 낮은 우선순위 프로세스가 계속 밀려 실행되지 못하는 경우.
원인
- 우선순위 기반 스케줄링
- 자원 할당 시 특정 프로세스에 불리한 정책
- 무한 대기 큐 구조
3. 우선순위 역전 (Priority Inversion)
- 정의: 낮은 우선순위 프로세스가 자원을 가지고 있어서, 높은 우선순위 프로세스가 그 자원을 기다리며 실행되지 못하는 상황.
- 중간 우선순위 프로세스가 계속 실행되면 높은 우선순위 프로세스는 더 오래 기다리게 됨 → 실질적 기아 발생.
해결 방법
- Priority Inheritance (우선순위 상속):
- 낮은 우선순위 프로세스가 자원을 가지고 있으면 임시로 높은 우선순위를 부여해 빠르게 자원 반환하도록 함.
- Priority Ceiling (우선순위 천장):
- 공유 자원에 대해 최대 우선순위를 미리 지정해, 해당 자원에 접근 시 우선순위를 높여줌.
4. 교착상태 vs 기아 비교
구분 교착상태 (Deadlock) 기아 (Starvation)
| 정의 | 프로세스들이 서로 자원을 기다리며 영원히 대기 | 특정 프로세스가 무한히 자원을 못 얻는 상태 |
| 원인 | 자원 할당의 원형 대기 | 우선순위 정책, 불공정 스케줄링 |
| 발생 조건 | Coffman 4조건 필요 | 특정 조건 없음 |
| 해결 | 예방, 회피, 탐지 및 회복 | Aging(우선순위 점진 상향), 공정 스케줄링 |
✅ 요약
- 교착상태: 여러 프로세스가 서로 자원을 기다리며 꼼짝 못하는 상태 (시스템 전체 멈춤).
- 기아: 특정 프로세스가 무한히 자원을 못 얻는 상태 (불공정성).
- 우선순위 역전은 기아의 한 사례 → 우선순위 상속/천장 기법으로 해결 가능.
교착상태 해결 방법
교착상태를 다루는 방법은 크게 네 가지로 나눌 수 있습니다:
1. 예방 (Deadlock Prevention)
- Coffman의 4가지 필요 조건 중 하나 이상을 아예 성립하지 않도록 설계하는 방식.
방법
- 상호 배제(Mutual Exclusion) 부정
- 자원을 여러 프로세스가 동시에 사용할 수 있게 설계 (현실적으로 모든 자원에 불가능).
- 점유 대기(Hold & Wait) 부정
- 프로세스가 실행 전에 필요한 모든 자원을 한 번에 할당.
- 단점: 자원 활용률↓, 기아 발생 가능.
- 비선점(No Preemption) 부정
- 자원을 빼앗을 수 있게 함 (ex: CPU 스케줄링 선점형, 메모리 페이지 스왑).
- 순환 대기(Circular Wait) 부정
- 자원에 번호를 붙여, 오름차순으로만 할당.
➡️ 장점: 교착상태 자체를 원천적으로 막음.
➡️ 단점: 자원 낭비, 활용률 저하.
2. 회피 (Deadlock Avoidance)
- 교착상태가 발생하지 않도록 자원 할당을 신중히 결정하는 방식.
- 대표적 방법: 은행가 알고리즘 (Banker’s Algorithm)
- 자원 요청 시, 현재 상태가 **안전 상태(Safe State)**인지 검사 후 허용.
- 안전하지 않으면 요청을 거절.
➡️ 장점: 교착상태 자체를 피할 수 있음.
➡️ 단점: 프로세스의 최대 자원 요구량을 미리 알아야 함 → 비현실적.
3. 탐지 후 회복 (Deadlock Detection & Recovery)
- 교착상태 발생을 허용하되, 탐지 알고리즘으로 발견하고 이후 회복.
탐지 방법
- 자원 할당 그래프(Resource Allocation Graph) 활용
- 순환(Cycle)이 있으면 교착상태 가능.
회복 방법
- 프로세스 종료
- 교착상태에 연루된 프로세스들을 강제로 종료.
- 한 번에 모두 종료 vs 하나씩 종료.
- 자원 선점(Preemption)
- 일부 프로세스에서 자원을 빼앗아 다른 프로세스에 할당.
- 단점: 프로세스 상태 rollback 필요 → 오버헤드 발생.
4. 무시 (Deadlock Ignorance)
- 교착상태를 해결하는 비용이 너무 크기 때문에, 실제로는 무시하는 방법.
- 대부분의 범용 OS (Windows, Linux, macOS)는 이 방법 사용.
- 교착상태 발생 빈도가 낮고, 발생 시 시스템을 재부팅하면 됨.
➡️ 장점: 구현 단순, 오버헤드 없음.
➡️ 단점: 일부 프로세스가 멈출 수 있음.
✅ 요약
방법 특징 장점 단점
| 예방 (Prevention) | 4조건 중 하나 제거 | 교착상태 절대 발생 X | 자원 낭비, 비효율 |
| 회피 (Avoidance) | 안전 상태 유지 | 교착상태 피할 수 있음 | 최대 자원 요구량 필요 |
| 탐지 & 회복 (Detection & Recovery) | 교착상태 허용 후 탐지·복구 | 자원 활용률 ↑ | 탐지·복구 비용 큼 |
| 무시 (Ignore) | 그냥 무시 | 단순, 효율 ↑ | 교착상태 시 시스템 멈춤 |
메모리 관리 전략
CPU는 PC(program counter)가 지시하는데로 메모리에서 다음 수행할 명령어를 가져옴
주 메모리 ↔ 프로세서 자체에 내장한 레지스터는 CPU의 유일한 범용 저장장치
주 메모리 접근시 속도 차이로 cpu클록 틱 사이클이 소요되고, 지연(stall) 현상이 발생함
논리, 물리 주소
1. CPU와 메모리 접근
- CPU는 **프로그램 카운터(PC, Program Counter)**가 가리키는 메모리 주소에서 명령어를 가져와 실행.
- CPU 내부에는 **레지스터(Register)**가 있어서 연산에 직접 사용되는 데이터를 저장.
- *주 메모리(Main Memory, RAM)**는 CPU가 데이터를 읽고 쓰는 기본 저장 장치지만, **CPU 클록 대비 속도가 느려 지연(stall)**이 발생.
- 이를 줄이기 위해 **캐시 메모리(Cache)**가 등장 (CPU ↔ 캐시 ↔ 메모리 구조).
2. 논리 주소와 물리 주소
- 논리 주소(Logical Address, 가상주소)
- CPU가 생성하는 주소 (프로그램 관점).
- 각 프로세스는 독립적인 주소 공간을 가진다고 "착각"할 수 있음.
- 물리 주소(Physical Address)
- 실제 메모리 하드웨어(RAM)가 갖는 주소.
- 주소 변환(Address Translation)
- *MMU (Memory Management Unit)**가 논리 주소 → 물리 주소 변환을 담당.
- 보통 **재배치 레지스터(Relocation Register)**를 사용해서 시작 위치를 보정.
➡️ 이를 통해 다중 프로세스 환경에서도 서로 간섭하지 않고 메모리 사용 가능.
3. 동적 적재 (Dynamic Loading)
- 프로세스 전체를 메모리에 올리지 않고, 필요한 부분만 메모리에 적재.
- 장점: 메모리 사용 효율 ↑, 다중 프로그래밍에 유리.
- 예: 라이브러리를 호출할 때 실제 필요한 함수만 메모리에 로드.
4. 메모리 할당 기법 (연속 메모리 할당)
프로세스들을 메모리에 배치할 때, 빈 공간(free hole)을 어떻게 선택할지 결정하는 방식.
- 최초 적합(First Fit)
- 처음 발견한 충분히 큰 공간에 배치.
- 속도 빠름, 하지만 단편화(fragmentation) 발생 가능.
- 최적 적합(Best Fit)
- 크기가 가장 작은, 딱 맞는 공간에 배치.
- 메모리 낭비 최소화, 그러나 작은 조각(외부 단편화) 많이 생김.
- 최악 적합(Worst Fit)
- 가장 큰 공간에 배치.
- 큰 공간을 나눠 사용 → 큰 프로세스를 위한 공간 확보 가능.
- 하지만 실제 효율은 떨어짐.
✅ 요약
- CPU ↔ 메모리 속도 차이를 줄이기 위해 캐시가 필요.
- 논리 주소는 CPU가 보는 주소, 물리 주소는 실제 메모리 주소.
- MMU가 주소 변환 수행.
- 동적 적재로 메모리 효율성을 높임.
- 메모리 배치 기법: 최초 적합, 최적 적합, 최악 적합 → 각각 속도/효율/낭비 측면에서 장단점 다름.
*** 동적 할당 → 외부 단편화 발생
1. 단편화(Fragmentation)
메모리 할당 과정에서 생기는 사용하지 못하는 빈 공간 문제.
(1) 외부 단편화 (External Fragmentation)
- 여러 번의 메모리 할당/해제로 인해 자잘한 빈 공간이 여기저기 흩어져 전체적으로는 충분한 메모리가 있어도 큰 프로세스를 넣을 수 없는 상황.
- 예: 10KB 프로세스 필요 → 빈 공간이 2KB+3KB+5KB로 나뉘어 있으면 수용 불가.
- 해결 방법:
- 압축(Compaction): 메모리 내용을 옮겨서 빈 공간을 하나로 모음.
(2) 내부 단편화 (Internal Fragmentation)
- 할당된 블록이 실제 요구보다 큰 경우 발생 → 블록 내에 낭비된 공간 존재.
- 예: 12KB 요청 → 16KB 단위 블록 할당 → 4KB 낭비.
2. 세그멘테이션 (Segmentation)
- *프로그래머가 논리적으로 프로그램을 나눈 단위(세그먼트)**를 메모리에 배치하는 기법.
- 세그먼트 = 코드, 데이터, 스택 등 가변 크기 블록.
- CPU가 생성하는 주소 = (세그먼트 번호, 오프셋)
- *세그먼트 테이블(Segment Table)**을 통해 물리 주소 변환.
✅ 장점:
- 프로그래머 관점 그대로 메모리 관리 가능 (논리적 단위 유지).
- 외부 단편화 발생하지만, 내부 단편화는 적음.
3. 페이징 (Paging)
- 메모리를 고정 크기 블록으로 나누는 기법.
- 프레임(Frame): 물리 메모리를 나눈 블록.
- 페이지(Page): 프로세스의 논리 주소 공간을 나눈 블록.
- 크기 동일 (예: 4KB).
➡️ CPU가 생성하는 주소 = (페이지 번호, 페이지 오프셋)
➡️ 페이지 테이블(Page Table): 페이지 번호 → 프레임 번호 변환.
✅ 장점:
- 외부 단편화 없음 (모두 같은 크기).
- 내부 단편화만 발생 (마지막 페이지 일부 낭비).
4. TLB (Translation Lookaside Buffer)
- 페이지 테이블 접근은 메모리 참조이므로 느림 → 매번 하면 2번 메모리 접근(페이지 테이블 + 실제 데이터) 필요.
- 이를 줄이기 위해 TLB라는 고속 캐시 사용.
- 최근 변환된 페이지 번호 ↔ 프레임 번호를 저장해 주소 변환 속도 향상.
✅ 요약
- 단편화
- 외부 단편화: 작은 조각 흩어짐 → 압축 or 페이징/세그멘테이션으로 해결.
- 내부 단편화: 블록 단위 때문에 남는 공간 발생.
- 세그멘테이션: 논리적 단위(코드, 데이터, 스택)를 가변 크기로 관리. → 프로그래머 친화적, 외부 단편화 존재.
- 페이징: 고정 크기 블록(Frame/Page)으로 관리. → 외부 단편화 없음, 내부 단편화 존재.
- 페이지 테이블 필요 → 성능 저하 → TLB로 보완.
. 가상 메모리(Virtual Memory)란?
- 프로세스 전체가 물리 메모리에 적재되지 않아도 실행 가능하게 하는 기법.
- 사용자 입장에서는 매우 큰 “연속적인 메모리 공간”을 쓰는 것처럼 보이지만, 실제로는 물리 메모리(RAM)와 보조기억장치(디스크, SSD 등)를 조합해서 구현.
- *논리 주소(가상 주소)**와 물리 주소를 분리하여 관리.
2. 필요성
- 메모리 효율성 향상
- 전체 프로그램을 메모리에 올릴 필요 없이 필요한 부분만 적재 → 더 많은 프로세스를 동시에 실행 가능 (멀티프로그래밍).
- 보호(Protection)
- 프로세스마다 독립적인 주소 공간 제공 → 서로 침범 불가.
- 유연성
- 실제 메모리보다 큰 프로그램도 실행 가능.
1. 요구 페이징 (Demand Paging)
- 정의: 프로세스 전체를 메모리에 적재하지 않고, 실제로 필요할 때 해당 페이지를 메모리에 적재하는 기법.
- 프로그램 실행 시 처음에는 필요한 최소한의 페이지만 로드 → 나머지는 실행 도중 필요할 때 디스크에서 불러옴.
(1) 참조의 지역성(Locality of Reference)
- 시간 지역성(Temporal Locality): 최근 접근한 데이터는 곧 다시 접근될 가능성 ↑
- 공간 지역성(Spatial Locality): 접근한 주소 근처의 데이터가 곧 참조될 가능성 ↑
- 요구 페이징은 이 성질을 활용 → 성능이 실제로는 꽤 좋음.
(2) 페이지 부재(Page Fault)
- CPU가 요청한 페이지가 메모리에 없을 때 발생.
- 처리 과정:
- CPU → 페이지 없음 감지 (트랩 발생).
- OS → 디스크에서 해당 페이지 적재.
- 페이지 테이블 갱신 후 재실행.
(3) 유효 접근 시간 (Effective Access Time, EAT)
- 실제 메모리 접근 시간은 **페이지 부재율(p)**에 비례.
EAT=(1−p)×메모리 접근 시간+p×페이지 폴트 처리 시간EAT = (1 - p) \times \text{메모리 접근 시간} + p \times \text{페이지 폴트 처리 시간}
EAT=(1−p)×메모리 접근 시간+p×페이지 폴트 처리 시간
- 페이지 폴트 처리 시간은 디스크 I/O가 포함되므로 매우 크다.
- 따라서 페이지 부재율은 극히 낮아야 성능 유지 가능.
2. 쓰기 시 복사 (Copy-on-Write, COW)
- 정의: 프로세스가 fork()나 exec()로 복제될 때, 모든 페이지를 처음부터 복사하지 않고 → 부모와 자식이 같은 물리 페이지를 공유하다가 실제로 쓰기(write) 연산이 발생하는 순간 복사하는 기법.
(1) 동작 원리
- fork() 시 자식 프로세스는 부모의 페이지를 그대로 참조. (읽기 전용)
- 어느 한쪽이 해당 페이지를 쓰기(write) 시도 → 페이지 부재 발생.
- OS가 그 시점에만 페이지를 새로 복사해서 분리.
(2) 장점
- 불필요한 페이지 복사를 방지 → 메모리 절약.
- fork() 후 exec()가 곧바로 이어지는 경우(자식이 새로운 프로그램 실행) → 부모 메모리 복사는 거의 필요 없음.
✅ 요약
- 요구 페이징(Demand Paging)
- 필요한 페이지만 적재 → 메모리 효율 ↑
- 참조의 지역성 때문에 실제 성능이 만족스러움
- 하지만 페이지 부재율이 높아지면 성능 저하 (스래싱 위험)
- 쓰기 시 복사(Copy-on-Write, COW)
- 부모-자식이 페이지를 공유하다가 쓰기 시점에만 복사
- fork() + exec() 최적화에 매우 효과적
1. 페이지 교체(Page Replacement)란?
- 가상 메모리 시스템에서, 새로운 페이지를 메모리에 불러와야 하는데 빈 프레임이 없을 때 → 기존에 있던 페이지 중 하나를 교체하는 과정.
- 어떤 페이지를 교체하느냐에 따라 **페이지 부재율(Page Fault Rate)**이 크게 달라짐.
- 목표: 페이지 부재율을 최소화하는 알고리즘 선택.
2. 프레임 할당 (Frame Allocation)
- 각 프로세스에 얼마나 많은 프레임을 줄 것인지 결정.
- 너무 적으면 → 페이지 폴트 잦음.
- 너무 많으면 → 다른 프로세스가 부족해짐.
3. 주요 페이지 교체 알고리즘
(1) 최적 교체 (Optimal Replacement, OPT)
- 앞으로 가장 오랫동안 사용하지 않을 페이지를 교체.
- 이론적으로 가장 좋은 성능 → 페이지 부재율 최소.
- 하지만 미래 참조를 알 수 없으므로 실제 구현 불가, 비교 기준으로 사용.
(2) FIFO (First-In First-Out)
- 메모리에 가장 오래 있던 페이지를 교체.
- 구현 단순하지만, 성능이 나쁠 수 있음.
- Belady의 모순(Belady’s Anomaly): 프레임 수를 늘렸는데도 페이지 폴트가 증가할 수 있음.
(3) LRU (Least Recently Used)
- 가장 오랫동안 사용하지 않은 페이지를 교체.
- 과거 사용 이력이 미래 사용 가능성과 연관 있다는 "참조의 지역성(Locality)"에 기반.
- 일반적으로 가장 널리 쓰임.
- 구현 방식:
- 카운터 기반 (최근 접근 시간 기록)
- 스택 기반 (최근 사용된 페이지를 스택 상단에 유지)
(4) LFU (Least Frequently Used)
- 사용 빈도가 가장 낮은 페이지를 교체.
- 문제: 최근 집중적으로 쓰였지만 앞으로 필요 없는 페이지도 남아있을 수 있음.
(5) Clock 알고리즘 (Second Chance)
- FIFO 변형: 교체 대상 페이지에 참조 비트(Reference Bit) 확인.
- 참조된 페이지는 한 번 기회를 주고 다음 후보로 넘김.
- 성능은 LRU에 근접하면서 구현은 단순.
4. 선택 기준
- 실제 운영체제에서는 LRU 또는 Clock 알고리즘이 주로 사용.
- 이유:
- OPT는 이상적이지만 불가능.
- FIFO는 성능 불안정.
- LRU는 locality 가정 하에 안정적으로 좋은 성능.
- Clock은 LRU 근사치로 구현 효율성 높음.
✅ 요약
- 페이지 교체는 빈 프레임이 없을 때 어떤 페이지를 제거할지 결정하는 문제.
- 목표: 페이지 부재율 최소화.
- 대표 알고리즘: OPT, FIFO, LRU, LFU, Clock.
- 실제 OS는 **LRU(또는 근사 알고리즘)**을 가장 많이 사용.
1. 쓰레싱(Thrashing)이란?
- 과도한 페이지 부재(Page Fault) 때문에 CPU가 실제 작업보다 페이징 처리에 더 많은 시간을 소모하는 현상.
- 결과적으로 CPU 이용률 급격히 저하, 시스템 성능이 심각하게 떨어짐.
- 즉, 프로세스가 필요한 페이지가 메모리에 거의 없어서 계속 디스크 ↔ 메모리 스왑이 일어나는 상태.
2. 원인
- 메모리 과다 할당 부족
- 프로세스들이 동시에 실행되면서 각 프로세스에 충분한 프레임을 배정하지 못한 경우.
- 지역성(Locality) 위반
- 요구 페이징은 참조의 지역성을 가정하는데, 프로그램이 메모리 전체를 자주 건드리면 페이지 폴트 ↑.
- 다중 프로그래밍 정도(Multiprogramming Degree) 과도
- 너무 많은 프로세스를 동시에 메모리에 올려두면 각 프로세스가 필요한 최소 프레임을 확보하지 못함.
3. 증상
- 페이지 부재율(Page Fault Rate) ↑
- CPU 이용률(CPU Utilization) ↓
- 디스크 I/O 폭증
- 프로그램 실행 속도 급격히 저하
4. 해결 방법
(1) 최소 프레임 보장
- 각 프로세스에 필요한 최소 프레임 수를 보장해야 함.
- 예: 페이지 교체 알고리즘과 함께 최소 프레임 개수를 할당.
(2) 작업 집합 모델 (Working Set Model)
- 프로세스가 일정 시간 동안 자주 참조하는 페이지 집합 = 작업 집합(Working Set).
- 이 집합을 모두 메모리에 적재 → 페이지 폴트 감소.
(3) PFF (Page Fault Frequency) 방식
- 페이지 폴트율을 측정해 임계값 초과 시 → 프레임을 늘려주고, 낮으면 줄임.
- 동적으로 프레임 수를 조절해 thrashing 방지.
(4) 다중 프로그래밍 정도 조절
- 시스템에 동시에 실행되는 프로세스 수를 줄임.
- 즉, 일부 프로세스를 swap-out 해서 나머지 프로세스가 충분한 프레임 확보하도록 함.
✅ 요약
- Thrashing = CPU가 일 못 하고 페이징만 하는 상태.
- 원인: 프레임 부족, 지역성 위반, 다중 프로그래밍 과도.
- 해결: 최소 프레임 보장, 작업 집합(Working Set), PFF(Page Fault Frequency), 다중 프로그래밍 정도 조절.
'CS > 운영체제' 카테고리의 다른 글
| fork 함수와 프로세스 (0) | 2022.03.01 |
|---|












