inflearn logo
강의

강의

N
챌린지

챌린지

멘토링

멘토링

N
클립

클립

로드맵

로드맵

지식공유

[인프런 워밍업 클럽 3기 CS전공지식] 3주차 발자국

영욱
1

1일차

가상 메모리 개요

 

 

가상 메모리란?

프로세스는 물리 메모리의 크기나 운영체제 영역이 어디에 위치하는지를 몰라도 된다. 프로세스는 메모리 관리자(MMU, Memory Management Unit)를 통해 메모리에 접근하며, 가상 메모리를 통해 자신만의 독립적인 주소 공간을 가진다.

가상 메모리의 크기는 이론적으로 무한하지만 실제로는 물리 메모리 크기CPU 비트 수에 따라 결정된다. 예를 들어, 32비트 CPU에서는 2의 32승(약 4GB) 크기의 가상 주소 공간을 제공할 수 있다.

만약 4GB 크기의 프로세스 5개가 실행되면 총 20GB가 필요한데, 이는 실제 물리 메모리보다 훨씬 크다.
이를 가능하게 하려면 물리 메모리의 일부만을 사용하고 나머지는 하드디스크의 스왑 영역에 저장했다가 필요할 때 가져오는 방식이 필요하다. 이 방식을 가상 메모리라고 한다.

가상 메모리를 통해 실행 중인 프로세스의 일부만 물리 메모리에 적재하고, 나머지는 스왑 공간에 두어 메모리를 효율적으로 사용할 수 있다.

 

주소 변환과 메모리 관리 전략

메모리 관리자는 가상 주소를 물리 주소로 변환하기 위해 동적 주소 변환(Dynamic Address Translation) 을 수행한다.
프로세스는 논리 주소를 사용하고, 실제 주소 변환은 MMU가 수행한다. 이때 물리 메모리와 스왑 영역을 포함한 공간을 관리해야 하므로 메모리 관리자는 복잡한 구조를 가진다.

물리 메모리에서 0x0 주소는 운영체제가 사용하는 공간이므로 일반 사용자 프로세스는 사용할 수 없으며, 나머지 영역은 고정 분할 방식 또는 가변 분할 방식으로 나눠진다.

가상 메모리 시스템에서는 가상 주소가 물리 메모리나 스왑 영역의 어느 위치에 있는지를 일대일 매핑 테이블(페이지 테이블 등) 을 통해 관리한다.

 

 

세그멘테이션(Segmentation)

세그멘테이션은 프로그램을 논리적 단위(코드, 데이터, 힙, 스택 등)로 분리하여 각 영역을 세그먼트로 나누는 방식이다.

변환 예시:

논리주소가 더 클 경우

장점:

단점:

페이징(Paging)

 

페이징은 논리 주소와 물리 주소를 동일한 크기의 단위로 나누어 사용하는 방식이라 외부 단편화가 발생하지 않음.

논리 주소 공간과 물리 주소 공간은 각각의 역할이 있다.

논리 주소 공간은 페이지(Page) 단위로 나뉘고 물리 주소 공간은 프레임(Frame) 단위로 나뉨

 

주소 변환 과정:

메모리 관리자는 테이블을 가지고 있는데 이를 페이지 테이블(Page Table)이라 부른다. CPU에서 논리 주소를 전달하면, 메모리 관리자는 이 논리 주소가 몇 번 페이지에 해당하는지와 오프셋(offset)을 계산한다.

  1. 논리 주소는 (페이지 번호, 오프셋)으로 나뉨

  2. 페이지 번호 → 페이지 테이블에서 대응되는 프레임 번호 찾음

  3. 물리 주소 = 프레임 시작 주소 + 오프셋

 

32bit cpu가 주소변환하는 과정 :

CPU가 논리 주소 0x1000에 접근할 때:

CPU가 논리 주소 0x1000에 접근할 때:

- 0x1000 = 4096 (10진수)

- 페이지 크기: 16MB = 16,777,216 bytes

- 페이지 번호 = 4096 ÷ 16,777,216 = 0 (페이지 크기 기준 몫)
- 오프셋 = 4096 % 16,777,216 = 4096 (또는 0x1000)

- 페이지 테이블에서 0번 인덱스의 값이 프레임 3이라면,
- 프레임 3의 시작 주소 = 3 × 16MB = 50,331,648 (또는 0x3000000)
- 물리 주소 = 프레임 시작 주소 + 오프셋 = 0x3000000 + 0x1000 = 0x3001000

최종 물리 주소 = 0x3001000

장점:

단점:

 

세그멘테이션 vs 페이징

이처럼 세그멘테이션은 논리적 구조에 맞는 유연한 관리가 가능하지만 외부 단편화가 단점
페이징은 메모리 관리가 간편하지만 내부 단편화와 테이블 크기 문제가 단점

 

 

페이지드 세그멘테이션 (Paged Segmentation)

페이지드 세그멘테이션은 세그멘테이션과 페이징을 결합한 메모리 관리 기법으로, 두 방식의 장점을 결합하여 메모리를 효율적으로 관리하면서도 공유 및 접근 보호 기능을 강화할 수 있다.

 

세그멘테이션과 페이징의 역할

페이지드 세그멘테이션에서는 세그먼트 내부에서 다시 페이징을 적용하여, 세그먼트의 크기가 가변적이지만, 내부 단위는 고정된 크기(페이지)로 관리된다.

 

주소 변환 방식:

요약

1. 가상 주소에서 세그먼트 번호를 추출

2. 세그먼트의 권한을 검사

3. 해당 세그먼트의 페이지 테이블에서 프레임 번호 추출

4. 프레임 시작 주소 + 오프셋 → 물리 주소 계산

 

 

페이지드 세그멘테이션의 장점과 단점

장점

단점

 

현대 운영체제에서의 활용

 

 

 

메모리 접근 권한

메모리 영역별로 접근 권한을 다르게 설정함:

운영체제는 가상 주소를 물리 주소로 변환할 때 해당 주소에 대한 접근 권한을 검사하며, 위반 시 예외(Exception) 또는 인터럽트를 발생시켜 프로세스를 종료한다.

 

 

 

 

삽입 정렬 (Insertion Sort)

삽입 정렬은 배열을 정렬된 영역정렬되지 않은 영역으로 나누고, 정렬되지 않은 값을 하나씩 정렬된 영역에 삽입하는 방식이다.

정렬 과정 예시: [4, 1, 5, 3, 6, 2]

  1. 정렬된 영역: [4], 삽입할 값: 1 → [1, 4, 5, 3, 6, 2]

  2. 다음 값 5는 4보다 크므로 그대로

  3. 3은 5보다 작아, 5와 4를 오른쪽으로 밀고 → [1, 3, 4, 5, 6, 2]

  4. 반복하여 최종 정렬 결과: [1, 2, 3, 4, 5, 6]

 

 

자바 코드 예시

public static void insertion(int[] arr) {
        for(int i = 1; i < arr.length; i++){ // i는 1부터 시작(첫 번째 원소는 이미 정렬된 상태로 간주)
            int insertingData = arr[i]; // 현재 삽입할 데이터를 저장
            int j; // j를 외부에서 선언
            
            for(j = i - 1; j >= 0; j--){ // 현재 삽입할 데이터(insertingData)와 정렬된 영역 비교
                if(arr[j] > insertingData){ // 삽입할 값보다 크면 한 칸 오른쪽으로 이동
                    arr[j + 1] = arr[j];
                } else {
                    break; // 삽입할 위치를 찾으면 루프 종료
                }
            }
            arr[j + 1] = insertingData; // 찾은 위치에 삽입할 값 저장
        }
    }

    public static void main(String[] args) {
        int[] arr = {1, 6, 4, 3, 2, 5};
        System.out.println("정렬 전: " + Arrays.toString(arr));
        insertion(arr);
        System.out.println("정렬 후: " + Arrays.toString(arr));
    }

 

 

 

 

2일차

 

디맨드 페이징(Demand Paging)(가져오기 정책)

프로그램이 메모리에 올라와 프로세스로 실행될 때, 코드(Code), 데이터(Data), 힙(Heap), 스택(Stack) 등의 모듈이 모두 메모리에 올라와 실행되는 것처럼 보이지만, 실제로는 필요할 때만 필요한 부분이 메모리에 올라와 실행된다.

지역성(Locality) 이론

90:10 법칙에 따르면 프로그램이 실행될 때 전체 코드의 10%에서 90%의 시간이 소비된다고 하고
이는 지역성(Locality) 이론과 관련이 있으며, 지역성에는 두 가지 유형이 있다.

  1. 공간적 지역성(Spatial Locality): 현재 위치와 가까운 데이터에 접근할 확률이 높음.

  2. 시간적 지역성(Temporal Locality): 현재 기준으로 가까운 시간에 접근한 데이터가 먼 시간에 접근했던 데이터보다 다시 접근될 확률이 높음.

운영체제는 이러한 지역성 이론을 활용하여 자주 사용될 데이터만 메모리에 올리고, 필요하지 않은 데이터는 스왑 영역으로 보내 성능을 향상시킨다.
디맨드 페이징(Demand Paging)은 조만간 필요할 것 같은 데이터를 메모리로 가져오고, 사용되지 않을 것 같은 데이터는 스왑 영역으로 이동시키는 정책이다.

디맨드 페이징을 사용하는 예

 

메모리 계층 구조

메모리는 속도에 따라 여러 계층으로 나눌 수 있다.

디맨드 페이징에서는 스왑 영역을 보조 저장 장치에 저장하는데, 성능 향상을 위해 스왑 영역으로 데이터를 이동시키는 횟수를 최소화해야 한다.

 

 

가상 메모리와 페이지 테이블

가상 메모리 크기는 물리 메모리 크기 + 스왑 영역 크기로 정의된다.

가상 주소가 주어지면 운영체제는 **페이지 테이블(Page Table)**을 참조하여 물리 메모리 내의 프레임(Frame) 위치를 찾거나, 스왑 영역 내의 위치를 확인한다.

페이지 테이블 엔트리(Page Table Entry, PTE)는 여러 비트 정보를 포함한다.

프로세스가 요청하면 운영체제는 페이지 테이블을 참조하여 물리 메모리에 프레임이 존재하는지 확인한다. 만약 페이지가 물리 메모리에 없다면 Page Fault(페이지 폴트) 예외가 발생한다.

Page Fault 발생 시 처리 과정

  1. 운영체제는 페이지 테이블을 참조하여 해당 페이지가 스왑 영역에 있는지 확인.

  2. 스왑 영역에서 데이터를 메모리로 가져옴 (스왑인).

  3. 해당 프로세스는 대기 상태가 됨.

  4. 데이터가 메모리에 올라간 후 프로세스가 다시 실행 상태가 됨.

 

물리 메모리에서 데이터 참조 과정

 

페이지 교체 알고리즘

운영체제는 스왑아웃과 스왑인 시 어떤 페이지를 교체할지 판단해야 하며, 이를페이지 교체 알고리즘(Page Replacement Algorithm)이라 한다.

대표적인 알고리즘:

 

 

병합 정렬(Merge Sort)

병합 정렬은 재귀적으로 정렬하는 알고리즘이다.

  1. 데이터를 반으로 나누어 분할.

  2. 각각을 정렬한 후 병합하며 정렬.

  3. 두 개의 데이터가 하나로 합쳐질 때 비교 연산이 두 번 발생.

  4. 네 개의 데이터가 두 개로 병합될 때 비교 연산이 네 번 발생.

  5. 단계마다 영역의 수가 반으로 줄어들기 때문에 **O(log n)**의 성능을 가짐.

  6. 병합할 때 n개의 데이터를 비교하므로 전체 시간 복잡도는 O(n log n).

장점

단점

 

자바 코드 예시

    public static void MergeSort(int[] arr, int leftIndex, int rightIndex) {
        if(leftIndex < rightIndex){
            int midIndex = (int)(leftIndex + rightIndex) / 2;
            MergeSort(arr, leftIndex, midIndex); // 왼쪽 영역 정렬
            MergeSort(arr, midIndex+1, rightIndex); // 오른쪽 영역 정렬
            Merge(arr, leftIndex, midIndex, rightIndex); // 정렬된 두 영역을 병합
        }

    }

    public static void Merge(int[] arr,int leftIndex,int midIndex, int rightIndex){
        int leftAreaIndex = leftIndex; // 왼쪽 영역 시작점
        int rightAreaIndex = midIndex + 1; // 오른쪽 영역 시작점

        int[] tempArr = new int[rightIndex + 1]; // 임시 배열 생성
        Arrays.fill(tempArr, 0);

        int tempArrIndex = leftIndex; // 비교한 배열을 넣을 시작점



        while(leftAreaIndex <= midIndex && rightAreaIndex <= rightIndex){  // 왼쪽과 오른쪽 영역을 비교하여 tempArr에 저장
            if(arr[leftAreaIndex] <= arr[rightAreaIndex]){
                tempArr[tempArrIndex] = arr[leftAreaIndex++];
            }else{
                tempArr[tempArrIndex] = arr[rightAreaIndex++];
            }
            tempArrIndex++;
        }

        if(leftAreaIndex > midIndex){ // 왼쪽 영역이 먼저 끝났을 경우, 오른쪽 나머지 요소 복사
            for(int i = rightAreaIndex; i <= rightIndex;i++){
                tempArr[tempArrIndex++] = arr[i];
            }
        }else{ // 오른쪽 영역이 먼저 끝났을 경우, 왼쪽 나머지 요소 복사
            for(int i =leftAreaIndex; i <= midIndex;i++){
                tempArr[tempArrIndex++] = arr[i];
            }
        }

        // 정렬된 결과를 원본 배열에 복사
        for(int i = leftIndex; i <= rightIndex; i++){ // arr배열에 정렬된 값을 넣어줌
            arr[i] = tempArr[i];
        }

        
        
    }

    public static void main(String[] args) {
        int[] arr = {3,5,2,4,1,7,8,6};

        System.out.println("정렬 전: " + Arrays.toString(arr));

        MergeSort(arr, 0, arr.length - 1); // 병합 정렬 실행

        System.out.println("정렬 후: " + Arrays.toString(arr)); // 정렬된 배열 출력
    }

 

 

 

3일차

 

페이지 교체 정책

프로세스는 데이터를 접근하기 위해 메모리를 참조하는데, 해당 데이터가 메모리에 없으면 페이지 폴트(Page Fault)가 발생한다.
이 경우, 운영체제는 해당 페이지를 스왑 영역에서 물리 메모리로 불러와야 한다.
하지만 메모리가 가득 차서 공간이 없다면, 기존에 메모리에 있는 페이지 중 하나를 선택하여 스왑 영역으로 옮겨야 하는데 이때 어떤 페이지를 교체할지 결정하는 것이 페이지 교체 정책(Page Replacement Policy)이다.

 

페이지 교체 정책의 종류

  1. 랜덤(Random) 교체 알고리즘

    • 무작위로 페이지를 선택하여 교체하는 방법.

    • 지역성을 고려하지 않아 성능이 낮아 실제로 거의 사용되지 않음.

     

  2. FIFO(First-In First-Out) 교체 알고리즘

    • 가장 먼저 메모리에 들어온 페이지를 제거하는 방식.

    • 단순하고 구현이 쉬우나, 자주 사용되는 페이지도 오래되었다는 이유로 교체될 수 있어 성능 저하 발생 가능.

    • 빌레이디의 역설(Bélády's Anomaly):프레임 수를 증가시켜도 오히려 페이지 폴트가 증가하는 현상이 발생할 수 있음.

     

  3. 최적(Optimal, OPT) 교체 알고리즘

    • 앞으로 가장 오랫동안 사용되지 않을 페이지를 교체하는 방식.

    • 이론적으로 최적이지만, 미래의 메모리 접근 패턴을 예측해야 하므로 실제 구현이 불가능.

    • 다른 알고리즘의 성능을 평가할 때 비교 기준으로 사용됨.

     

  4. LRU(Least Recently Used) 교체 알고리즘

    • 가장 오랫동안 사용되지 않은 페이지를 교체하는 방식.

    • 시간적 지역성(Temporal Locality)을 고려하여 자주 사용된 페이지를 유지함.

    • 하지만 모든 페이지의 사용 시간을 기록해야 하므로 구현이 어려움.

    • 하드웨어적으로 접근 비트(Access Bit)를 지원하지 않는 경우 구현이 불가능하여 다른 대체 기법을 사용해야 함.

 

FIFO의 문제점과 해결책

 

LRU의 한계와 클락(Clock) 알고리즘

 

 

스레싱(Thrashing)과 워킹셋(Working Set)

 

 

 

퀵 정렬(Quick Sort)

퀵 정렬의 성능

퀵 정렬이 병합 정렬보다 선호되는 이유

 

 자바 코드 예시

    public static void quickSort(int[] arr, int left, int right) {
        if(left <= right){
            int pivot = divide(arr,left,right);
            quickSort(arr,left,pivot - 1);
            quickSort(arr,pivot + 1,right);

        }

    }

    public static int divide(int[] arr, int left, int right){
        int pivot = arr[left]; // 배열의 가장 왼쪽을 피벗으로 지정
        int leftStartIndex = left + 1; // 피벗이 인덱스0이니 그다음인 인덱스 1을 지정
        int rightStartIndex = right; // 가장 오른쪽 인덱스부터 시작

        while(leftStartIndex <= rightStartIndex){
            while(leftStartIndex <= right && pivot >= arr[leftStartIndex]){
                // 피벗보다 작거나 같은 값은 이미 왼쪽에 있어야 하니 다음 인덱스로 이동
                leftStartIndex++;
            }

            while(rightStartIndex >= (left + 1)  && pivot <= arr[rightStartIndex]){
                // 피벗보다 큰 값은 오른쪽에 있어야 하니 왼쪽으로 이동
                rightStartIndex--;
            }

            if(leftStartIndex <= rightStartIndex){ 
          // leftStartIndex가 rightStartIndex보다 값이 작을경우 이경우는 아직 서로 마주치지 않은상태
                swap(arr, leftStartIndex, rightStartIndex);
            }

        }

        swap(arr, left, rightStartIndex); 
        // 피벗을 기준으로 나뉜 두 그룹의 경계 위치인 rightStartIndex와 피벗을 교환

        return rightStartIndex; // 피벗의 최종 위치(정렬 후 피벗이 위치해야 하는 자리)를 반환
        
    }

    public static void swap(int[] arr,int index1, int index2){
        int temp = arr[index1];
        arr[index1] = arr[index2];
        arr[index2] = temp;
    }

    public static void main(String[] args) {
        int[] arr = {5,3,7,2,6,4,9,1,8};

        System.out.println("정렬 전: " + Arrays.toString(arr));

        quickSort(arr, 0, arr.length-1);

        System.out.println("정렬 후: " + Arrays.toString(arr)); // 정렬된 배열 출력
    }

 

 

4일차

입출력 장치

 

 

주변장치 개요

주변장치는 그래픽카드, 하드디스크, SSD, 키보드, 마우스 등 다양한 장치들이 있으며, 이들은 메인보드의 버스를 통해 연결된다. 버스는 주소 버스(Address Bus), 데이터 버스(Data Bus), 제어 버스(Control Bus)로 구성되어 있으며, I/O 디바이스는 이를 통해 데이터를 송수신한다.

각 주변장치에는 외부 인터페이스와 함께 상태나 데이터를 보관하는 레지스터들이 존재하며, 이 레지스터에 저장된 데이터는 CPU가 사용하기 위해 메모리로 이동되기도 한다.

주변장치는 캐릭터 디바이스블록 디바이스로 구분된다. 이는 데이터를 전송하는 단위에 따라 나뉘며, 캐릭터 단위인지 블록 단위인지를 기준으로 한다.

 

입출력 구조와 제어 방식

예전 시스템에서는 모든 주변장치를 하나의 버스로 연결했으며, CPU가 직접 I/O 장치에 접근하여 데이터를 처리했다. 이 방식은 입출력 중 CPU가 다른 작업을 하지 못해 CPU 사용률이 낮아지는 문제가 있었다.

이를 개선하기 위해 **입출력 제어기(I/O Controller)**와 입출력 버스가 도입되었다. CPU는 I/O 명령을 만나면 입출력 제어기에 명령을 위임하고 다른 작업을 수행할 수 있게 되었다.

입출력 제어기는 두 채널로 구분된다:

입출력 버스는 다시 고속 I/O 버스저속 I/O 버스로 나뉘며, 장치 속도에 따라 병목 현상을 방지한다. 예를 들어, 그래픽카드는 매우 빠른 대역폭이 필요하기 때문에 시스템 버스에 직접 연결되기도 한다.

하지만 입출력 제어기만으로는 데이터를 메모리에 쓰기 위해 여전히 CPU의 개입이 필요했다. 이를 해결하기 위해 DMA(Direct Memory Access) 제어기가 도입되었다. DMA는 입출력 장치가 CPU를 거치지 않고 직접 메모리에 접근할 수 있게 하여 성능을 향상시킨다.

DMA와 CPU가 메모리 충돌 없이 사용할 수 있도록, 메모리를 나눠 관리하는 방식을 Memory Mapped I/O라고 한다.

 

마우스와 키보드 동작 방식

 

하드디스크와 플래시 메모리

하드디스크 구조

하드디스크에는 중심축인 스핀들이 있으며, 여기에 여러 개의 원판(플래터)이 장착되어 있다. 플래터는 자기화된 원판으로, 디스크 암에 고정된 읽기/쓰기 헤드가 데이터를 읽거나 쓴다.

플래터는 여러 개의 트랙(원형 경로)으로 구성되며, 각 트랙은 다시 섹터로 나뉜다. 섹터는 하드디스크의 가장 작은 저장 단위이다.
플래터가 여러 개인 경우 같은 위치의 트랙들을 묶은 것을 실린더(Cylinder)라고 한다.

플래터 표면에는 자성이 있으며, N극으로 자화된 부분은 0, S극으로 자화된 부분은 1로 인식된다.
이러한 방식으로 디지털 데이터를 저장한다.

일반적으로 하드디스크는 2개 이상의 플래터를 사용하고, 각 플래터의 양면에 헤드가 위치해 데이터를 읽는다.

헤드는 디스크 암에 고정되어 있으며, 하나의 디스크 암이 여러 헤드를 동시에 움직이기 때문에 모든 헤드는 함께 이동하게 된다.

하드디스크는 이처럼 기계적인 이동이 많기 때문에 느리고, 충격에 약하며, 소음이 발생하는 단점이 있다.

 

플래시 메모리(SSD)

플래시 메모리는 전기적으로 데이터를 처리하기 때문에 빠르고 조용하며, 충격에도 강하다. 또한 자성을 띠지 않으므로 외부 자기장에 영향을 받지 않는다.

하지만 플래시 메모리는 특정 영역에 데이터를 다시 쓰기 위해서는 먼저 해당 영역을 지워야 하며, 이 지우기 작업에는 횟수 제한이 존재한다.
같은 지점을 반복해서 지우고 쓰는 작업이 누적되면 해당 셀의 수명이 다해 결국 플래시 메모리가 손상되어 사용할 수 없게 되는 문제가 발생할 수 있다.
또한 자성을 띠지 않으므로 외부 자기장에 영향을 받지 않는다.

 

동적 프로그래밍과 메모이제이션

동적 프로그래밍(Dynamic Programming)은 큰 문제를 작은 문제로 나누어 해결하는 방식으로,
특히 중복 계산을 피하는 데 효과적이다.

예를 들어, 피보나치 수열을 재귀로 구현하면 같은 계산을 반복하게 되어 O(n²)의 성능을 가지게 되지만,
메모이제이션(Memoization)을 사용하면 계산 결과를 저장해 중복 연산을 제거하여 O(n)의 성능을 달성할 수 있다.

단점으로는, 속도 향상을 위해 메모리를 추가로 사용하게 된다는 점이 있다.

 

자바코드 예제

    public static int fibonacci1(int n) {
        if(n == 0 || n == 1){
            return n;
        }

        return fibonacci1(n - 2) + fibonacci1(n - 1);
    }

    public static int fibonacci2(int n, HashMap<Integer,Integer> memo){
        if(n == 0 || n == 1){
            return n;
        }

        if (!memo.containsKey(n)) {
            memo.put(n, fibonacci2(n - 2, memo) + fibonacci2(n - 1, memo));
        }

        return memo.get(n);
        
    }



    public static void main(String[] args) {


        System.out.println("fibonacci1 : " + fibonacci1(5));

        HashMap<Integer,Integer> memo = new HashMap<>();

        System.out.println("fibonacci2 : " + fibonacci2(5,memo));
    }

 

 

 

 

5일차

파일과 파일시스템

 

파일시스템의 역할과 구조

메모리처럼 직접 접근이 가능한 장치와는 달리, 하드디스크나 SSD 같은 저장장치는 사용자가 직접 데이터를 저장하거나 수정하게 되면 중요한 정보가 손상될 수 있다.
그래서 사용자는 운영체제를 통해 파일 저장을 요청하고, 운영체제가 이를 안전하게 처리한다.

운영체제는 이러한 파일 관리를 위해 파일 관리자를 두며, 이를 파일시스템(File System)이라 부릅니다.
이는 메모리 관리에서 페이지 테이블을 통해 가상 주소를 물리 주소로 변환하듯, 파일 테이블을 통해 파일의 정보와 위치 등을 관리한다.

 

 

파일시스템의 주요 기능

  1. 파일과 디렉토리 생성: 새 파일 및 폴더를 만들 수 있도록 지원함

  2. 파일과 디렉토리 수정 및 삭제: 기존 데이터를 변경하거나 제거할 수 있음

  3. 파일 권한 관리: 사용자별 접근 권한을 설정하고 제어함

  4. 무결성 보장: 파일 손상 없이 안전하게 유지되도록 함

  5. 백업 및 복구: 데이터 유실에 대비해 저장 및 복원 기능 제공

  6. 암호화: 파일 내용을 보호하기 위한 암호화 기능 제공

 

파일시스템과 저장장치의 관계

파일시스템은 하드디스크나 플래시 메모리(SSD) 같은 저장장치에 설치된다.
이러한 장치는 데이터를 블록 단위로 전송하지만, 사용자는 바이트 단위로 데이터를 읽고 쓰기 때문에 파일시스템은 중간에서 블록을 바이트 단위로 관리해주는 역할을 한다.

주변장치의 관점에서 보면, 하드디스크와 플래시 메모리는 블록 디바이스에 해당하며, 키보드나 마우스처럼 캐릭터 단위로 처리되지 않는다.

 

 

파일과 파일 디스크립터

파일은 헤더(Header)와 데이터(Data)로 구성되어 있으며, 헤더에는 파일의 속성 정보가 담겨 있다.
운영체제는 파일 정보를 관리하기 위해 파일 제어 블록(File Control Block) 을 유지하며, 사용자가 파일을 열면
파일 디스크립터(File Descriptor) 라는 참조 번호를 통해 파일 제어 블록에 간접적으로 접근할 수 있도록 한다.

 

파일 구조의 종류

  1. 순차 파일 구조 (Sequential File Structure)

    • 데이터가 파일 내에 순서대로 저장됨.

    • C 언어에서는 open() 함수로 파일을 열고, 파일 디스크립터는 파일의 맨 앞을 가리킨다.

    • 읽기/쓰기 작업은 처음부터 진행되며, 특정 위치로 이동하려면 lseek() 함수를 사용해야 함.

    • 장점: 구조가 단순하고 공간 낭비가 적음.

    • 단점: 중간 삽입/삭제가 느림.

  2. 직접 파일 구조 (Direct File Structure)

    • 데이터를 저장할 위치를 해시 함수를 이용해 결정.

    • 해시테이블, JSON 등의 구조와 유사.

    • 장점: 매우 빠른 접근 속도.

    • 단점: 해시 함수 선정이 중요하며, 충돌 및 공간 낭비 가능성 존재.

  3. 인덱스 파일 구조 (Indexed File Structure)

    • 순차 접근과 직접 접근의 장점을 결합.

    • 인덱스를 통해 빠른 접근이 가능하고, 순차 처리도 지원함.

 

디렉토리 구조

디렉토리는 파일을 포함할 수 있으며, 하위 디렉토리를 포함할 수도 있다. 디렉토리 구조는 계층형 트리 구조로 발전해 왔으며, 최상위 디렉토리를 루트 디렉토리(Root Directory)라고 부른다.

디렉토리는 일반 파일과 동일한 구조를 가지지만, 일반 파일은 데이터를 저장하고 디렉토리는 파일 목록 정보를 저장한다.

 

디렉토리의 발전

 

파일과 디스크 할당 방식

파일시스템은 파일 정보를 파일 테이블로 관리하고, 이 테이블에는 파일이 저장된 블록의 시작 위치가 기록된다.

파일은 여러 블록으로 나뉘어 저장되며, 블록 연결 방식에 따라 아래와 같이 분류된다:

  1. 연속 할당 (Contiguous Allocation)

    • 파일을 연속된 블록에 저장.

    • 시작 블록만 알면 전체 파일에 접근 가능 → 매우 빠름.

    • 단점: 외부 단편화 발생, 파일 크기 예측 어려움, 실제로는 사용하지않는 방식

  2. 불연속 할당 (Non-contiguous Allocation)

    • 디스크의 빈 블록에 데이터를 분산 저장.

    • 연결할당과 인덱스할당으로 나눠짐

    • 연결 할당 (Linked Allocation)

      • 각 데이터 블록이 다음 블록의 위치를 가리킴 (연결 리스트 방식)

      • 파일 테이블에는 시작 블록만 저장됨

    • 인덱스 할당 (Indexed Allocation)

      • 파일마다 인덱스 블록을 두고, 그 안에 데이터 블록의 포인터를 저장

      • 인덱스 블록이 꽉 차면 다른 인덱스 블록을 추가 연결 (확장 가능)

      • 파일의 크기가 작다면 데이터를 바로 참조하는 블록 포인터를 이용
        크다면 간접 포인터를 이용해 많은데이터를 참조

      • 유닉스/리눅스에서는 이 구조를 i-node라고 함

 

디스크 블록과 공간 관리

 

Free Block List

 

 

 

동적 프로그래밍 - 타뷸레이션(Tabulation)

타뷸레이션은 상향식 계산 방식이다. 문제를 해결하는 데 필요한 값을 미리 계산하여 테이블에 저장하고, 이후 필요한 값은 테이블에서 꺼내 사용하는 방식이다.

메모이제이션 vs 타뷸레이션

문제가 분할 정복적으로 풀기 쉬우면 메모이제이션, 재귀가 복잡하거나 메모리 최적화가 중요하면 타뷸레이션이 더 적합하다.

 

 자바 코드 예시

    public static int fibonacci3(int n){
        if(n <= 1){
            return n;
        }
        int[] table = new int[n+1];

        table[0] = 0;
        table[1] = 1;

        for(int i = 2;i <= n;i++){
            table[i] = table[i - 2] + table[i - 1];
        }

        return table[n];
    }



    public static void main(String[] args) {
        System.out.println("fibonacci3 : " + fibonacci3(5));
    }

 

 

3주차 회고

이번주 역시 매일 스케쥴에 맞춰 강의를 듣고 발자국 정리 하는건 빼먹지않았습니다.

3주차 강의내용은 생각보다 정말 어려워 아직 이해했다 하기는 힘든상태라 발자국에 정리를 해두고 다시 3주차 처음부터 공부를 다시 해볼 계획입니다.

 

 

 

 

 

 

 이번 인프런 워밍업 클럽 후기

인프런 강의를 사두고 강의를 완강하는건 쉽지않아 완강할 수있는 계기가 필요해 이번 인프런 워밍업 클럽에 신청해
그중 cs지식이 필요해 감자님의 강의를 수강했습니다.

국비과정을 수료해 코드만 칠줄알았었는데 cs 공부를 처음해보니 이해하기 정말 힘들었어서 이번 워밍업 클럽이 아니였으면 사실 손도 안댔을거같습니다.

강의는 cs지식이 0에 가까운 저에게 강의제목처럼 그림으로 이해하기 쉽게 알려주셔서 cs지식이 필요한데 뭘 공부할지 모르시는 분들께 정말 추천드리고 강의를 듣다가 질문을 작성하면 답변을 새벽에 올려도 금방금방 답변해주셔서 정말 좋았습니다.

 

만약 4기 워밍업 클럽도 진행한다면 강력추천합니다.

강의 영상도 좋고 질문답변해주시는 것도 빠르고 좋습니다.

특히 난 강의를 구매하고 완강을 하기 힘드신분은 강력 추천드립니다.

매일 어느파트의 강의를 들어야하는 시간표를 건내줘서 본인이 해당 시간표에 맞춰 행동하겠다 마음만 먹으면 충분히 하실수 있습니다.

 

 

 

 

 

 

답변 0