운영체제
- 메모리의 커널 영역에 적재되는 특별한 프로그램
- 자원들을 분배/관리하여 다른 프로그램들이 올바르게 실행되도록 운영하는 응용프로그램을 위한 프로그램
- 하드웨어 ↔️ 운영체제 ↔️ 응용프로그램s
커널
- 운영체제의 핵심
- 운영체제가 제공하는 기능들 중 가장 핵심적인 기능
- 커널이 제공하는 가장 핵심적인 기능?
- 자원에 접근/조작 하는 기능
- 응용 프로그램들이 자원에 직접 접근할 경우, 자원을 동시에 사용해 충돌이 일어날 수 있다.
- A 프로그램과 B프로그램이 동시에, CPU를 사용해 충돌하는 상황
- A프로그램이 데이터를 저장하고 있는데, B프로그램이 같은 장소에 데이터를 덮어 쓰는 상황
- 따라서, 응용 프로그램들이 자원에 접근할 때, 반드시 운영체제를 통해 접근하도록하여, 자원을 보호
- 응용 프로그램들이 자원에 직접 접근할 경우, 자원을 동시에 사용해 충돌이 일어날 수 있다.
- 사용자가 실행하는 응용 프로그램들은 시스템 자원에 직접 접근이 가능할까? ➡️ 불가능!
- 프로그램이 올바르고 안전하게 실행되게 도와주는 기능
- 자원에 접근/조작 하는 기능
- 커널에 속하지 않는 기능
- UI (User Interface)
- 커널이 제공하는 가장 핵심적인 기능?
운영체제의 서비스 종류
프로세스 관리
- 수많은 프로세스(현재 실행 중인 프로그램)들이 동시에 실행할 수 있도록 관리
- 동시다발적으로 생성/실행/삭제되는 프로세스들을 문제없이 관리
자원 접근 / 할당
- CPU 스케줄링
- 여러 프로세스들 중 어떤 프로세스를 먼저 할당할까?
- 현재 프로세스를 얼마나 오래 실행할까?
- 메모리 자원 관리
- 메모리는 한정되어 있다 ➡️ 모든 프로세스를 메모리에 적재하는 것은 자원 낭비! ➡️ 페이징 기법, 스와핑 기법 등...
- 입출력장치 접근 관리
- 운영체제의 커널 영역에 있는 인터럽트 서비스 루틴으로 관리
파일 시스템 관리
- 파일 시스템 = 디렉토리(폴더) + 파일
시스템 콜 / 이중 모드
시스템 콜 (시스템 호출)
- 커널 모드로 전환하여 실행하기 위해 호출
- 일종의 소프트웨어 인터럽트
- 운영체제마다 정해진 명령어(함수)를 통해 호출
- 응용 프로그램 ➡️ (시스템 콜) ➡️ 운영체제의 커널 모드 전환
- 예시) 리눅스의 시스템 콜
- 프로그램 관리 시스템 콜
- fork() : 새 프로세스 실행
- execve() : 프로세스 실행
- exit() : 프로세스 종료
- 파일 관리 시스템 콜
- open() : 파일 열기
- clase() : 파일 닫기
- read() : 파일 읽기
- write() : 파일 쓰기
- 그 외, 디렉토리 관리, 파일 시스템 관리 등 ...
- 프로그램 관리 시스템 콜
- strace 명령어를 이용해, 각 명령어마다 실행되는 시스템 콜 요청과정을 관찰할 수 있다. (아래 사진은 "strace ls")
이중모드
- CPU가 명령어를 실행하는 모드를 사용자 모드 ↔️ 커널 모드로 구분하는 방식
- 사용자 모드 ↔️ [시스템 콜] ↔️ 커널 모드
- 플래그 레지스터들 중 슈퍼바이저 플래그로 사용자모드, 커널 모드를 구분한다.
- 슈퍼바이저 플래그가 0 ➡️ 사용자 모드
- 슈퍼바이저 플래그가 1 ➡️ 커널 모드
사용자 모드
- 운영체제 서비스를 제공받을 수 없는 실행 모드 ➡️ 커널 영역의 코드를 실행할 수 없는 모드 ➡️ 자원에 접근 불가
커널 모드
- 운영체제의 서비스를 제공받을 수 있는 실행 모드 ➡️ 자원 접근을 비롯한 모든 명령어를 실행할 수 있음
프로세스(2023.05.05)
- 실행 위치에 따른 종류
- 포그라운드 프로세스, Foreground Process
- 사용자가 볼 수 있는 공간(화면상에서)에서 실행되는 프로세스.
- 백그라운드 프로세스, Background Process
- 데몬(daemon), 서비스(service)
- 사용자가 볼 수 없는 공간(화면뒤에서)에서 실행되는 프로세스 ➡️ 사용자와 상호작용 ❌
프로세스 제어 블록, PCB
- 운영체제의 커널영역에 적재.
- 프로세스들은 돌아가며, 한정된 시간만큼 CPU를 이용 ⬅️ 이 과정을 운영체제가 관리해줘야한다!
- 자신의 차례에 정해진 시간만큼 CPU 이용
- 타이머 인터럽트가 발생 ➡️ 차례 양보
- 운영체제는 프로세스들을 일목요연하게 관리를 해야한다. 이때, 사용되는 자료구조가 프로세스 제어 블록(Process Control Block)이다.
- PCB에는 프로세스 관련 정보가 저장됨
- 프로세스 ID (PID)
특정 프로세스를 식별하기 위한 고유 ID - 레지스터 값
문맥 교환(Context Switch)시 주로 사용됨.
자신의 실행 차례가 오면, 이전까지의 레지스터 값을 복원 후 실행하기 위해 저장 (ex, 프로그램 카운터, 스택 포인터 등이 저장됨) - 프로세스 상태
대기 상태, 실행 상태 등을 저장 - CPU 스케줄링 정보
- 메모리 정보
프로세스가 어느 주소에 저장되어 있는 지 저장
페이지 테이블 정보 저장 - 사용한 파일과 입출력장치 정보
할당된 입출력 장치, 사용 중인 파일 정보 저장
- 프로세스 ID (PID)
- 프로세스 생성 ➡️ 커널 영역에 생성.
종료 시 ➡️ 폐기.
문맥 교환 (Context Switch)
- 한 프로세스(A)에서 다른 프로세스(B)로 실행 순서가 넘어가면
- 실행 중이던 프로세스 A의 정보(= 문맥, Context)는 메모리에 저장
- 새로 실행할 프로세스 B의 정보(= 문맥, Context)는 메모리에서 불러옴
- Context = 각종 레지스터 값, 메모리 정보, 열었던 파일, 사용한 입출력장치 등
- 프로세스 A ➡️ (문맥교환) ➡️프로세스 B ➡️ (문맥교환) ➡️ 프로세스 C ➡️ (문맥교환) ➡️ 프로세스 D ➡️ ...
프로세스의 메모리 영역
- PCB는 커널 영역에 적재
- 프로세스는 사용자 영역에 적재
- 사용자 영역 = 스택 영역 + 힙 영역 + 데이터 영역 + 코드 영역 + ...
- 코드 영역 ****
- 실행 가능한 코드, 기계어 명령어 저장
- 데이터가 아닌, CPU가 실행할 명령어가 담긴 Read-Only 영역
- 데이터 영역
- Temp 데이터가 아닌, 실행되는 동안 계속 가지고 있어야할 데이터가 저장되는 영역 (ex, 전역 변수)
- 힙 영역
- 프로그래머가 직접 할당할 수 있는 동적(가변적) 영역
- 힙 영역에 할당 후 반드시 반환해아함. (가비지 컬렉션에서 대신 관리하기도 함) ➡️ 반환하지 않으면, 메모리 누수 발생
- 메모리 공간 상 아래(낮은 주소)에서 위(높은 주소)로 할당
- 스택 영역
- 데이터가 일시적으로 저장되는 동적(가변적) 공간 ➡️ 임시 Temp 데이터 저장 (ex, 매개 변수, 지역 변수)
- 메모리 공간 상 위(높은 주소)에서 아래(낮은 주소)로 할당
- 코드 영역 ****
프로세스 상태
- 생성 상태
- 프로세스가 생성되어 메모리에 적재되어 PCB를 할당 받은 상태
- 준비 되면, 준비 상태로 변경된다.
- 준비 상태
- 당장이라도 CPU를 할당 받아 실행할 수 있는 상태
- 자신의 차례를 기다리는 상태
- 자신의 차례가 오면 실행 상태로 변경(디스패치 : 준비 상태 ➡️ 실행 상태)
- 실행 상태
- CPU를 할당 받아 실행 중인 상태
- 할당된 시간을 모두 사용(타이머 인터럽트) ➡️ 준비 상태
- 실행 도중 입출력장치를 사용 ➡️ 입출력 작업이 끝날 때까지 대기 상태
- 대기 상태
- 프로세스 실행 도중 입출력장치를 사용할 경우 대기 상태로 변경
- 입출력 작업을 우선 완료 후 입출력 완료 후 인터럽트를 받으면, 준비 상태로 변경
- 종료 상태
- 프로세스가 종료된 상태
- 프로세스가 할당 받은 PCB는 폐기
- 프로세스가 점유하고 있던 메모리 영역을 정리
프로세스의 계층
프로세스를 실행 중 시스템 콜을 통해 다른 프로세스를 생성할 수 있다. 이렇게 프로세스 실행 도중 다른 프로세스를 생성하는 과정에서 트리 형태의 계층이 생긴다.
- 부모 프로세스 : 프로세스를 생성한 프로세스
- 자식 프로세스 : 부모 프로세스에 의해 생성된 프로세스
- 부모 프로세스의 PID != 자식 프로세스의 PID
- 운영체제에 따라, 자식 프로세스의 PCB에 부모 프로세스의 PID(PPID)를 명시하기도 한다. (PPID : Parent Process ID )
- 리눅스에서 "pstree" 명령어를 통해 관찰할 수 있다.
프로세스 생성 기법
- 부모 프로세스의 자식 프로세스 생성 방법 ( 복제 + 옷 갈아입기)
- Fork(복제) 와 Exec(옷 갈아입기) 시스템 콜을 호출해 자식 프로세스 생성
- 부모 프로세스 ➡️ [fork 시스템 콜] ➡️ 자신의 복사본을 자식 프로세스로 생성 (복제) (다른 PID를 가짐) (부모 프로세스의 자원 상속)
- 자식 프로세스 ➡️ [exec 시스템 콜] ➡️ 자신의 메모리 공간을 다른 프로세스로 교체 (옷 갈아입기)
- 자식 프로세스가 자신만의 코드 실행하는법
쓰레드 thread
스레드 : 프로세스를 구성하는 실행 흐름의 단위
- 하나의 프로세스가 여러 개의 쓰레드를 가질 수 있음 (멀티 스레드 프로세스)
- ex) 웹 브라우저 ➡️ 화면 출력 스레드 + 입력 스레드 + 검색 스레드 등 ...
- 프로세스가 가진 여러 개의 스레드가 동시 동작
- 스레드의 구성 요소 = 스레드 ID, 스레드 자신의 프로그램 카운터, 레지스터 값, 스택 등 실행에 필요한 최소한의 정보
- 프로세스 내 스레드들은 프로세스의 자원(힙 영역, 데이터 영역, 코드 영역 등)을 공유
멀티 프로세스 ↔️ 멀티 스레드
멀티 프로세스 : 동일한 작업을 수행하는 단일 스레드 프로세스를 여 러 개 실행
멀티 스레드 : 하나의 프로세스를 여러 개의 스레드로 구성하여 실행
- 차이점
- 자원 공유 유무
- 멀티 프로세스 : 각 단일 스레드 프로세스들 간에 자원 공유 ❌ (IPC를 통해 자원 공유를 할 수는 있지만, 멀티 스레드에 비해 불리하다.)
- IPC (Inter-Process Communication) : 프로세스들 간에 데이터를 주고 받는 통신.
- 멀티 스레드 : 프로세스 내 스레드들 간에 자원 공유 ⭕
- 멀티 프로세스 : 각 단일 스레드 프로세스들 간에 자원 공유 ❌ (IPC를 통해 자원 공유를 할 수는 있지만, 멀티 스레드에 비해 불리하다.)
- PID와 PCB
- 멀티 프로세스 : 각 프로세스들이 고유한 PID와 PCB를 가짐
- 멀티 스레드 : 프로세스가 가진 PID와 PCB를 스레드들이 공유받아 사용
- 자원 공유 유무
- 특징
- 멀티 프로세스(자원 공유 ❌)
- 각 프로세스들이 독립적인 실행
- 하나의 프로세스에 문제가 생겨도 다른 프로세스들에는 문제가 없다.
- 멀티 스레드(자원 공유 ⭕)
- 각 스레드 간 협력과 통신에 유리
- 하나의 스레드에 문제가 발생 ➡️ 다른 스레드들에도 문제가 생길 수 있다. (자원을 공유하기 때문에)
- 멀티 프로세스(자원 공유 ❌)
CPU 스케줄링(2023.05.06)
CPU 스케줄링 : 운영체제가 프로세스들에게 효율적으로 CPU 자원을 배분 하는 것
프로세스의 우선순위
- 프로세스의 우선순위를 고려해, 프로세스를 할당.
- 프로세스별 PCB 내, 해당 프로세스의 우선순위를 기록해둠.
- 입출력 작업이 많은 프로세스 일수록 우선순위 ⬆️
- 입출력 작업이 적고, CPU 작업이 많은 프로세스 일수록 우선순위 ⬇️
스케줄링 큐
- 각 장치에 대한 스케줄링 큐(준비큐 + 대기큐)를 만들어서 관리
- 예시)
- (준비큐)CPU 이용 스케줄링 큐 : PCB 1, PCB 2 ...
- (대기큐)하드디스크 이용 스케줄링 큐 : PCB 5, PCB 6, ...
- (대기큐)프린터 이용 스케줄링 큐 : PCB 9, PCB 10, ...
- ...
- 준비 큐 : CPU를 이용할 PCB들의 스케줄링
- 대기 큐 : 입출력 장치를 이용할 PCB들의 스케줄링
- 같은 장치를 이용할 PCB들은 동일한 큐에서 관리 가능
선점형 스케줄링 Preemptive Scheduling
현재 CPU를 사용 중인 프로세스로부터 CPU 자원을 잠시 빼앗아 다른 프로세스(더 급한 프로세스)에게 할당
- 장점
- 어느 한 프로세스의 자원 독점을 방지
- 프로세스들에게 자원을 고르게 분배
- 단점
- 문맥 교환(Context Switching)의 빈도가 높아져, 부하⬆️
비선점형 스케줄링
현재 CPU를 사용 중인 프로세스의 작업이 다 끝날때까지 기다림
- 장점
- 선점형 스케줄링에 비해 문맥 교환 빈도⬇️, 부하 ⬇️
- 단점
- 모든 프로세스가 고르게 자원 이용 ❌
CPU 스케줄링 알고리즘
운영체제마다 CPU 스케줄링 알고리즘은 다를 수 있다.
FCFS(비선점) ➡️ SJF(비선점) ➡️ RR(FCFS + Time Slice) ➡️ SRT(SJF + RR) ➡️ MultiLevel Queue(우선순위 큐 발전) ➡️ MultiLevel Feedback Queue (MultiLevel Queue + Queue 간 이동)
1. 선입 선처리 스케줄링, FCFS
FCFS(First Come Fist Served) 스케줄링
- 준비 큐에 먼저 들어온 순서(CPU를 먼저 요청한 순서)로 처리하는 비선점 스케줄링
- 비선점 스케줄링 ➡️ 앞의 프로세스가 완료될 때까지 기다려야한다.
- 가장 간단한 방식의 스케줄링
- 장점
- 구현이 간단, 이해하기 쉬움
- 단점
- 호위 효과 발생(Convoy Effect) : 먼저 들어온 프로세스의 작업시간이 길어지면, 그 뒤에 있는 다른 프로세스들의 대기 시간이 길어짐. ➡️ 프로세스들의 평균 대기시간이 길어질 수 있음
2. 최단 작업 우선 스케줄링, SJF
SJF(Shortest Job First) 스케줄링
- 앞선 FCFS 스케줄링 방식에서 호위 효과를 방지하는 스케줄링 방법.
- 큐에 담긴 프로세스들 중 CPU 사용 시간이 가장 짧은 프로세스 먼저 처리
- 비선점형 스케줄링
- 기본적으로 비선점형 스케줄링으로 분류되지만, 선점형 스케줄링으로도 구현이 가능하다!
- 우선순위 스케줄링의 한 종류
- 장점
- 프로세스들의 평균 대기시간이 짧다.
- 작업 완료 시간이 가장 짧은 작업을 먼저 실행 ➡️ 응답 시간이 빨라짐
- 단점
- 기아 현상(Starvation) : 작업 시간이 긴 프로세스는 무한정 기다리게 될 수 있다.
- 프로세스 별, 작업 실행 시간을 예측하기 어렵다.
3. 라운드 로빈 스케줄링, RR
RR(Round Robin) 스케줄링
- 선입 선처리 스케줄링(FCFS) + 타임 슬라이스
- FCFS 방식처럼 먼저 들어온 순서로 CPU를 사용. 대신, 정해진 시간(타임 슬라이스)만큼 사용 후, 다음 프로세스에게 양보
- 만약, 정해진 타임 슬라이스만큼 작업 후에도 완료하지 못했으면, 큐의 맨 뒤로 삽입
- 타임 슬라이스 : 각 프로세스가 CPU를 사용할 수 있는 시간 단위
- 장점
- 모든 프로세스가 동등한 Time Slice ➡️ 공평한 스케쥴링 ➡️ 기아 현상(Starvation) ❌
- 단점
- Time Slice의 크기가 작으면, 잦은 문맥 교환(Context Switch) ➡️ 부하가 높아짐.
- Time Slice의 크기가 크면, 프로세스 별 대기 시간이 증가
4. 최소 잔여 시간 우선 스케줄링, SRT
SRT(Shortest Remaining Time) 스케줄링
- 최단 작업 우선 스케줄링(SJF) + 라운드 로빈 스케줄링(RR)
- 정해진 타임 슬라이스만큼 CPU를 이용하되, 그 다음 프로세스는 남은 작업 시간이 가장 짧은 프로세스를 선택!
- 장점
- 프로세스들의 평균 대기 시간과 응답 시간이 짧음 (SJF의 특징)
- 단점
- 작업 완료 시간의 예측이 어려움 (SJF)
- 기아 현상(Starvation) (SJF)
- 문맥 교환(Context Switch) 비용 증가 (RR)
5. 우선순위 스케줄링
Priority-Based 스케줄링
- 프로세스들에 우선순위를 부여하고, 우선순위가 높은 프로세스부터 실행
- 만약 우선순위가 같다? ➡️ 먼저 들어온 프로세스부터 처리(FCFS)
- SJF와 SRT는 우선순위 스케줄링이다.
- 최단 작업 우선 스케줄링(SJF) & 최소 잔여 시간 스케줄링(Shortest Remaining Time) ➡️ (남은 )작업 시간을 기준으로 우선순위를 부여하는 우선순위 스케줄링
- 문제점
- 기아 현상(Starvation) 발생
- 기아 현상(Starvation) : 우선순위가 높은 프로세스들만 계속 실행 ➡️ 우선 순위가 낮은 프로세스들은 무기한 연기될 수 있다.
- 기아 현상(Starvation) 발생
- 기아 현상 방지 방법
- 에이징(Aging) 기법
- 오랫동안 실행되지 못한 프로세스들에 나이를 부여해, 우선순위를 높임.
- 에이징(Aging) 기법
6. 다단계 큐 스케줄링
MultiLevel Queue 스케줄링
- 우선순위 스케줄링의 발전된 형태
- 우선순위 스케줄링 + 여러 개의 준비 큐
- 우선순위 별로 준비 큐를 사용하는 스케줄링 방식
- 우선순위가 가장 높은 큐에 있는 프로세스를 먼저 처리
- 만약, 상위 우선순위 큐가 비어 있으면, 하위 우선순위 큐를 처리
- 다수의 큐 사용 ➡️ 각 우선순위 큐 별로, 타임 슬라이스를 조정하거나, 큐의 특징(입출력 집중 프로세스들이 담긴 큐 | CPU 집중 프로세스들이 담긴 큐)에 맞게 스케줄링 기법(FCFS, RR 등)을 다르게 설정할 수 있다.
- 문제점
- 프로세스들이 큐 간에 이동이 불가하다.
- 낮은 우선순위 큐에 들어 있는 프로세스들은 기아 현상(Starvation)이 발생 가능
- 프로세스들이 큐 간에 이동이 불가하다.
- 장점
- 각 큐 별로 그룹화하여, 다양한 스케줄링 기법 적용 가능
- 큐의 우선순위에 따라 작업을 구분 ➡️ 시스템 응답 시간 + 처리량 최적화 용이
- 단점
- 그룹 간 상호작용이 적절하게 이루어지지 않으면, 효율성이 ⬇️
- 작업을 그룹화하고, 우선순위를 관리하기 위한 추가적인 자원 소모가 발생
7. 다단계 피드백 큐 스케줄링
MultiLevel Feedback Queue 스케줄링
- 다단계 큐 스케줄링의 발전된 형태
- 다단계 큐 스케줄링 + 큐 간의 이동
- 들어온 프로세스들은 우선순위가 가장 높은 큐에 삽입됨.
- 만약, 일정 타임 슬라이스만큼 CPU 작업 후에도 완료되지 못하면, 우선순위가 한 단계 낮은 큐로 삽입
- ➡️ CPU를 많이 사용하는 CPU 집중 프로세스들의 우선순위는 자연스레 낮아지고, 입출력 작업이 많은 프로세스들의 우선순위는 자연스레 높아진다.
- CPU 집중 프로세스 ➡️ 낮은 우선순위 큐로 이동 ➡️ 우선순위 점차 낮아지도록
- 에이징 기법 ➡️ 높은 우선순위 큐로 이동 ➡️ 우선순위가 점차 높아지도록
동기화
동기화가 필요한 이유
프로세스와 스레드는 동시다발적으로 실행됨 ➡️ 서로 협력하고, 영향을 주고 받음 ➡️ 자원의 일관성을 보장해야 한다(동기화를 통해).
동기화란?
프로세스들의 수행 시기를 (실행 순서 제어와 상호 배제를 통해) 맞추는 것
- 실행 순서 제어 : 프로세스들을 올바른 순서대로 실행
- 상호 배제 : 동시에 접근하면 안 되는 자원은 하나의 프로세스만 접근하게 제어
실행 순서 제어 동기화
실행 순서를 알맞게 조정하는 동기화.
1. Reader Writer Problem
- Reader Process: book.txt 파일에 저장된 값을 읽으려는 프로세스
- Writer Process: book.txt 파일에 값을 쓰려는(저장하려는) 프로세스
- 리더 프로세스와 라이터 프로세스는 순서에 맞게 실행되어야 한다!
- 리더 프로세스는 book.txt 파일 안에 값이 있어야 읽을 수 있다.
- 위의 상황 말고도, 프로세스들의 실행 순서를 조정해줘야 하는 다양한 상황이 있다.
상호 배제를 위한 동기화
공유 불가능한 자원의 동시 사용을 피하기(상호 배제) 위한 동기화.
상호 배제 동기화를 하지 않을 경우 생기는 상황들
1. Bank Account Problem
- 현재 계좌 잔액 : 10만원
- 프로세스 A는 "1)잔액을 읽고, 2) 2만원 추가하고, 3) 저장하는" 프로세스
- 프로세스 B는 "1)잔액을 읽고, 2) 5만원을 추가하고, 3) 저장하는" 프로세스
- 만약, A와 B 동시에 실행될 경우,
- A가 잔액을 읽어드림 ( A 입장에서 현재 잔액 10만원)
- A가 2만원 추가 (A입장에서 현재 잔액 12만원)
- 문맥교환 (Context Switch)
- B가 잔액 읽음(B입장에서 현재 잔액 10만원)
- B가 5만원 추가(B입장에서 현재 잔액 15만원)
- 문맥교환 (Context Switch)
- A가 잔액 저장 (현재 잔액 : 12만원)
- 문맥교환 (Context Switch)
- B가 잔액 저장 (현재 잔액 : 15만원)
- 최종 잔액 : 15만원
2. Producer & Consumer Problem
- 생산자(Producer) : 물건을 계속 생산하는 생산자(프로세스 or 스레드)
- 생산자 함수의 실행 순서
- 버퍼에 데이터 삽입
- 변수 "Amount"을 1 증가
- 생산자 함수의 실행 순서
- 소비자(Consumer) : 물건을 계속 소비하는 소비자(프로세스 or 스레드)
- 소비자 함수의 실행 순서
- 버퍼에서 데이터 꺼냄
- 변수 "Amount"을 1 감소
- 소비자 함수의 실행 순서
- 0으로 초기화된 변수 "Amount"을 공유
- 위의 상황에서 생산자 함수(변수 "Amount" 1 증가)와 소비자 함수(변수 "Amount" 1 감소)를 각각 10번, 100번, 1000번 등 같은 수만큼 실행시킬 경우, 변수 "Amount"는 초기의 0으로 유지 될까? ❌
- 변수 "Amount"의 값은 그때 그때 다르다.
- 총합 "변수를 동시에 접근해서 생김"
- 변수 "Amount"의 값은 그때 그때 다르다.
공유 자원과 임계 구역
위의 Bank Account Problem과 Producer & Consumer Problem은 상호 배제해야할(공유하면 안되는) 자원을 공유했기 때문에 발생한 것이다.
- 공유 자원 : 여러 프로세스/스레드 가 공유하는 자원
- ex) 전역 변수, 파일, 입출력장치, 보조기억장치, ...
- 임계 구역 : 동시에 실행하면 문제가 발생하는 자원에 접근하는 코드 영역 (위의 "Amount" 변수, "잔액" 변수)
- 여러 프로세스/스레드가 임계 구역에 동시에 접근 ➡️ 자원의 일관성이 깨지는데 이를 레이스 컨디션(경쟁조건)(Race Condition)이라고 한다.
상호 배제를 위한 동기화 3원칙 (2023.05.07)
- 상호 배제 (Mutual Exclusion) : 한 프로세스가 임계 구역에 진입했다면, 다른 프로세스들은 진입하지 못하게...
- 진행 (Progress) : 임계 구역에 진입한 프로세스가 없다면, 진입할 수 있게 ...
- 유한 대기 (Bounded Waiting) : 한 프로세스가 임계 구역에 진입하고자 한다면, 언젠가는 진입할 수 있도록...
동기화 기법들
- 뮤텍스 락
- 세마포
- 모니터
뮤텍스 락
프로세스들 간 동기화를 이루기 위한 2가지 방법들(1. 실행 순서 제어, 2. 상호 배제) 중 상호 배제를 위한 동기화 도구
임계 구역에 대한 자물쇠 역할을 한다.
전역 변수 1개와 함수 2개를 이용해 간단히 구현할 수 있다.
- Lock (전역 변수) : 프로세스들이 공유하는 자물쇠 역할의 전역 변수 (Boolean)
- Acquire (함수) : 임계 구역을 잠그는 함수
- Release (함수) : 임계 구역의 잠금을 해제하는 함수
Bool LOCK ; // 자물쇠 역할, True: 잠김, False: 열림
acquire() { //임계 구역을 잠그는 함수(진입하는 함수)
//Busy Waiting 발생(계속 while을 돌면서 자물쇠를 확인해야한다.)
while(lock == true) ;
lock = true;
}
release() { //자물쇠 해제
lock = false;
}
...
acquire()
// 임계 구역 코드
release()
- 단점
- busy waiting(바쁜 대기) 발생.
- While 문을 돌면서, 자물쇠가 열려있는지 계속 확인해야 한다.
- busy waiting(바쁜 대기) 발생.
세마포
- 뮤텍스 락보다 좀 더 일반화된(보편적인) 방식의 동기화 도구
- 공유 자원이 여러 개 있는 경우에도 적용 가능
- 전역 변수 1 개 + 함수 2 개로 간단히 구현 가능 (카운팅 세마포)
- S (전역 변수) : 임계 구역에 진입 가능한 프로세스(스레드)의 개수
- wait (함수) : 임계 구역에 진입 가능한지 알려주는 함수
- signal(함수) : 임계 구역 진입 전, 기다리게 하고, 들어가도 된다는 신호를 주는 함수
- 종류
- 이진 세마포
- 뮤텍스 락과 유사.
- 0(잠금)과 1(열림)의 이진 데이터를 이용해 상호배제를 해결하는 세마포 기법
- 이진 세마포
- 카운팅 세마포
- Busy Waiting 발생 ❌
- While문 ➡️ If문 : 한번만 확인하면 된다. (CPU 자원 절약)
- 세마포는 상호 배제말고, 실행 순서 제어도 가능하다
- 세마포 카운팅 변수 S를 0으로 초기화하고,
- 먼저 실행해야할 프로세스(스레드) 뒤에 signal 함수를 실행하고,
- 그 다음에 실행해야할 프로세스(스레드) 앞에 wait() 함수를 실행 시켜주면 된다.
- Busy Waiting 발생 ❌
// 임계구역에 진입가능한 프로세스(스레드) 수 int S = 3; // 임계구역 진입함수 wait() { s--; //진입 가능한지 체크 if(S<0) { 프로세스를 대기 큐에 삽입 대기 상태로 전환 } } signal() { s++; if(S <=0){ 대기 큐에서 프로세스 pop popped 프로세스를 대기상태 ➡️ 준비상태 전환 } } wait() // 임계구역 코드 signal()
모니터
개발자 친화적인 편리한 동기화 도구
세마포를 사용할 경우, 일어날 수 있는 실수들 ➡️ 디버깅하기도 어렵다.
- 세마포 누락
//wait 누락 임계구역 //signal 누락
- wait, signal 순서 헷갈림
signal() 임계 구역 wait()
- wait, signal 중복 사용
wait() 임계 구역 wait()
디버깅 하기 어려운 이유
1. 높은 복잡도
- 여러 프로세스/스레드 간 상호작용을 조율하기 때문에, 상태 전이(대기, 실행 중지 등)와 실행 흐름을 추적하기 어려움
2. 동시성
- 세마포 기법이 실행되는 동안 발생할 수 있는 레이스 컨디션(race condition)은 디버깅 하기 어려움
3. 비결정적 특성
- 실행 순서/타이밍이 실행 환경에 따라 달라질 수 있음 ➡️ **똑같은 환경이 아니면, 버그를 발견하지 못할 수 있음**.
이러한 발생 가능할 실수들을 보완하고자 "모니터 기법" 등장
- 기본 개념
- 모니터에는 항상 1개의 프로세스/스레드가 들어올 수 있음
- 상호 배제 동기화
- 공유 자원에 접근하는 통로(큐)를 제공
- 공유 자원에 접근하고자 하는 프로세스들은 큐에 삽입
- 큐에 삽입된 순서대로 인터페이스를 통해 공유 자원 이용
- 실행 순서 제어 동기화
- 조건 변수(Condition Variable) 이용 (조건 변수에 대한 wait 큐가 존재)
- 예시) 큐 안에 프로세스 A와 B가 있고, 반드시 A가 먼저 실행되어야 하는데, 만약 큐에서 B가 먼저 나올 경우,
- 프로세스 B가 큐에서 pop
- A가 먼저 실행되어야 한다는 조건 확인.
- B를 대기 큐에 삽입
- 프로세스 A를 pop후 실행
- 프로세스 A가 signal() 실행
- 프로세스 B가 대기 큐 탈출 후 실행 (이 과정에 또 2가지 방법이 있다.)
- case 1) 프로세스 A가 Signal() 실행 후, 모니터를 탈출한 다음에 B가 실행
- case 2) 프로세스 A가 signal() 실행 후, 잠시 중단 후, 중단 되는 동안 프로세스 B가 실행. B가 완료되면, 다시 A를 실행.
교착 상태 Dead Lock
프로세스/스레드가 서로 자원을 점유하여, 결과적으로 각 프로세스/스레드가 다른 프로세스/스레드가 자원을 점유하고 있어 기다리고 있는 상태
교착 상태가 발생하는 조건
아래 4가지 조건을 모두 회피 ➡️ 교착 상태 ❌
- 상호 배제
- 한 프로세스가 사용하는 자원을 다른 프로세스가 사용하지 못하게 막는 것
- 점유와 대기
- 자원을 할당 받은 상태에서 다른 자원을 할당받기 위해 기다리고 있는 것
- 비선점
- 어떤 프로세스도 다른 프로세스의 자원을 강제로 뺏지 못하는 상태
- 원형 대기
- 프로세스들이 원의 형태(순환)로 자원을 대기하는 상태
교착 상태 해결
예방
처음부터 교착 상태가 일어나지 않도록 교착 상태 발생 조건(상호 배제, 점유와 대기, 비선점, 원형 대기) 중 하나를 없애버리는 법
- 상호 배제 없애기
- 모든 자원을 공유 ➡️ 현실적으로 불가능
- 점유와 대기 없애기
- 특정 프로세스에게 자원을 모두 할당 | 아예 할당 ❌
- 자원의 활용이 매우 낮아짐
- 특정 프로세스에게 자원을 모두 할당 | 아예 할당 ❌
- 비선점 조건 없애기
- 선점이 가능한 자원(CPU)에 한해 효과적이지만, 모든 자원이 선점 가능한 것이 아님.
- 원형 대기 조건
- 모든 자원에 대해 번호를 할당 후 오름차순으로 자원을 할당
- 모든 자원에 대해 고유한 번호를 할당하는 것은 현실적으로 어렵다.
- 각 자원의 번호에 따라 활용률이 달라진다.
회피
기본 전제: 교착 상태 = 무분별한 자원 할당으로 인해 발생
➡️ 교착 상태가발생하지 않도록 조심 조심 자원을 할당
(배분할 수 있는 자원의 양을 고려하여 자원을 배분)
- 안전 순서열 : 교착 상태 없이 안전하게 프로세스들에게 자원을 할당할 수 있는 순서
- 안전 상태 (안전 순서열이 있는 상태): 교착 상태 없이 모든 프로세스가 자원을 할당 받고 종료할 수 있는 상태
- 불안전 상태 (안전 순서열이 없는 상태): 교착 상태가 발생할 수 있는 상태
- 항상 안전 상태 ➡️ 안전 상태의 상황에만 자원을 할당
- ex) 은행원 알고리즘
검출 후 회복
- 자원을 일당 할당 ➡️ 교착 상태 발생하면 회복
- 선점을 통한 회복
- 교착 상태가 해결될 때까지 한 프로세스에게 자원을 몰아줌
- 강제 종료 회복
- 교착 상태 프로세스들을 강제 종료 ➡️ 작업 내용 잃을 수 있음
- 교착 상태가 해결될 때까지 한 프로세스씩 강제 종료 ➡️ 과부하
- 선점을 통한 회복
운영체제의 메모리 관리(2023.05.14)
연속 메모리 할당
프로세스에 연속적인 메모리 공간 할당
연속 메모리 할당 방식 3 종류
- 최초 적합(first-fit)
- 운영체제가 메모리 내 빈 공간을 순회하다 적재할 수 있는 공간 발견하면, 거기에 프로세스를 적재
- 장점
- 최소 검색
- 빠른 할당
- 최적 적합
- 메모리 내 빈 공간들 중 적재 가능한 가장 작은 공간에 적재
- 최악 적합
- 메모리 내 빈 공간들 중 적재 가능한 가장 큰 공간에 적재
- 연속 메모리 할당은 비효율적 ➡️ 잘 사용하지 않음
- 외부 단편화(External Fragmentation) 문제 발생
외부 단편화
연속 메모리 할당 방식에서 프로세스들은 메모리에 적재, 실행, 종료 과정을 반복하면서, 메모리 사이사이에 작은 빈 공간들이 생긴다.
이 작은 빈 공간들이 작아, 프로세스를 할당할 수 없어 낭비 되는 문제가 발생한다.
- 외부 단편화 해결법
- 메모리 압축(Compaction)
- 프로세스들을 재배치해, 흩어져 있는 빈 공간들을 하나로 모은다.
- 단점
- 메모리에 이미 적재된 프로세스들을 재배치 해야함 ➡️ overhead
- 가상 메모리 기법(페이징, 세그멘테이션) (가장 많이 사용됨)
- 메모리 압축(Compaction)
가상 메모리 기법 (강의 영상 4시간 40분부터 시청하는 걸 추천)
실행할 프로세스의 일부만 메모리에 적재해 실행하는 기술
- 이점
- 실제 물리 메모리보다 더 큰 프로세스를 실행할 수 있다.
- 종류
- 페이징, 세그멘테이션
- 페이징(paging)
- 페이지(page) : 프로세스의 논리 주소 공간의 일정 단위
- 프로세스 = 페이지 1 + 페이지 2 + ... + 페이지N
- 프레임(frame) : 페이지와 동일한 크기로 자른 메모리의 물리 주소 공간의 일정 단위
- 메모리 = 프레임 1 + 프레임 2 + ... + 프레임N
- 페이지들을 프레임에 할당하는 가상 메모리 기법
- 페이징의 스와핑
- 페이지 인 : 프로세스 단위가 아닌, 페이지 단위의 스왑 인
(필요한 페이지를 메모리에 적재) - 페이지 아웃 : 프로세스 단위가 아닌, 페이지 단위의 스왑 아웃
(당장 필요없는 페이지는 보조기억장치의 스왑 영역으로...) - 보조기억장치의 스왑 영역을 활용
- 페이지 인 : 프로세스 단위가 아닌, 페이지 단위의 스왑 인
- 페이지 테이블
- 페이징 기법을 사용할 경우, 프로세스는 페이지 단위로 쪼개어져, 메모리에 불연속적으로 적재됨 ➡️ CPU는 프로세스의 각 페이지들의 위치를 알아야 한다. ➡️ 페이지 테이블
- 페이지 번호 - 프레임 번호 - 유효 비트 - 보호 비트 - 참조 비트 - 수정 비트를 기록하는 공간
- 페이지 번호(페이지 번호+변위)-프레임 번호(프레임 번호+변위)
- 이때, 매핑되어있는 페이지 번호와 프레임 번호의 변위들은 동일한 크기
- 유효 비트
- 페이지가 메모리에 적재 ⭕ ➡️ 1
- 페이지가 메모리에 적재(스왑 영역에 적재된 상태)❌ ➡️ 0
- 유효 비트가 0 ➡️ 후술할 페이지 폴트(page fault) 인터럽트 발생
- 보호 비트
- R/W/X 권한을 기록해 페이지를 보호하는 비트
- 참조 비트
- CPU가 해당 페이지에 접근한 적이 있는지 기록
- 수정 비트(dirty bit)
- CPU가 해당 페이지에 데이터를 수정한 적이 있는지 기록
- 페이지 아웃(스왑 아웃)을 할때, 보조기억장치에도 수정을 해줘야하는지 알기 위해...
- 내부 단편화 발생
- 프로세스의 크기가 페이지 크기의 배수가 아닐 경우, 낭비 되는 단편 공간이 발생
- 예시)
- 페이지 크기 = 10
- 프로세스 크기 = 108
- 10 크기의 페이지 11개 = 110
- 2만큼의 단편 공간이 낭비됨
- 예시)
- 프로세스의 크기가 페이지 크기의 배수가 아닐 경우, 낭비 되는 단편 공간이 발생
- 각 프로세스마다 페이지 테이블을 가지고 있고, 메모리에 적재된다..
- 각 프로세스의 페이지 테이블 메모리 주소는 CPU내 PTBR(Page Table Base Register)이 가르킨다.
- 페이지 테이블이 메모리에 있을 때 단점
- 페이지 테이블 참조 + 페이지 참조 ➡️ 메모리 접근 시간이 2배로 된다. ➡️ TLB 등장
- TLB
- CPU 옆에 위치한 페이지 테이블용 캐시 메모리
- 페이지 테이블의 일부를 가져와 저장
- TLB = 캐시 메모리 ➡️ TLB Hit / TLB Miss
- TLB Hit
- CPU가 접근하려는 논리 주소가 TLB에 존재
- TLB Miss
- CPU가 접근하려는 논리 주소가 TLB에 없음
- TLB Hit
- 페이지(page) : 프로세스의 논리 주소 공간의 일정 단위
페이징 기법이 이점
- 쓰기 시 복사 (Copy On Write)
- 부모 프로세스와 동일한 자식 프로세스가 복제되어 생성되면, 자식 프로세스는 부모 프로세스와 동일한 프레임을 가르킴
- 부모/자식 프로세스 둘 중 하나가 페이지에 쓰기 작업 수행 시, 해당 페이지는 별도의 공간으로 복제
계측정 페이징 (다단계 페이징 )
페이지 테이블을 페이징해 여러 단의 페이지를 두는 방식 ➡️ 페이지 테이블을 쪼개서 관리
스와핑
현재 사용되지 않는 프로세스를 보조기억장치의 **스왑 영역**으로 쫒아내고(스왑 아웃), 빈 공간에 새 프로세스를 적재(스왑 인)
- 스와핑을 사용하는 이유
- 프로세스들의 요구 메모리 공간보다 실제 메모리 공간이 적은 경우에도 프로세스를 실행할 수 있다.
- ex)
- 실제 메모리 공간 = 10
- 프로세스 1의 필요 메모리 공간 = 5
- 프로세스 2의 필요 메모리 공간 = 3
- 프로세스 3의 필요 메모리 공간 = 4
- 1과 2를 메모리에 적재 후, 3을 실행시켜야할 때, 1과 2 중 하나를 스왑 아웃하고 3을 스왑 인.
요구 페이징 (2023.05.17)
처음부터 모든 페이지를 메모리에 적재하지 않고, 필요한 페이지만(요구되는) 적재하는 기법
- 요구 페이징 과정
- CPU가 특정 페이지에 접근하는 명령어를 실행
- 해당 페이지가 현재 메모리에 있냐?
- 있다(유효 비트가 1이다) ➡️ CPU는 페이지가 적재된 프레임이 접근
- 없다(유효 비트가 0이다) ➡️ 페이지 폴트 발생
- 페이지 폴트 처리 루틴 : 해당 페이지를 메모리로 적재, 유효 비트는 1로
- 1번으로 돌아간다.
- 요구 페이징 시스템이 안정적으로 동작하려면 아래 2가지를 고민해야 한다.
- 페이지 교체
- 프레임 할당
페이지 교체 알고리즘
요구 페이징 기업으로 페이지들을 적재하다보면, 언젠가 메모리는 가득 찬다. 따라서, **당장 실행에 필요한 페이지를 적재하기 위해서는 메모리에 적재된 페이지를 보조기억장치로 보내야한다**.
기본 전제
- **페이지 폴트를 적게 발생시키는 페이지 교체 알고리즘이 좋다**.
- 페이지 폴트가 발생하면, 보조기억장치에 접근해서, 해당 페이지를 다시 가져와야 하기 때문에 성능 저하가 발생
- 페이지 참조열(page reference string)을 통해 페이지 폴트 횟수를 파악할 수 있다.
- 페이지 참조열 : CPU가 참조하는 페이지들 중 연속된 페이지를 생략한 페이지열
- FIFO 페이지 교체 알고리즘
- 가장 단순
- 메모리에 가장 먼저 올라온 페이지부터 내보낸다. (가장 오래된 페이지부터 내보낸다.)
- 한계점(단점)
- 프로그램이 실행되는 동안 **계속 필요한 페이지도 차례가 오면 내보낸다**.
- **2차 기회 페이지 교체 알고리즘** ➡️ 단점을 보완한 알고리즘
- 참조 비트를 추가
- 페이지를 내보낼 때
- 참조 비트 == 1 : 최근에 참조한 적이 있는 페이지 ➡️ 해당 페이지는 내보내지 않고, 적재된 타이밍을 현재로 초기화
- 참조 비트 == 0 : 참조한 적이 없는 페이지 ➡️ 내보낸다.
- 페이지를 내보낼 때
- 참조 비트를 추가
- 최적 페이지 교체 알고리즘
- CPU에 의해 참조되는 횟수를 고려
- 왜? 메모리에 오래 남아야 하는 페이지는 자주 사용하는 페이지여야 하니까
- 보통, 페이지 교체 알고리즘 **성능을 평가할 때 사용**.
- 단점
- 구현이 어렵다 ➡️ 앞으로 오랫동안 사용되지 않을 페이지를 미리 예측하는 건 어렵다. ➡️ 보통 평가 도구로써 사용
- CPU에 의해 참조되는 횟수를 고려
- LRU(Least-Recently-Used) 페이지 교체 알고리즘
- 현재, 가장 오래 사용되지 않은 페이지를 내보낸다.
- 최적 페이지 교체 알고리즘의 문제 = 자주 사용되지 않을 페이지(미래)를 예측하는 것은 불가능
- LRU : 그렇다면, 가장 오래 사용되지 않은 페이지(과거)를 교체하자!
스래싱과 프레임 할당
페이지 폴트는 왜 자주 발생할까 ?
- 나쁜 페이지 교체 알고리즘 사용
- 프로세스가 사용할 수 있는 프레임 자체가 적다.
- 스래싱
- 페이징에 소요되는 시간 > 프로세스 실행에 소요되는 시간 ➡️ 성능 저하되는 상황을 스래싱이라 한다.
- 스래싱이 발생하는 이유
- 각 프로세스가 필요한 최소한의 프레임을 보장받지 못해서 ➡️ 각 프로세스의 최소한의 프레임 수를 파악 후, 보장해줘서 해결할 수 있다.
- 프레임 할당 기법
- 균등 할당(Equal Allocation)
- 가장 단순
- 모든 프로세스들에게 균등하게 프레임 할당.
- 예) 만약, 현재 프레임이 300개이고, 프로세스 수는 3개 ➡️ 각 프로세스에게 100 프레임씩 할당
- 문제점
- 각 프로세스마다 필요한 최소한으로 적합한 프레임 수가 다른데, 이를 고려하지 않음(스래싱을 고려하지 않는 방식이다.)
- 비례 할당(Proportional Allocation)
- 정적 할당 방식
- 프로세스의 크기를 고려해 할당(스레싱 문제를 고려)
- 할당되는 프레임의 수는 프로세스의 크기에 비례
- 문제점
- 각 프로세스가 필요로하는 프레임의 수는 동적으로 변할 수 있다. 즉, 실행해 봐야 필요한 프레임 수를 알 수 있다.
- 작업 집한 모델 (Working Set Model)
- 동적 할당 방식 (비례 할당 방식의 정적 방식을 극복하기 위해)
- 프로세스가 실행하는 과정에서 배분할 프레임을 결정
- 스레싱 문제를 고려
- CPU가 **특정 시간 동안 주로 참조한 페이지 개수(작업 집합의 크기)**만큼 프레임을 할당
- 페이지 폴트 빈도 기법
- 동적 할당 방식
- 기본 전제
- 페이지 폴트율이 너무 높다 ➡️ 그 프로세스는 너무 적은 프레을 가진 것
- 페이지 폴트율이 너무 낮다 ➡️ 그 프로세스는 너무 많은 프레임을 가진 것
- 페이지 폴트율에 상한선과 하한선을 정하고, 그 내부 범위에서만 프레임을 할당하는 방식
- 균등 할당(Equal Allocation)
파일 & 디렉토리
파일 시스템
- 파일-디렉토리를 관리하는 운영체제 내 프로그램
- 파일&디렉토리에 대한 연산(접근)은 시스템 콜을 통해서 가능
파일
보조기억장치에 저장된 데이터의 집합
- 파일 = 파일을 실행하기 위한 데이터 + 부가 정보(속성, 메타 데이터)
디렉토리
- 트리 구조 디렉토리 : 여러 계층으로 파일과 폴더를 관리
- 특별한 형태의 **파일**
- 최상위 디렉토리 = 루트 디렉토리(/)
- 절대 경로 : 루트 디렉토리부터 표기하는 방식
- 상대 경로 : 현재 디렉토리부터 표기하는 방식
- 디렉토리 엔트리 테이블 : 각 엔트리(행)에 담기는 정보
- 디렉토리에 속한 대상의 이름과 위치가 매핑된 정보를 테이블로 저장
파일 시스템
파티셔닝 & 포매팅
저장 장치들은 파티셔닝과 포매팅을 해야 사용할 수 있다.
- 파티셔닝 : 저장 장치의 논리적인 영역을 구획(C드라이브, D드라이브 ...)
- 포매팅 : 파일 시스템을 설정하는 작업.
- 각 파티션 별로, 다르게 포매팅하는 것도 가능
- 각 파일과 디렉토리는 **블록 단위**로 관리됨.
파일 할당 방법
- 연속 할당
- 보조기억장치 내 연속적인 블록에 파일 할당 ➡️ 디렉토리 엔트리에 파일 이름 + **첫 번째 블록 주소 + 블록 단위 길이**(연속적으로 저장되니까)를 명시
- 위의 연속 메모리 할당 방식에서와 같은 **외부 단편화 문제**를 야기
- **불연속 할당** (현대에 **가장 보편적**인 방식)
- 연결 할당
- 링크드 리스트로 관리 ➡️ 각 블록의 일부에 다음 블록의 주소를 저장
- 디렉토리 엔토리에 저장되는 엔트리(행)
- 파일 이름 - 첫 번째 블록 주소 - 블록 단위 길이
- 다음 블록의 위치는 엔트리가 아닌, 블록 내에 저장됨
- 단점 (= 링크드 리스트의 문제점들)
- 링크드 리스트 ➡️ 반드시, 첫 번째 블록부터 읽기 시작해야한다.
- 특정 블록에서 오류 발생 ➡️ 그 뒤의 블록들은 찾기 어렵다.(오류가 난 블록에 다음 블록의 정보가 있으니까...)
- 색인 할당
- 특정 블록을 **색인 블록**으로 사용.
- 파일의 모든 블록 주소를 색인 블록에 저장.
- 즉, 파일의 모든 블록들은 색인 블록에 의해 묶인다.
- 디렉토리 엔트리에 저장되는 엔트리(행)
- 파일 이름 - 색인 블록 주소 (색인 블록만 알면, 파일을 이루는 나머지 블록들의 주소를 알 수 있으니까...)
- 연결 할당
FAT 파일 시스템
- File Allocation Table
- FAT 12, 16, 32, ... : FAT+N에서 N의 의미는 각 블록 비트 크기.
- 불연속 - 연결 할당 기반의 파일 시스템
- 연결 할당의 단점을 보완
- 연결 할당의 문제점들은 기초적인 링크드 리스트 형태라서 발생
- 각 블록의 다음 블록의 주소 정보를 해당 블록에 저장 ❌
- 각 블록의 다음 **블록 주소 정보를 모아서 테이블(FAT 영역)**에 저장
- **FAT 파일 시스템으로 포매팅할 경우, 4개의 영역으로 나누어져 관리됨.**
- **FAT 파일 시스템 = 예약 영역 + FAT 영역 + 루트 디렉토리 영역 + 데이터 영역**
- 디렉토리 엔트리에 파일의 속성까지도 저장됨
유닉스 파일 시스템
- 불연속 - 색인 할당 기반의 파일 시스템
- 색인 블록 = "i-node"
- i-node
- 파일의 속성 정보(메타 데이터)와 15개의 블록 주소를 저장할 수 있다.
- 만약, 15개보다 많은 30개의 블록이 필요한 큰 데이터를 가진 파일은 어떻게 저장할까?
- 초기 12개의 블록 = 직접 블록 : 파일 데이터가 저장된 블록의 주소를 저장.
- 만약, 12개만으로 부족할 경우, 13번째 블록을 단일 간접 블록(데이터들을 저장한 블록 주소가 저장)으로 사용
- 이것도 부족하면, 14번째 블록을 이중 간접 블록(단일 간접 블록들의 주소를 저장) 주소 저장
- 이것도 부족?! ➡️ 삼중 간접 블록
- 초기 12개의 블록 = 직접 블록 : 파일 데이터가 저장된 블록의 주소를 저장.
- 만약, 15개보다 많은 30개의 블록이 필요한 큰 데이터를 가진 파일은 어떻게 저장할까?
- 파일의 속성 정보(메타 데이터)와 15개의 블록 주소를 저장할 수 있다.
- i-node
- 유닉스 파일 시스템은 3개의 영역으로 나누어 관리
- **Unix 파일 시스템 = 예약 영역 + i-node 영역 + 데이터 영역**
- 디렉토리 엔트리에 저장되는 엔트리
- i-node 번호 + 파일 이름 (i-node만 알면 다 알 수 있다! )
참고 강의
https://www.youtube.com/watch?v=isj4sZhoxjk&list=PLYH7OjNUOWLUz15j4Q9M6INxK5J3-59GC&index=4 (강추)
https://ko.wikipedia.org/
'CS' 카테고리의 다른 글
[JAVA] 객체 지향 설계의 5원칙 - SOLID 정리 (0) | 2024.03.10 |
---|---|
운영체제 - 프로세스와 스레드 (0) | 2024.03.10 |
자료 구조 - 트리 구조 정리 (0) | 2024.03.07 |
운영체제 - 기초 (0) | 2024.03.07 |
[JAVA] - 동시성 (0) | 2024.02.24 |