Skip to main content

Command Palette

Search for a command to run...

Operating System File Management: Directory, Inode, Block, and File Descriptors

Updated
49 min read
Operating System File Management: Directory, Inode, Block, and File Descriptors
H
Hi there, I'm a full time software engineering student, and full time mum based in Brisbane, QLD, Australia. Korean is my native language and English is my second, but I love learning software in English. My posts are a mix of both Korean and English, so there's something for everyone! All my posts come straight from my lecture notes, so follow along my study journey with me <3

파일 시스템(File System)이 필요한 이유

첫 번째 질문이 있다. 컴퓨터에 파일을 저장하는 상황을 떠올려보자. 과제 파일, 사진, 동영상처럼 다양한 파일을 계속 저장하다 보면 파일의 수가 많아질수록 어떤 문제가 발생할까?

파일의 수가 많아지면 필요한 파일을 찾는 데 오랜 시간이 걸릴 수 있다. 또한 파일이 어디에 저장되어 있는지 헷갈릴 수도 있다. 아무 기준 없이 파일이 저장되어 있다면 원하는 파일을 빠르게 찾는 것은 더욱 어려워진다.

이처럼 데이터를 단순히 저장하는 것만으로는 부족하다. 파일을 효율적으로 사용하기 위해서는 체계적으로 관리하는 구조가 필요하다. 운영체제(Operating System)는 파일 시스템(File System)을 통해 파일을 구조적으로 관리하고, 사용자가 원하는 파일에 효율적으로 접근할 수 있도록 지원한다.

두 번째 질문이 있다. 파일을 저장할 때 우리는 디스크(Disk)의 실제 위치를 직접 지정하지 않는다. 보통 파일 이름(File Name)만 정해서 저장한다. 그렇다면 저장된 파일은 실제로 어디에 저장되는 것일까? 또한 나중에 파일을 사용할 때는 어떤 기준으로 찾아오게 될까?

파일의 이름과 실제 저장 위치(Storage Location)는 분리되어 있다. 실제 파일 데이터는 보조기억장치(Secondary Storage)의 특정 위치에 저장되며, 운영체제가 이 위치 정보를 체계적으로 관리한다.

만약 이러한 위치 정보가 제대로 관리되지 않는다면 파일을 찾는 데 시간이 오래 걸리거나, 원하는 파일을 찾지 못하는 상황이 발생할 수 있다. 따라서 이번 내용에서는 파일의 저장 위치를 운영체제가 어떻게 관리하는지, 그리고 파일을 효율적으로 찾기 위해 어떤 구조를 사용하는지 살펴본다.


파일 시스템(File System) 학습 개요

이번 주차는 파일 시스템(File System)에 대해 학습한다. 프로그램이 실행되면서 생성되는 데이터를 지속적으로 보관하기 위해서는 보조기억장치(Secondary Storage)에 저장해야 한다.

이때 운영체제(Operating System)는 데이터를 단순히 저장하는 것이 아니라, 파일(File)이라는 논리적인 단위로 관리한다. 이를 통해 사용자가 데이터를 효율적으로 저장하고 접근할 수 있도록 지원한다.

이번 시간에는 파일의 구성 방식과 관리 방법, 그리고 파일이 디스크(Disk)에 저장되고 접근되는 전체적인 흐름을 살펴본다. 특히 파일과 디렉토리(Directory)의 구성, 아이노드(Inode)를 통한 파일 접근 과정, 디스크 블록(Block) 단위의 저장 방식을 학습한다.

또한 파일이 디스크에 저장될 때 사용되는 연속 할당(Contiguous Allocation), 연결 할당(Linked Allocation), 인덱스 할당(Indexed Allocation) 방식의 특징을 이해하고, 각 방식의 접근 성능과 저장 공간 효율을 비교한다.

마지막으로 파일 입출력(File Input/Output) 실습을 통해 파일 디스크립터(File Descriptor)와 오프셋(Offset)의 의미를 이해한다. 또한 파일이 여러 프로세스(Process) 간에 공유될 때 어떤 구조로 동작하는지도 함께 살펴본다.


파일 시스템(File System)의 개념과 역할

먼저 파일 시스템(File System)의 개념과 역할에 대해 살펴보자. 파일 시스템이란 파일을 생성(Create), 저장(Store), 검색(Search), 수정(Update)하는 전체 과정을 관리하는 운영체제(Operating System)의 중요한 구성 요소이다.

파일 시스템은 디스크(Disk)의 물리적인 저장 구조를 사용자에게 직접 보여주지 않는다. 대신 파일(File)이라는 논리적인 단위를 통해 사용자가 데이터를 쉽게 저장하고 접근할 수 있도록 도와준다.

또한 파일 시스템은 개별 파일만 관리하는 것이 아니라, 디렉토리(Directory) 구조와 메타데이터(Metadata)를 포함해 전체 저장 공간을 관리한다. 여기서 메타데이터란 파일의 실제 내용이 아니라, 파일의 크기(File Size), 생성 시간(Creation Time), 접근 권한(Access Permission), 저장 위치(Storage Location)처럼 파일을 관리하기 위한 정보를 의미한다.

결국 파일 시스템은 사용자가 복잡한 저장 구조를 직접 신경 쓰지 않아도 데이터를 효율적으로 저장하고 찾을 수 있도록 해주는 핵심적인 관리 구조라고 이해할 수 있다.


파일 시스템(File System)의 역할

파일 시스템(File System)이 실제로 어떤 역할을 수행하는지 살펴보자.

먼저 파일 시스템은 파일을 생성(Create), 삭제(Delete), 수정(Update)하는 기본적인 작업을 처리한다. 또한 파일마다 접근 권한(Access Permission)을 관리하여 허용된 사용자만 파일을 읽거나 수정할 수 있도록 보호 기능(Protection)을 수행한다.

또한 파일 시스템은 디스크 공간(Disk Space)을 효율적으로 관리한다. 각 파일이 디스크의 어느 위치에 저장되어 있는지 추적하고, 사용자가 파일 이름(File Name)만으로도 해당 파일에 접근할 수 있도록 지원한다.

파일 시스템은 사용자와 저장장치(Storage Device) 사이에서 인터페이스(Interface) 역할도 수행한다. 사용자는 복잡한 디스크 구조를 직접 알 필요 없이 파일과 디렉토리(Directory)를 통해 데이터를 사용할 수 있다.

마지막으로 파일 시스템은 데이터의 일관성(Consistency)과 무결성(Integrity)을 유지한다. 여러 작업이 동시에 이루어지는 상황에서도 데이터가 손상되지 않고 올바른 상태로 유지되도록 관리하는 역할을 한다.


파일 시스템(File System)의 내부 구조

파일 시스템(File System)의 내부 구조는 사용자의 요청이 실제 디스크(Disk)에 접근하기까지의 흐름으로 이해할 수 있다.

먼저 사용자는 파일 생성(Create), 읽기(Read), 쓰기(Write)와 같은 작업을 요청한다. 파일 시스템은 이 요청을 받아 처리하며, 이 과정에서 디렉토리 관리(Directory Management)파일 조작(File Operation)이 이루어진다.

그다음 단계에서는 파일이 레코드(Record) 단위로 나뉘고, 논리적인 파일 구조(Logical File Structure)가 실제 디스크의 블록 구조(Block Structure)에 맞게 변환된다.

이후에는 블록 캐시(Block Cache)를 통해 디스크 접근 횟수를 줄인다. 자주 사용하는 데이터를 메모리(Memory)에 유지하면 매번 디스크에 접근하지 않아도 되므로 파일 접근 속도를 높일 수 있다.

오른쪽으로 갈수록 실제 디스크와 가까운 영역이다. 이 단계에서는 파일 할당(File Allocation), 빈 공간 관리(Free Space Management), 디스크 스케줄링(Disk Scheduling)과 같은 작업이 수행된다.

즉, 이 구조는 사용자의 파일 요청이 파일 시스템을 거쳐 최종적으로 디스크에 접근하는 전체 흐름을 보여준다.


파일(File)의 개념

파일(File)은 이름을 가진 하나의 논리적인 데이터 집합이다. 파일은 여러 데이터로 구성되어 있지만, 사용자 입장에서는 하나의 단위로 다룰 수 있다.

실제로 파일은 디스크(Disk)에 여러 블록(Block)으로 나뉘어 저장될 수 있다. 하지만 사용자는 파일을 여러 조각으로 인식하지 않고, 하나의 연속된 데이터처럼 사용한다.

즉, 파일의 실제 저장 위치나 내부 구조는 사용자에게 직접 보이지 않는다. 이러한 정보는 운영체제(Operating System)가 내부적으로 관리한다.

따라서 파일은 사용자가 데이터를 쉽게 다룰 수 있도록 제공되는 논리적인 데이터 단위(Logical Data Unit)이며, 실제 저장 구조와 관리는 운영체제가 담당한다고 이해할 수 있다.


파일(File)의 구성 요소

파일(File)은 여러 단계의 구성 요소로 이루어진다.

먼저 가장 작은 단위로 필드(Field)가 있다. 필드는 이름, 학번, 점수처럼 개별적인 정보를 의미한다.

이러한 필드들이 모이면 하나의 레코드(Record)가 된다. 레코드는 의미 있는 데이터의 묶음으로, 예를 들어 한 학생에 대한 전체 정보를 하나의 레코드라고 볼 수 있다.

여러 개의 레코드는 블록(Block) 단위로 저장된다. 블록은 디스크(Disk)에서 데이터를 읽고 쓰는 기본 단위이기 때문에, 운영체제(Operating System)는 실제로 블록 단위를 기준으로 데이터를 처리한다.

마지막으로 여러 블록이 모여 하나의 파일(File)을 구성한다. 즉, 파일은 필드(Field), 레코드(Record), 블록(Block)이라는 계층 구조를 통해 구성된다고 할 수 있다.


파일(File)의 속성

파일(File)은 실제 데이터뿐만 아니라, 파일을 관리하기 위한 다양한 정보도 함께 가진다.

파일의 속성(File Attribute)에는 파일 이름(File Name), 파일 크기(File Size), 저장 위치(Storage Location)와 같은 기본 정보가 포함된다. 운영체제(Operating System)는 이러한 정보를 통해 파일을 식별하고 접근한다.

또한 파일의 생성 시간(Creation Time), 수정 시간(Modification Time)과 같은 시간 정보도 관리된다. 접근 권한(Access Permission)과 같은 보호 정보도 포함되어 있어 파일의 읽기(Read), 쓰기(Write), 수정(Update) 권한을 제어할 수 있다.

이러한 속성들은 파일의 실제 내용을 직접 담고 있지는 않지만, 파일을 효율적으로 관리하고 보호하기 위해 반드시 필요한 정보라고 할 수 있다.


파일(File)의 속성과 데이터 연결 구조

파일(File)의 속성과 데이터가 어떻게 연결되는지 그림 구조를 통해 살펴보자.

먼저 디렉토리(Directory)에는 파일 이름(File Name)과 해당 파일을 가리키는 인덱스(Index) 정보가 저장되어 있다. 사용자가 파일 이름으로 접근하면, 운영체제(Operating System)는 디렉토리를 통해 해당 파일을 찾는다.

그다음 인덱스(Index)를 통해 파일 헤더(File Header)로 연결된다. 파일 헤더에는 파일 크기(File Size), 생성 시간(Creation Time), 접근 권한(Access Permission)과 같은 파일 속성(File Attribute)이 저장되어 있다.

또한 파일 헤더에는 실제 데이터가 저장된 위치를 가리키는 주소 정보(Address Information)도 포함된다. 운영체제는 이 주소를 따라가 실제 데이터 블록(Data Block) 또는 인덱스 블록(Index Block)에 접근한다.

정리하면, 파일은 디렉토리(Directory), 파일 헤더(File Header), 데이터 블록(Data Block)의 구조를 통해 접근된다. 사용자는 파일 이름만 사용하지만, 내부적으로는 운영체제가 파일 속성과 저장 위치 정보를 이용해 실제 데이터를 찾아간다.


File Operations(파일 연산)

이번에는 파일 연산(File Operation)에 대해 살펴본다. 파일 연산은 파일을 열고, 데이터를 읽거나 쓰고, 사용이 끝나면 닫고, 필요하면 삭제하는 일련의 과정으로 이해할 수 있다.

먼저 open은 파일을 식별하고 접근을 준비하는 단계이다. 파일 이름(File Name)을 기준으로 디렉토리(Directory)를 탐색하고, 해당 파일의 위치(Location)와 속성(Attribute) 정보를 찾아온다.

read/write는 실제 데이터를 처리하는 단계이다. 이때 현재 파일 내 위치를 오프셋(Offset)이라고 하며, 운영체제(Operating System)는 이 오프셋을 기준으로 데이터를 읽거나 쓴다. 데이터를 처리하면 파일 포인터(File Pointer)의 위치도 함께 이동한다.

close는 파일 사용이 끝났을 때 수행되는 단계이다. 파일과 관련된 자원(Resource)을 반환하고, 파일 접근 상태를 종료한다.

마지막으로 delete는 파일을 제거하는 작업이다. 파일을 삭제하고, 해당 파일이 사용하던 디스크 공간(Disk Space)을 해제한다.

즉, 파일 연산은 파일을 열고(open), 사용하고(read/write), 닫고(close), 필요하면 삭제하는(delete) 흐름으로 이루어진다고 이해할 수 있다.


Directory(디렉토리)

디렉토리(Directory)는 파일 이름(File Name)과 실제 저장 위치를 가리키는 아이노드(Inode)를 연결하는 구조이다.

사용자는 파일 이름으로 파일에 접근하지만, 운영체제(Operating System)는 디렉토리를 통해 해당 파일의 아이노드를 찾고, 이를 바탕으로 실제 저장 위치와 연결한다.

또한 디렉토리는 여러 파일을 그룹화(Grouping)하는 역할을 한다. 관련된 파일을 함께 관리할 수 있게 해주며, 파일 탐색(File Search)을 더 효율적으로 만들어준다.

디렉토리는 계층 구조(Hierarchical Structure)를 통해 파일을 체계적으로 관리한다. 사용자는 폴더(Folder)처럼 보이는 디렉토리 구조를 이용해 파일을 분류하고 쉽게 찾을 수 있다.

📌아이노드(Inode)는 파일의 실제 데이터 자체를 저장하는 공간이 아니라, 파일의 메타데이터(Metadata)와 데이터 블록(Data Block)의 위치 정보를 저장하는 구조이다.

즉, 디렉토리는 보통 “파일 이름 → 아이노드 번호(Inode Number)”를 연결하고, 아이노드는 “파일 정보 → 실제 데이터 블록 위치”를 연결한다고 이해하면 좋다.


Directory Structure(디렉토리 구조)

디렉토리(Directory)는 계층 구조(Hierarchical Structure)를 가진다. 즉, 하나의 디렉토리 안에 또 다른 디렉토리가 포함될 수 있으며, 이를 통해 전체 파일을 체계적으로 관리할 수 있다.

일반적으로 디렉토리 구조는 루트 디렉토리(Root Directory)를 기준으로 여러 개의 디렉토리와 파일(File)이 트리 구조(Tree Structure) 형태로 구성된다.

이러한 디렉토리 계층 구조를 사용하면 파일을 논리적으로 구분할 수 있고, 사용자는 원하는 파일을 더 효율적으로 탐색할 수 있다.

📌루트 디렉토리(Root Directory)는 디렉토리 구조의 가장 위에 있는 시작점이다. 리눅스/유닉스(Linux/Unix)에서는 /로 표현하고, 윈도우(Windows)에서는 보통 C:\와 같은 드라이브 단위로 표현된다.


Directory Organization(디렉토리 구조의 조직 방식)

앞서 디렉토리(Directory)의 역할을 살펴보았다면, 이번에는 디렉토리 구조(Directory Structure)가 어떻게 조직되는지 살펴본다.

초기의 파일 시스템(File System)은 모든 파일을 하나의 공간에 저장하는 단일 디렉토리 구조(Single-Level Directory Structure)로 시작하였다. 하지만 파일의 수가 증가하면서 파일을 구분하고 관리하는 것이 점점 어려워졌다.

이를 해결하기 위해 계층적 디렉토리 구조(Hierarchical Directory Structure)가 사용되기 시작했다. 트리 구조(Tree Structure)가 도입되면서 하나의 디렉토리 안에 하위 디렉토리(Subdirectory)를 포함할 수 있게 되었고, 파일을 주제나 목적에 따라 체계적으로 분류하고 관리할 수 있게 되었다.

또한 그래프 구조(Graph Structure)에서는 하나의 파일을 여러 위치에서 참조할 수 있다. 이를 통해 파일 공유(File Sharing)가 가능하지만, 여러 경로에서 같은 파일을 참조하기 때문에 관리가 더 복잡해질 수 있다.


Directory Structure 2(디렉토리 구조 2)

디렉토리가 어떻게 구성되고 연결되는지 살펴보자. 디렉토리 구조는 루트 디렉토리(Root Directory)에서 시작되며, 그 아래에 여러 하위 디렉토리(Subdirectory)와 파일(File)이 연결되는 형태로 구성된다.

디렉토리(Directory)는 단순히 이름만 저장하는 것이 아니라, 파일 이름(File Name)이나 하위 디렉토리 이름과 함께 해당 대상을 가리키는 아이노드 번호(Inode Number)를 함께 저장한다.

운영체제(Operating System)는 이 아이노드 번호를 따라가면서 파일의 실제 정보와 저장 위치를 확인하고, 최종적으로 데이터 블록(Data Block)에 접근한다. 여기서 아이노드 번호(Inode Number)는 파일의 정보와 위치를 찾기 위해 사용하는 고유한 식별자이다.

예를 들어 디렉토리 안에 p3라는 항목이 있고, 그 옆에 5067과 같은 값이 저장되어 있다면, 이 값은 해당 파일이나 디렉토리를 가리키는 아이노드 번호이다. 운영체제는 이 번호를 이용해 해당 파일의 메타데이터(Metadata)와 데이터 위치를 찾아간다.

또한 데이터 블록(Data Block)은 실제 파일 내용이 저장되어 있는 디스크 공간을 의미한다. 디렉토리는 파일 이름과 아이노드 번호를 연결하고, 아이노드는 실제 데이터 블록의 위치를 관리한다고 이해할 수 있다.

각 디렉토리에는 ... 항목도 존재한다. .은 현재 디렉토리(Current Directory)를 의미하고, ..은 부모 디렉토리(Parent Directory)를 의미한다. 이를 통해 사용자는 계층 구조 안에서 현재 위치를 기준으로 이동할 수 있다.

정리하면, 디렉토리는 파일 이름과 실제 저장 위치를 연결하고, 계층적인 파일 탐색을 가능하게 하는 구조이다.

📌디렉토리(Directory)는 보통 “파일 이름 → 아이노드 번호(Inode Number)”를 저장한다. 반면 아이노드(Inode)는 파일 크기, 권한, 시간 정보 같은 메타데이터(Metadata)와 실제 데이터 블록(Data Block)의 위치 정보를 저장한다.

즉, 파일 접근 과정은 보통 파일 이름 → 디렉토리 → 아이노드 → 데이터 블록의 흐름으로 이해하면 좋다.


Tree-Structured Directory(트리 구조 디렉토리)

트리 구조 디렉토리(Tree-Structured Directory)는 루트 디렉토리(Root Directory)를 기준으로 하위 디렉토리(Subdirectory)가 계층적으로 확장되는 구조이다.

하나의 디렉토리(Directory) 아래에는 여러 개의 하위 디렉토리와 파일(File)이 포함될 수 있다. 이러한 구조가 반복되면서 전체 파일 시스템(File System)이 구성된다.

트리 구조 디렉토리는 파일을 목적별, 사용자별, 기능별로 구분하여 체계적으로 관리할 수 있게 해준다. 따라서 파일의 탐색(File Search)과 관리 효율이 높아 일반적으로 많이 사용되는 디렉토리 구조이다.

Tree-Structured Directory Diagram(트리 구조 디렉토리 그림 설명)

그림을 보면 루트 디렉토리(Root Directory)를 기준으로 여러 사용자 디렉토리(User Directory)가 나뉘어 있는 것을 확인할 수 있다.

각 사용자 디렉토리 안에는 다시 하위 디렉토리(Subdirectory)와 파일(File)이 포함되어 있다. 이러한 구조가 반복되면서 전체 파일 시스템(File System)이 트리 구조(Tree Structure) 형태로 구성된다.

트리 구조 디렉토리(Tree-Structured Directory)는 경로(Path)를 따라 파일을 탐색할 수 있게 해준다. 또한 파일을 사용자별, 목적별, 종류별로 체계적으로 분류할 수 있기 때문에 파일 관리가 효율적이다.

따라서 트리 구조는 현재 가장 일반적으로 사용되는 디렉토리 구조라고 할 수 있다.

트리 구조에서는 파일의 위치를 경로(Path)로 표현한다. 예를 들어 /user1/report.txt와 같은 경로는 루트 디렉토리(Root Directory)에서 시작해 user1 디렉토리 안의 report.txt 파일에 접근한다는 의미이다.


Graph-Structured Directory(그래프 구조 디렉토리)

그래프 구조 디렉토리(Graph-Structured Directory)는 하나의 파일(File)이나 디렉토리(Directory)를 여러 경로(Path)에서 참조할 수 있는 구조이다.

즉, 동일한 파일을 여러 위치에서 공유(File Sharing)할 수 있다는 특징이 있다. 이를 통해 파일의 중복 저장을 줄이고, 데이터를 더 효율적으로 활용할 수 있다.

하지만 그래프 구조는 트리 구조(Tree Structure)보다 관리가 복잡하다. 특히 순환 구조(Cycle)가 발생하면 파일 탐색(File Search) 과정에서 같은 경로를 반복해서 따라가게 되어 무한 반복(Infinite Loop) 문제가 생길 수 있다.

따라서 실제 시스템에서는 순환이 발생하지 않도록 제한한 비순환 그래프(Acyclic Graph) 형태로 사용하는 경우가 많다.


Graph-Structured Directory Diagram(그래프 구조 디렉토리 그림 설명)

그래프 구조 디렉토리(Graph-Structured Directory)는 하나의 파일(File)이나 디렉토리(Directory)가 여러 경로(Path)를 통해 연결될 수 있는 구조이다. 즉, 하나의 파일이 두 개 이상의 디렉토리에서 참조되면서 동일한 데이터를 여러 위치에서 사용할 수 있다.

그림에서 실선은 기본적인 디렉토리 계층 구조(Hierarchical Structure)를 나타내고, 점선은 추가적인 참조(Reference)나 공유 관계(Sharing Relationship)를 의미한다.

예를 들어 order 아래의 mail, expo는 일반적인 하위 디렉토리 관계로 실선으로 연결되어 있다. 반면 copy는 다른 방식으로 연결되어 있으며, copy 아래의 항목은 실선뿐만 아니라 program, expo와도 점선으로 연결되어 있다.

이처럼 그래프 구조에서는 하나의 대상이 여러 위치에서 참조될 수 있고, 경우에 따라 순환 관계(Cycle)가 발생할 수도 있다. 순환 관계가 생기면 파일 탐색(File Search) 과정에서 반복이 발생하거나, 디렉토리 구조 관리가 복잡해질 수 있다.

따라서 그래프 구조 디렉토리는 파일 공유(File Sharing)에는 유리하지만, 탐색과 관리 측면에서는 추가적인 고려가 필요한 구조라고 이해할 수 있다.


UNIX File System Structure(UNIX 파일 시스템 구조)

이제 파일 시스템(File System)이 디스크(Disk) 내부에서 어떻게 구성되는지 살펴본다.

UNIX 파일 시스템(UNIX File System)은 하나의 공간으로 구성되는 것이 아니라, 여러 영역으로 나뉘어 관리된다. 대표적으로 부트 블록(Boot Block), 슈퍼블록(Superblock), 아이노드 영역(Inode Area), 데이터 블록(Data Block)으로 구성되며, 각 영역은 서로 다른 역할을 수행한다.

이처럼 기능별로 영역을 분리해서 관리하면 파일 시스템의 효율성(Efficiency)과 안정성(Stability)을 높일 수 있다.

이 구조에서 가장 중요한 개념은 아이노드(Inode)이다. 아이노드는 파일의 실제 데이터 자체를 저장하는 공간이 아니라, 파일의 메타데이터(Metadata)와 데이터 위치 정보(Location Information)를 저장하는 구조이다.

예를 들어 아이노드에는 파일 크기(File Size), 접근 권한(Access Permission), 소유자 정보(Owner Information) 등이 포함된다. 실제 파일 데이터는 아이노드가 가진 포인터(Pointer)를 통해 데이터 블록(Data Block)에 접근하여 찾을 수 있다.

즉, 파일 이름(File Name)은 디렉토리(Directory)가 관리하고, 아이노드(Inode)는 파일의 정보와 실제 데이터 위치를 관리한다고 이해하면 된다.

UNIX/Linux 계열에서는 일반적으로 디렉토리(Directory)가 “파일 이름(File Name) → 아이노드 번호(Inode Number)”를 연결하고, 아이노드(Inode)가 “파일 정보(Metadata) → 데이터 블록(Data Block) 위치”를 연결한다.

따라서 파일 접근 흐름은 보통 다음과 같이 이해할 수 있다.

파일 이름(File Name) → 디렉토리(Directory) → 아이노드(Inode) → 데이터 블록(Data Block)


Inode Internal Structure(아이노드 내부 구조)

아이노드(Inode) 내부에는 여러 개의 포인터(Pointer)가 포함되어 있다. 이 포인터들은 실제 파일 데이터가 저장된 데이터 블록(Data Block)을 가리키는 역할을 한다.

먼저 직접 블록 포인터(Direct Block Pointer)는 데이터 블록을 바로 가리키는 구조이다. 파일 크기가 작은 경우에는 직접 블록 포인터만으로도 충분히 데이터를 찾을 수 있다.

하지만 파일 크기가 커지면 직접 포인터만으로는 모든 데이터 블록을 가리키기 어렵다. 이때 간접 포인터(Indirect Pointer)가 사용된다.

일중 간접 포인터(Single Indirect Pointer)는 인덱스 블록(Index Block)을 한 번 거쳐 데이터 블록에 접근한다. 이중 간접 포인터(Double Indirect Pointer)와 삼중 간접 포인터(Triple Indirect Pointer)는 인덱스 블록을 여러 단계 거쳐 더 많은 데이터 블록을 연결할 수 있다.

즉, 아이노드는 파일 크기에 따라 직접 포인터(Direct Pointer), 일중 간접 포인터(Single Indirect Pointer), 이중 간접 포인터(Double Indirect Pointer), 삼중 간접 포인터(Triple Indirect Pointer)를 사용해 확장된다. 이를 통해 작은 파일은 빠르게 접근하고, 큰 파일은 효율적으로 저장할 수 있다.

직접 포인터(Direct Pointer)는 데이터 블록을 바로 가리키기 때문에 접근 속도가 빠르다. 반면 간접 포인터(Indirect Pointer)는 중간에 인덱스 블록(Index Block)을 거치므로 접근 단계가 늘어나지만, 훨씬 큰 파일을 저장할 수 있게 해준다.


Actual File Access Process(파일 접근의 실제 동작)

실제 파일이 어떤 방식으로 접근되는지 살펴보자. 사용자는 파일 이름(File Name)으로 파일에 접근하지만, 운영체제(Operating System) 내부에서는 다른 방식으로 처리된다.

파일 이름은 주로 디렉토리(Directory)에서 사용된다. 운영체제는 디렉토리에서 파일 이름을 찾고, 해당 파일과 연결된 아이노드(Inode)를 확인한다. 실제 파일의 정보와 저장 위치는 이 아이노드를 통해 결정된다.

또한 프로그램(Process)은 파일 이름을 계속 사용하는 것이 아니라, 파일을 열 때 얻은 파일 디스크립터(File Descriptor, FD)를 기준으로 파일에 접근한다. 파일 디스크립터는 열린 파일을 식별하기 위해 운영체제가 제공하는 내부 번호이다.

즉, 사용자는 파일 이름으로 접근한다고 생각하지만, 운영체제 내부에서는 디렉토리(Directory), 아이노드(Inode), 파일 디스크립터(File Descriptor)를 통해 파일 접근이 이루어진다. 사용자가 보는 방식과 운영체제가 처리하는 방식이 다르다는 점이 핵심이다.

일반적으로 파일 접근 흐름은 다음과 같이 이해할 수 있다.

파일 이름(File Name) → 디렉토리(Directory) → 아이노드(Inode) → 파일 디스크립터(File Descriptor) → 데이터 블록(Data Block)


File Access Flow(파일 접근 흐름)

실제 파일 접근 흐름은 먼저 파일 이름(File Name)을 기준으로 디렉토리(Directory)를 탐색하는 것에서 시작된다. 운영체제(Operating System)는 디렉토리에서 해당 파일 이름과 연결된 아이노드 번호(Inode Number)를 찾는다.

그다음 아이노드(Inode)를 통해 파일의 정보와 실제 데이터 위치를 확인한다. 아이노드에는 파일의 메타데이터(Metadata)와 데이터 블록(Data Block)을 가리키는 위치 정보가 저장되어 있다.

마지막으로 운영체제는 해당 데이터 블록(Data Block)에 접근하여 읽기(Read) 또는 쓰기(Write) 작업을 수행한다.

더 정확히는 파일 이름과 아이노드가 직접 연결되는 것이 아니라, 디렉토리(Directory)가 중간에서 파일 이름과 아이노드 번호(Inode Number)를 연결한다.

따라서 실제 흐름은 파일 이름(File Name) → 디렉토리(Directory) → 아이노드(Inode) → 데이터 블록(Data Block)으로 이해하면 더 정확하다.


File Protection and Access Control(파일 보호와 접근 제어)

여러 사용자가 하나의 파일을 함께 사용할 경우, 누구나 자유롭게 수정할 수 있다면 데이터가 덮어씌워지거나 삭제되는 문제가 발생할 수 있다. 그래서 운영체제(Operating System)는 파일을 보호하고, 사용자별로 접근을 제어하는 기능을 제공한다.

파일 보호(File Protection)는 허가되지 않은 접근을 막아 데이터를 안전하게 유지하는 기능이다. 특히 여러 사용자가 동시에 파일을 사용하는 환경에서는 파일 보호 기능이 필수적이다.

접근 제어(Access Control)는 사용자별로 읽기(Read), 쓰기(Write), 실행(Execute)과 같은 작업 권한을 구분하는 역할을 한다. 예를 들어 A 사용자는 파일을 읽기만 가능하고, B 사용자는 파일을 수정할 수 있도록 다르게 설정할 수 있다.

접근 제어 방식에는 ACL(Access Control List) 방식과 그룹 기반 방식(Group-Based Access Control)이 있다. ACL 방식은 사용자별로 세밀한 권한 설정이 가능하지만, 관리가 복잡해질 수 있다. 반면 그룹 기반 방식은 여러 사용자를 그룹으로 묶어 권한을 관리하기 때문에 상대적으로 단순하고 효율적이다.

리눅스/유닉스(Linux/Unix)에서는 파일 권한을 보통 소유자(User), 그룹(Group), 기타 사용자(Others)로 나누어 관리한다. 각 대상에 대해 읽기(Read), 쓰기(Write), 실행(Execute) 권한을 설정할 수 있다.


UNIX Permission Structure(UNIX 권한 구조)

유닉스(UNIX)에서는 파일 접근 권한(File Permission)을 사용자 종류에 따라 구분해서 관리한다. 사용자는 크게 소유자(User/Owner), 그룹(Group), 기타 사용자(Others)로 나뉜다.

권한은 보통 -rwx rwx rwx와 같은 형태로 표현된다. 맨 앞의 문자는 파일 종류(File Type)를 의미한다. 일반 파일이면 -, 디렉토리(Directory)이면 d로 표시된다.

그 뒤의 권한은 세 묶음으로 나뉜다. 첫 번째 묶음은 소유자(User/Owner), 두 번째 묶음은 그룹(Group), 세 번째 묶음은 기타 사용자(Others)에 대한 권한을 나타낸다.

각 권한에서 r은 읽기(Read), w는 쓰기(Write), x는 실행(Execute)을 의미한다. 예를 들어 rwx r-x r--라고 표시되어 있다면, 소유자는 읽기, 쓰기, 실행이 모두 가능하다. 그룹 사용자는 읽기와 실행만 가능하고, 기타 사용자는 읽기만 가능하다는 뜻이다.

이처럼 유닉스는 사용자 그룹을 구분하고, 읽기(Read), 쓰기(Write), 실행(Execute) 권한을 조합하여 파일 접근을 제어한다. 지금까지는 파일 시스템(File System)의 구조, 파일 접근 방식, 그리고 접근 권한까지의 전체 흐름을 살펴보았다.

디렉토리(Directory)에서 실행 권한(Execute Permission)은 파일 실행과는 의미가 다르다. 디렉토리에 x 권한이 있어야 그 디렉토리 안으로 들어가거나 내부 파일에 접근할 수 있다.


Need for File Storage Structure(파일 저장 구조가 필요한 이유)

파일 저장 구조(File Storage Structure)가 필요한 이유는 디스크(Disk)가 항상 연속된 공간을 보장하지 않기 때문이다. 메모리(Memory)처럼 파일을 하나의 연속된 덩어리로 저장하기 어려운 경우가 많다.

그래서 파일(File)은 여러 개의 블록(Block)으로 나뉘어 디스크의 서로 다른 위치에 저장될 수 있다. 또한 파일이 생성(Create), 삭제(Delete), 확장(Extend)되는 과정에서 블록의 배치도 계속 달라질 수 있다.

이처럼 파일의 저장 위치가 계속 변하기 때문에, 운영체제(Operating System)가 이를 관리하지 않으면 파일 위치를 추적하거나 접근하는 것이 어려워진다.

따라서 운영체제는 파일이 어떤 블록에 저장되어 있는지 관리하기 위해 파일 저장 구조(File Storage Structure)를 사용한다.

파일 저장 구조는 파일 할당 방식(File Allocation Method)과 연결된다. 대표적인 방식으로는 연속 할당(Contiguous Allocation), 연결 할당(Linked Allocation), 인덱스 할당(Indexed Allocation)이 있다.


File Storage Structure Diagram(파일 저장 구조 그림 설명)

각 파일이 어떤 블록(Block)에 저장되어 있는지는 파일 테이블(File Table) 형태로 관리할 수 있다.

예를 들어 파일 A(File A)의 블록 번호가 1, 3, 9라면, 파일 A의 데이터가 디스크(Disk)의 1번, 3번, 9번 블록에 나누어 저장되어 있다는 의미이다.

저장장치(Storage Device)를 보면 이러한 블록들이 서로 떨어진 위치에 분산되어 저장될 수 있다. 따라서 파일을 읽을 때는 각 블록의 위치를 따라가며 데이터를 가져와야 한다.

운영체제(Operating System)는 이러한 블록 위치 정보(Block Location Information)를 관리한다. 그리고 분산되어 저장된 데이터를 하나의 연속된 파일(File)처럼 연결해주는 역할을 한다.

즉, 실제 저장은 여러 블록에 나뉘어 분산되어 있지만, 사용자는 하나의 파일처럼 사용할 수 있다. 이것이 파일 저장 구조(File Storage Structure)가 필요한 이유이다.

📌 파일이 여러 블록에 분산되어 저장되는 현상을 단편화(Fragmentation)와 연결해서 이해할 수 있다. 파일 시스템(File System)은 블록 위치를 관리하여 사용자가 단편화된 저장 구조를 직접 신경 쓰지 않아도 되게 한다.


File and Disk Storage Relationship(파일과 디스크 저장의 관계)

이제 파일의 블록(Block)이 실제 디스크(Disk)에서 어떻게 관리되는지 살펴본다.

파일(File)은 사용자 입장에서 하나의 논리적인 순서(Logical Order)를 가진다. 예를 들어 파일은 0, 1, 2, 3과 같은 순서의 블록들로 구성되며, 이를 논리 블록 번호(VBN, Virtual Block Number)라고 한다.

하지만 실제 디스크에서는 이 데이터가 반드시 연속적으로 저장되는 것이 아니다. 파일의 각 블록은 디스크의 여러 물리적인 위치에 나누어 저장될 수 있으며, 이때 실제 디스크 위치를 나타내는 번호를 물리 블록 번호(LBN, Logical Block Number)라고 한다.

즉, 파일 내부에서는 논리 블록 번호(VBN)를 기준으로 순서가 정해지지만, 실제 디스크에서는 물리 블록 번호(LBN)를 기준으로 저장 위치가 결정된다.

파일 시스템(File System)은 이 두 구조를 연결하는 매핑 정보(Mapping Information)를 관리한다. 이를 통해 실제 블록들이 디스크에 흩어져 있어도 사용자는 파일을 논리적으로 연속된 데이터처럼 사용할 수 있다.


File and Disk Storage Relationship 2 (파일과 디스크 저장의 관계 2)

그림을 보면 왼쪽에는 논리적인 파일 구조(Logical File Structure)가 있고, 각 블록은 논리 블록 번호(VBN, Virtual Block Number)를 기준으로 순서가 정해져 있다.

반면 오른쪽의 디스크(Disk)에서는 데이터가 서로 떨어진 물리 블록(Physical Block)에 저장되어 있는 것을 확인할 수 있다. 즉, 파일 안에서는 0, 1, 2, 3처럼 순서가 있어도 실제 디스크에서는 연속된 위치에 저장되지 않을 수 있다.

이때 VBN에서 LBN으로 향하는 화살표는 운영체제(Operating System)가 논리 블록(Logical Block)을 물리 블록(Physical Block)으로 변환하는 매핑(Mapping) 과정을 의미한다.

예를 들어 논리 블록 0번이 디스크의 558번 위치에 저장될 수 있다. 이처럼 논리적인 순서와 실제 저장 위치는 서로 다르게 연결될 수 있다.

따라서 파일을 읽을 때는 단순히 디스크의 연속된 위치를 따라가는 것이 아니라, 운영체제가 매핑 정보(Mapping Information)를 이용해 각 물리 블록을 순서대로 찾아가며 데이터를 읽는다.

VBN(Virtual Block Number)은 파일 안에서의 논리적인 블록 순서를 의미하고, LBN(Logical Block Number)은 실제 저장장치에서 블록이 위치한 주소를 의미한다.

핵심은 사용자는 파일을 연속된 데이터처럼 보지만, 실제 디스크에서는 여러 위치에 나뉘어 저장될 수 있으며 운영체제가 이를 연결해준다는 점이다.


Physical Structure of Disk(디스크의 물리적 구조)

파일이 저장되는 디스크(Disk)의 물리적 구조를 살펴보자. 디스크는 여러 개의 원판(Platter)으로 구성되어 있으며, 각 원판은 트랙(Track)과 섹터(Sector)로 나뉘어 데이터를 저장한다.

실제 데이터는 섹터(Sector) 단위로 저장된다. 디스크에 접근할 때는 원하는 위치로 헤드(Head)가 이동하는 시간과, 원판이 회전하면서 원하는 섹터가 헤드 아래로 올 때까지 기다리는 회전 지연 시간(Rotational Latency)이 발생한다.

즉, 디스크 접근에는 헤드 이동 시간(Seek Time)과 회전 지연 시간(Rotational Latency)이라는 비용이 포함된다. 그래서 디스크에서 데이터를 읽고 쓰는 속도는 단순히 데이터 크기뿐만 아니라, 데이터가 어디에 저장되어 있는지에도 영향을 받는다.

하지만 이러한 물리적인 구조는 사용자에게 직접 보이지 않는다. 파일 시스템(File System)이 디스크 구조를 추상화(Abstraction)해서 관리하기 때문에, 사용자는 복잡한 디스크 구조를 몰라도 파일(File) 단위로 데이터를 쉽게 사용할 수 있다.

이 설명은 전통적인 하드 디스크(HDD, Hard Disk Drive)에 해당한다. SSD(Solid State Drive)는 회전하는 원판이나 헤드가 없기 때문에 헤드 이동 시간(Seek Time)이나 회전 지연 시간(Rotational Latency)이 거의 없다. 다만 운영체제는 HDD든 SSD든 파일 시스템(File System)을 통해 파일을 논리적인 단위로 관리한다.


Disk Block and File Storage Structure(디스크 블록과 파일 저장 구조)

디스크(Disk)는 실제로 섹터(Sector) 단위로 데이터를 저장하지만, 파일 시스템(File System)은 여러 섹터를 묶어 블록(Block) 단위로 관리한다. 이 블록은 파일 저장(File Storage)과 입출력(Input/Output)의 기본 단위가 된다.

운영체제(Operating System)는 데이터를 한 바이트(Byte)씩 처리하기보다는 블록 단위로 읽고 쓴다. 디스크 특성상 작은 단위로 여러 번 접근하는 것보다, 더 큰 단위로 한 번에 읽고 쓰는 것이 효율적이기 때문이다.

블록 안에는 파일 데이터(File Data)가 저장된다. 필요한 경우에는 다음 블록의 위치 정보(Location Information)가 포인터(Pointer) 형태로 포함될 수도 있다. 이는 파일의 여러 블록을 서로 연결해 관리하기 위한 방식이다.

블록 크기(Block Size)는 시스템에서 미리 정해진 값으로 사용되며, 보통 1KB, 2KB, 4KB처럼 2의 거듭제곱 형태로 설정된다. 즉, 파일 시스템은 디스크의 물리적인 섹터를 그대로 사용하는 것이 아니라, 여러 섹터를 묶은 블록 단위로 파일을 저장하고 관리한다.

블록 크기(Block Size)가 너무 작으면 관리해야 할 블록 수가 많아져 오버헤드(Overhead)가 커질 수 있다. 반대로 블록 크기가 너무 크면 작은 파일을 저장할 때 남는 공간이 생겨 내부 단편화(Internal Fragmentation)가 발생할 수 있다.


File and Block Relationship(파일과 블록의 관계)

정리하면 하나의 파일(File)은 여러 개의 블록(Block)으로 구성된다. 이러한 블록들은 디스크(Disk)상에서 항상 연속적으로 저장되는 것이 아니라, 서로 떨어진 위치에 저장될 수도 있다.

파일의 크기(File Size)에 따라 필요한 블록의 개수가 결정된다. 파일이 작으면 적은 수의 블록만 필요하고, 파일이 커질수록 더 많은 블록이 필요하다.

파일의 읽기(Read)와 쓰기(Write) 작업도 모두 블록 단위로 이루어진다. 운영체제(Operating System)는 파일을 바이트(Byte) 단위로 하나씩 처리하기보다는, 블록 단위로 데이터를 읽고 쓰는 방식으로 관리한다.

이때 블록의 배치 방식(Block Allocation Method)에 따라 파일 접근 성능(File Access Performance)이 달라질 수 있다. 예를 들어 블록이 연속적으로 배치되어 있으면 빠르게 접근할 수 있지만, 여러 위치에 흩어져 있으면 접근 속도가 느려질 수 있다.

접근 속도가 느려지는 이유는 디스크 접근 횟수가 늘어날 수 있기 때문이다. 특히 HDD(Hard Disk Drive)에서는 헤드 이동 시간(Seek Time)이 발생하기 때문에 성능에 영향을 줄 수 있다. 반면 SSD(Solid State Drive)는 물리적인 헤드 이동이 없어서 흩어진 블록에 접근할 때의 영향이 HDD보다 상대적으로 작다.

따라서 이후에 살펴볼 파일 할당 방식(File Allocation Method)에서는 블록을 어떻게 배치하고 관리할 것인지가 중요한 요소가 된다.


Characteristics of Block-Based Storage(블록 기반 저장의 특징)

디스크(Disk)는 데이터를 블록(Block) 단위로 저장하고 관리한다. 이러한 블록 기반 저장 방식에는 몇 가지 특징이 있다.

먼저 파일의 크기(File Size)가 블록 크기(Block Size)보다 작은 경우, 블록 내부에 사용되지 않는 공간이 발생할 수 있다. 이를 내부 단편화(Internal Fragmentation)라고 한다.

내부 단편화(Internal Fragmentation)는 할당된 블록 안에서 남는 공간이 생기는 문제이다. 예를 들어 블록 크기가 4KB인데 파일 크기가 1KB라면, 나머지 3KB는 사용되지 않는 공간으로 남을 수 있다.

하지만 블록 단위로 데이터를 처리하면 디스크 입출력(Disk I/O)을 한 번에 수행할 수 있기 때문에, 전체적인 입출력 성능은 효율적으로 향상될 수 있다.

또한 파일 시스템(File System)은 각 블록의 위치 정보를 메타데이터(Metadata) 형태로 관리한다. 따라서 블록이 어떻게 배치되어 있는지에 따라 파일 접근 속도(File Access Speed)가 달라질 수 있다.


Key Design Issues in File Storage Structure(저장 구조 설계의 핵심 문제)

파일을 여러 블록(Block)으로 나누어 저장할 때는 이 블록들을 어떤 방식으로 배치할 것인지 결정해야 한다. 이것이 파일 저장 구조(File Storage Structure) 설계의 핵심 문제이다.

블록을 연속적으로 배치하면 파일 접근 속도(File Access Speed)는 빨라질 수 있다. 하지만 연속된 빈 공간을 확보해야 하므로 저장 공간 효율(Storage Efficiency)이 떨어질 수 있다.

반대로 블록을 흩어진 위치에 저장하면 디스크 공간을 더 유연하게 사용할 수 있지만, 파일을 읽을 때 여러 위치를 따라가야 하므로 접근 속도가 느려질 수 있다.

즉, 파일 저장 구조에는 성능(Performance)과 저장 효율(Efficiency) 사이의 트레이드 오프(Trade-off)가 존재한다.

또한 파일 시스템(File System)은 내부 단편화(Internal Fragmentation)를 줄이고, 파일 크기 변화에도 유연하게 대응할 수 있어야 한다. 결국 파일 시스템 설계에서는 성능, 효율, 유연성(Flexibility) 사이의 균형을 맞추는 것이 중요하다.

이러한 설계 문제를 해결하기 위해 파일 시스템은 연속 할당(Contiguous Allocation), 연결 할당(Linked Allocation), 인덱스 할당(Indexed Allocation)과 같은 파일 할당 방식(File Allocation Method)을 사용한다. 더 자세한 내용은 앞서 배워볼 예정이다.


Overview of File Allocation Methods(파일 할당 방식의 개요)

파일 할당 방식(File Allocation Method)에서 중요한 것은 파일을 구성하는 블록(Block)을 디스크(Disk)에 어떻게 배치할 것인가이다.

즉, 파일 할당 방식은 파일의 여러 블록을 디스크의 어느 위치에 저장하고, 이 블록들을 어떻게 연결해서 관리할지를 결정하는 방법이다.

할당 방식에 따라 파일 접근 속도(File Access Speed)와 디스크 공간 활용 방식(Disk Space Utilization)이 달라진다. 따라서 파일 할당 방식은 파일 시스템(File System)의 성능을 결정하는 중요한 개념이다.

대표적인 파일 할당 방식(File Allocation Method)에는 연속 할당(Contiguous Allocation), 연결 할당(Linked Allocation), 인덱스 할당(Indexed Allocation)이 있다. 각각 접근 속도, 저장 효율, 파일 크기 변화 대응 방식에서 차이가 있다.


Contiguous Allocation(연속 할당)

연속 할당(Contiguous Allocation)은 파일을 디스크(Disk)의 하나의 연속된 구간에 저장하는 방식이다. 가장 직관적이고 단순한 파일 할당 방식(File Allocation Method)이라고 할 수 있다.

이 방식에서는 파일의 시작 위치(Start Block)와 길이(Length)만 알면 전체 데이터를 쉽게 찾을 수 있다. 따라서 파일의 저장 구조가 단순하고 관리하기 쉽다.

또한 데이터 블록(Block)이 연속적으로 배치되기 때문에 파일 접근 속도(File Access Speed)가 빠르다는 장점이 있다. 특히 파일을 순서대로 읽는 순차 접근(Sequential Access)에 효율적이다.


Contiguous Allocation Structure(연속 할당의 구조)

연속 할당(Contiguous Allocation)의 구조를 그림으로 살펴보면, 디렉토리(Directory)에 파일 이름(File Name), 시작 블록(Start Block), 길이(Length)가 함께 저장되어 있는 것을 확인할 수 있다.

예를 들어 CTR.doc 파일의 시작 블록이 1이고 길이가 4라면, 디스크(Disk)의 1번 블록부터 4개의 연속된 블록을 이 파일이 사용한다는 의미이다.

오른쪽 디스크 구조를 보면 파일이 중간에 끊기지 않고 연속된 공간을 차지하고 있다. 이처럼 연속 할당은 파일을 시작 위치와 길이로 관리하며, 하나의 연속된 구간에 저장하는 방식이다.

연속 할당은 시작 블록과 길이만 알면 원하는 위치를 계산하기 쉽기 때문에 직접 접근(Direct Access)에 유리하다. 예를 들어 파일의 세 번째 블록에 접근하려면 시작 블록 + 2처럼 계산할 수 있다.


Characteristics of Contiguous Allocation(연속 할당의 특징)

연속 할당(Contiguous Allocation)은 데이터 블록(Block)이 디스크(Disk)에 연속적으로 저장되는 방식이다. 따라서 디스크 헤드 이동(Disk Head Movement)이 최소화되어 높은 입출력 성능(I/O Performance)을 보일 수 있다.

이 방식은 파일을 처음부터 순서대로 읽는 순차 접근(Sequential Access)에도 효율적이고, 특정 위치를 바로 찾아가는 직접 접근(Direct Access)에도 효율적이다.

하지만 단점도 존재한다. 파일을 삭제하거나 이동하는 과정에서 디스크 중간중간에 빈 공간이 생기면서 단편화(Fragmentation) 문제가 발생할 수 있다. 이로 인해 큰 파일을 저장할 때 필요한 연속된 공간을 확보하기 어려워질 수 있다.

또한 파일 크기(File Size)가 증가했을 때 뒤쪽에 연속된 빈 공간이 없다면, 파일 전체를 다른 위치로 이동해야 하는 문제가 생길 수 있다.

그래서 실제 파일 시스템(File System)에서는 연속 할당을 그대로 사용하기보다는 익스텐트(Extent) 기반 방식으로 변형해서 사용하는 경우가 많다. 이 부분은 이후에 다시 살펴보자

📌익스텐트(Extent)는 여러 개의 연속된 블록을 하나의 묶음으로 관리하는 방식이다. 즉, 파일 전체가 하나의 연속 구간에 있어야 하는 것은 아니지만, 가능한 한 연속된 블록 묶음 단위로 저장하여 성능과 유연성을 함께 확보하려는 방식이다.


Linked Allocation(연결 할당)

연결 할당(Linked Allocation)은 연속 할당(Contiguous Allocation)의 단점을 보완하기 위한 방식이다. 이 방식은 파일을 여러 블록(Block)으로 나누어 저장하고, 각 블록을 포인터(Pointer)로 연결하여 하나의 파일로 구성한다.

각 블록에는 다음 블록의 위치 정보(Location Information)가 포함되어 있다. 운영체제(Operating System)는 이 포인터를 따라가면서 파일 데이터를 순서대로 읽는다.

연결 할당은 파일을 반드시 연속된 공간에 저장할 필요가 없다. 따라서 디스크(Disk)의 흩어진 빈 공간을 활용할 수 있고, 파일 크기(File Size)가 증가할 때도 비교적 유연하게 확장할 수 있다.

연결 할당은 순차 접근(Sequential Access)에는 적합하지만, 특정 위치로 바로 이동하는 직접 접근(Direct Access)에는 불리하다. 중간 블록에 접근하려면 첫 블록부터 포인터를 따라가야 하기 때문이다.


Linked Allocation Diagram(연결 할당 그림 설명)

그림을 보면 하나의 파일(File)이 연속된 공간이 아니라 여러 디스크 블록(Disk Block)에 나누어 저장되어 있는 것을 확인할 수 있다.

예를 들어 COT.doc 파일의 시작 블록(Start Block)이 18번이라고 하면, 18번 블록에는 다음 블록인 19번의 위치 정보가 저장되어 있다. 19번 블록으로 이동하면 다시 2번 블록으로 연결된다.

2번 블록에는 더 이상 다음 블록을 가리키는 연결 정보가 없으므로, 이 지점이 파일의 끝(End of File)을 의미한다. 즉, 이 파일은 18 → 19 → 2 순서로 구성되어 있다고 볼 수 있다.

이처럼 연결 할당(Linked Allocation)은 각 블록이 다음 블록의 위치를 포인터(Pointer)로 가지고 있기 때문에, 처음 블록만 알면 나머지 블록들을 순서대로 따라가며 파일을 읽을 수 있다.

따라서 파일은 물리적으로는 디스크 여러 위치에 흩어져 저장되어 있지만, 논리적으로는 하나의 파일처럼 동작한다.

연결 할당(Linked Allocation)은 연속된 빈 공간이 필요 없기 때문에 저장 공간 활용에는 유리하다. 하지만 중간 블록에 바로 접근하려면 앞에서부터 포인터를 따라가야 하므로 직접 접근(Direct Access) 성능은 떨어질 수 있다.


Characteristics of Linked Allocation(연결 할당의 특징)

연결 할당(Linked Allocation)은 파일을 앞에서부터 순서대로 읽는 순차 접근(Sequential Access)에서는 큰 문제가 없다. 각 블록(Block)의 포인터(Pointer)를 따라가며 차례대로 읽으면 되기 때문이다.

하지만 파일의 중간 위치에 바로 접근하려면 문제가 생긴다. 원하는 블록으로 바로 이동할 수 없고, 처음 블록부터 순서대로 포인터를 따라가야 한다. 이러한 방식을 포인터 체이싱(Pointer Chasing)이라고 한다.

또한 블록들이 디스크(Disk) 전체에 흩어져 저장될 수 있기 때문에, 파일을 읽을 때마다 디스크 헤드 이동(Disk Head Movement)이 자주 발생할 수 있다. 이로 인해 탐색 비용(Seek Cost)이 증가하고 접근 속도가 느려질 수 있다.

중간 블록이 손상될 경우에도 문제가 발생한다. 해당 블록 이후의 연결 정보가 끊어지면 파일의 일부를 읽지 못할 수 있다.

이러한 단점을 보완하기 위해 포인터 정보를 각 블록 안에 저장하지 않고, 한곳에 모아서 관리하는 FAT(File Allocation Table) 방식이 사용되기도 한다.

FAT(File Allocation Table)는 파일의 각 블록이 다음에 어떤 블록으로 이어지는지를 별도의 테이블에 저장하는 방식이다. 연결 할당의 기본 개념은 유지하되, 블록 연결 정보를 한곳에서 관리한다는 점이 특징이다.


Indexed Allocation(인덱스 할당)

앞서 연속 할당(Contiguous Allocation)연결 할당(Linked Allocation)을 살펴보았다. 연속 할당은 접근 속도가 빠르지만 연속된 공간을 확보하기 어렵고, 연결 할당은 파일 크기 변화에 유연하지만 직접 접근 속도가 느리다는 문제가 있다.

이러한 단점을 보완하기 위해 사용하는 방식이 인덱스 할당(Indexed Allocation)이다.

인덱스 할당은 데이터 블록(Data Block)을 서로 직접 연결하지 않고, 별도의 인덱스 블록(Index Block)에 각 데이터 블록의 위치 정보를 모아서 저장하는 방식이다.

파일을 읽을 때는 블록을 처음부터 순차적으로 따라가는 것이 아니라, 인덱스 블록을 통해 원하는 데이터 블록의 위치를 바로 확인할 수 있다.

따라서 인덱스 할당은 연결 할당보다 직접 접근(Direct Access)에 유리하고, 파일이 디스크의 여러 위치에 나뉘어 저장되어도 관리가 비교적 효율적이다.

인덱스 할당(Indexed Allocation)은 인덱스 블록(Index Block)을 추가로 사용하기 때문에, 파일마다 인덱스 정보를 저장하기 위한 별도의 공간이 필요하다. 즉, 접근 성능은 좋아지지만 인덱스 블록을 위한 저장 공간 오버헤드(Overhead)가 발생할 수 있다.


Indexed Allocation Structure(인덱스 할당 구조)

그림을 보면 디렉토리(Directory)파일 이름(File Name)과 함께 인덱스 블록(Index Block)의 위치가 저장되어 있다.

예를 들어 test 파일의 인덱스 블록이 8번이라면, 8번 블록에는 18, 20, 5, 7, 14, 9와 같이 실제 데이터 블록(Data Block)의 위치가 목록 형태로 저장되어 있다.

마지막 값이 -1인 경우에는 더 이상 연결된 블록이 없다는 의미이며, 파일의 끝(End of File)을 나타내는 표시로 이해할 수 있다.

이처럼 인덱스 블록(Index Block)은 파일을 구성하는 데이터 블록의 위치를 한곳에 모아서 관리한다. 따라서 실제 데이터가 디스크(Disk)에 흩어져 있어도 필요한 블록의 위치를 바로 확인할 수 있다.

즉, 인덱스 할당(Indexed Allocation)은 연결 할당(Linked Allocation)처럼 블록을 순차적으로 따라갈 필요 없이, 인덱스 블록을 통해 원하는 데이터 블록에 직접 접근할 수 있는 방식이다.

인덱스 할당(Indexed Allocation)은 직접 접근(Direct Access)에 유리하지만, 인덱스 블록(Index Block)을 따로 저장해야 하므로 추가 저장 공간이 필요하다. 또한 파일이 매우 커지면 하나의 인덱스 블록만으로 모든 데이터 블록 위치를 저장하기 어려울 수 있다.


Characteristics of Indexed Allocation(인덱스 할당 방식의 특징)

인덱스 할당(Indexed Allocation)은 인덱스 블록(Index Block)을 통해 데이터 블록(Data Block)의 위치를 한 번에 확인할 수 있는 방식이다.

이 방식은 원하는 블록의 위치를 바로 찾을 수 있기 때문에 직접 접근(Direct Access)이 가능하다. 따라서 처음 블록부터 순서대로 따라가야 하는 연결 할당(Linked Allocation)보다 접근 성능이 더 빠르다.

또한 데이터 블록들이 디스크(Disk)에 흩어져 있어도 인덱스 블록에 전체 위치 정보가 정리되어 있기 때문에 관리가 비교적 쉽다.

하지만 하나의 인덱스 블록에 저장할 수 있는 주소(Address)의 수에는 제한이 있다. 따라서 큰 파일을 저장할 때는 하나의 인덱스 블록만으로 모든 데이터 블록을 가리키기 어려워질 수 있다.

이를 해결하기 위해 다단계 인덱스 구조(Multi-Level Index Structure)나 유닉스(UNIX)의 아이노드(Inode)처럼 직접 포인터(Direct Pointer)간접 포인터(Indirect Pointer)를 함께 사용하는 방식이 사용된다.

다만 간접 포인터를 사용하는 경우에는 중간의 간접 블록(Indirect Block)을 거쳐야 하므로 추가적인 접근 비용(Access Cost)이 발생할 수 있다.

인덱스 할당(Indexed Allocation)은 연속 할당(Contiguous Allocation)과 연결 할당(Linked Allocation)의 단점을 보완한 방식이다. 직접 접근이 가능하면서도 파일이 반드시 연속된 공간에 저장될 필요가 없기 때문에, 성능과 유연성의 균형을 맞추는 방식이라고 이해하면 좋다.


Comparison of File Allocation Methods(파일 할당 방식 비교)

지금까지 세 가지 파일 할당 방식(File Allocation Method)을 살펴보았다.

연속 할당(Contiguous Allocation)은 구조가 단순하고 파일 접근 속도(File Access Speed)가 빠르다. 하지만 파일을 저장하기 위해 연속된 빈 공간이 필요하기 때문에 디스크 공간 활용(Disk Space Utilization)에 제약이 생길 수 있다.

연결 할당(Linked Allocation)은 파일 블록(Block)을 흩어진 공간에 저장할 수 있어 공간 활용에는 유리하다. 하지만 원하는 블록에 접근하려면 처음부터 포인터(Pointer)를 따라가야 하므로 접근 효율이 떨어질 수 있다.

인덱스 할당(Indexed Allocation)은 데이터 블록의 위치 정보를 인덱스 블록(Index Block)에 모아서 관리한다. 따라서 직접 접근(Direct Access)이 가능하고 구조적으로 균형이 잡혀 있다. 다만 별도의 인덱스 공간(Index Space)이 필요하다는 특징이 있다.

정리하면 연속 할당은 빠르지만 공간 확보가 어렵고, 연결 할당은 유연하지만 접근 속도가 느릴 수 있으며, 인덱스 할당은 접근성과 유연성의 균형을 제공하지만 추가 공간이 필요하다.

파일 할당 방식은 파일 시스템(File System)의 성능과 저장 효율을 결정하는 중요한 요소이다. 실제 파일 시스템에서는 하나의 방식만 단순히 사용하기보다, 익스텐트(Extent), 아이노드(Inode), 인덱스 구조(Index Structure) 등을 조합하여 성능과 효율을 함께 고려하는 경우가 많다.


Design Considerations for File Systems(파일 시스템 설계 고려 요소)

결국 파일 시스템(File System)을 설계할 때는 하나의 방식만 고려하는 것이 아니라 여러 요소를 함께 살펴봐야 한다.

먼저 파일 접근 패턴(File Access Pattern)에 따라 적합한 저장 방식이 달라진다. 파일을 처음부터 순서대로 읽는 순차 접근(Sequential Access)의 비중이 높은지, 특정 위치에 바로 접근하는 직접 접근(Direct Access) 또는 임의 접근(Random Access)의 비중이 높은지에 따라 효율적인 할당 방식이 달라질 수 있다.

또한 디스크 접근 비용(Disk Access Cost)도 중요한 요소이다. 특히 HDD(Hard Disk Drive)에서는 헤드 이동 시간(Seek Time)과 회전 지연 시간(Rotational Latency)이 발생하기 때문에, 블록(Block)이 어떻게 배치되어 있는지가 성능에 영향을 준다.

저장 효율(Storage Efficiency)과 접근 성능(Access Performance) 사이에는 항상 트레이드 오프(Trade-off)가 존재한다. 따라서 실제 파일 시스템에서는 하나의 방식만 사용하는 것이 아니라 여러 방식을 결합한 하이브리드 구조(Hybrid Structure)를 사용하는 경우가 많다.

예를 들어 ext4와 같은 파일 시스템(File System)에서는 연속 할당(Contiguous Allocation)을 확장한 익스텐트(Extent) 방식과 인덱스 구조(Index Structure)를 함께 활용한다. 이를 통해 연속적인 저장의 성능 장점과 큰 파일을 관리하기 위한 유연성을 함께 확보할 수 있다.


File System Operation in Practice(파일 시스템의 실제 동작)

이제 앞에서 살펴본 개념들이 실제 시스템(System)에서 어떻게 동작하는지 살펴본다.

프로그램이 실행되면 파일이 생성(Create)되거나, 기존 파일에 접근(Access)하고, 데이터를 읽고 쓰는 작업(Read/Write)이 이루어진다. 사용자는 파일 이름(File Name)을 기준으로 파일을 사용하지만, 운영체제(Operating System) 내부에서는 디렉토리(Directory), 아이노드(Inode), 파일 디스크립터(File Descriptor), 오프셋(Offset) 등을 통해 파일 접근이 처리된다.

이번 내용에서는 프로그램 실행 과정에서 파일이 어떻게 생성되고 접근되는지, 그리고 이러한 작업이 시스템 내부에서 어떤 구조로 처리되는지를 중심으로 살펴본다.

파일을 open하면 운영체제는 파일 이름을 찾아 아이노드(Inode) 정보를 확인하고, 열린 파일을 관리하기 위해 파일 디스크립터(File Descriptor)를 반환한다. 이후 프로그램은 파일 이름이 아니라 이 파일 디스크립터를 사용해 read, write, close 같은 작업을 수행한다.


File Descriptor(fd)(파일 디스크립터)

실습에서는 먼저 파일 디스크립터(File Descriptor, fd)에 대해 살펴본다.

시스템 내부에서 파일은 파일 이름(File Name)으로 계속 접근되는 것이 아니라, 파일 디스크립터(fd)라는 정수값(Integer Value)을 통해 접근된다.

프로그램이 open 함수를 호출하면 운영체제(Operating System)는 해당 파일을 열고, 그 파일을 식별하기 위한 파일 디스크립터를 반환한다.

이후 readwrite 같은 파일 입출력(File I/O) 연산은 파일 이름이 아니라 fd를 기준으로 수행된다. 즉, 사용자는 파일 이름으로 파일을 구분하지만, 운영체제는 열린 파일을 문자열이 아닌 정수값인 파일 디스크립터로 관리한다고 이해할 수 있다.

파일 디스크립터(fd)는 프로세스(Process)마다 따로 관리된다. 같은 숫자의 fd라도 프로세스가 다르면 서로 다른 파일을 가리킬 수 있다.


Core Code: open() and write()

핵심 코드를 통해 파일 디스크립터(File Descriptor, fd)가 어떻게 사용되는지 살펴보자.

int fd = open("lab1.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);

open() 함수는 파일을 열고, 그 결과로 파일 디스크립터(fd)를 반환한다. 이때 옵션(Option)을 통해 파일을 여는 방식을 결정한다.

O_CREAT는 파일이 없으면 새로 생성하겠다는 의미이다. O_TRUNC는 파일이 이미 존재할 경우 기존 내용을 지우겠다는 의미이다. O_WRONLY는 파일을 쓰기 전용(Write Only)으로 열겠다는 의미이다.

따라서 이 코드는 lab1.txt 파일이 없으면 새로 만들고, 이미 존재한다면 기존 내용을 지운 뒤 쓰기 전용으로 여는 코드이다.

뒤에 있는 0644는 파일 권한(File Permission)을 의미한다. 소유자(Owner)는 읽기(Read)와 쓰기(Write)가 가능하고, 그룹(Group)과 기타 사용자(Others)는 읽기만 가능한 권한이다.

다음으로 write() 함수는 파일 이름이 아니라 fd를 기준으로 데이터를 기록한다.

write(fd, "ABC\n", 4);

여기서 "ABC\n"은 기록할 문자열이고, 4는 총 4바이트(Byte)를 기록한다는 의미이다. 즉 A, B, C 세 글자와 줄바꿈 문자(Newline)까지 포함해 총 4바이트가 파일에 저장된다.

fd는 커널(Kernel)의 열린 파일 테이블(Open File Table)과 연결되어 있기 때문에, 파일의 상태와 현재 위치 정보도 함께 관리된다. 또한 동일한 파일이라도 open()을 다시 호출하면 새로운 fd가 생성된다. 이 부분은 파일 디스크립터의 동작을 이해하는 데 중요한 특징이므로 반드시 기억해야 한다.

📌 O_TRUNC는 “기존 파일을 삭제”하는 것이 아니라, 파일 자체는 유지하되 기존 내용을 비워서 파일 크기를 0으로 만드는 옵션이다.


File Descriptor(fd)(파일 디스크립터)

open() 함수는 파일(File)을 생성하거나 열면서 접근 권한(Access Permission)과 접근 방식(Access Mode)을 함께 설정한다.

예를 들어 O_WRONLY | O_CREAT | O_TRUNC와 같은 옵션(Option)을 사용하면 파일을 쓰기 전용으로 열지, 파일이 없을 때 새로 만들지, 기존 내용을 비울지를 결정할 수 있다.

파일을 열면 운영체제(Operating System)는 해당 파일을 식별하기 위한 파일 디스크립터(File Descriptor, fd)를 생성한다. 이후 프로그램은 파일 이름(File Name)이 아니라 이 fd를 기준으로 파일에 접근한다.

write() 함수는 메모리(Memory)에 있는 데이터를 파일에 기록하는 역할을 한다. 이 과정에서도 파일 이름을 직접 사용하는 것이 아니라, open()을 통해 얻은 파일 디스크립터(fd)를 사용한다.

즉, 파일은 사용자 입장에서는 이름으로 구분되지만, 프로그램 실행 중에는 파일 디스크립터(fd)를 통해 관리되고 접근된다고 이해할 수 있다.


File Descriptor Example(파일 디스크립터 실제 예제)

파일 디스크립터(File Descriptor, fd)의 실제 예제를 살펴보자.

open() 함수는 lab1.txt 파일을 열도록 되어 있다. 이때 O_CREAT 옵션이 있으면 파일이 존재하지 않을 경우 새로 생성하고, O_TRUNC 옵션이 있으면 기존 파일의 내용을 비워 초기화한다.

open() 함수가 실행되면 결과로 파일을 식별하는 정수값(Integer Value)인 fd가 반환된다. 이 fd는 열린 lab1.txt 파일을 가리키는 역할을 한다.

이후 write() 함수는 파일 이름이 아니라 fd를 기준으로 데이터를 기록한다. 즉, lab1.txt라는 이름을 직접 사용하는 것이 아니라, open()을 통해 얻은 fd를 이용해 해당 파일에 데이터를 쓰는 방식이다.

마지막으로 close() 함수를 호출하면 파일 사용이 끝났음을 운영체제(Operating System)에 알리고, 파일과 관련된 자원(Resource)을 반환한다.

정리하면 파일 접근 흐름은 open() → fd 반환 → write(fd) → close(fd) 순서로 이루어진다.

close()를 호출하지 않으면 열린 파일과 관련된 자원이 계속 유지될 수 있다. 따라서 파일 사용이 끝나면 close(fd)를 통해 파일을 닫는 것이 중요하다.


File Descriptor Execution Result(파일 디스크립터 실행 결과)

실행 결과를 보면 fd 값이 3으로 출력된 것을 확인할 수 있다. 이 값은 운영체제(Operating System)가 해당 파일에 할당한 파일 디스크립터 번호(File Descriptor Number)이다.

일반적으로 파일 디스크립터 0, 1, 2는 미리 정해진 용도로 사용된다. 0은 표준 입력(Standard Input), 1은 표준 출력(Standard Output), 2는 표준 오류(Standard Error)를 의미한다.

따라서 프로그램에서 새 파일을 열면 보통 그다음 번호인 3부터 파일 디스크립터가 할당된다고 이해할 수 있다.

이후 write() 함수를 통해 두 개의 문자열이 lab1.txt 파일에 기록된다. 마지막으로 close()가 호출되면서 파일이 정상적으로 닫히고, 관련 자원(Resource)이 반환된다.

생성된 lab1.txt 파일을 열어보면 두 문자열이 순서대로 저장되어 있는 것을 확인할 수 있다. 즉, 프로그램은 파일 이름이 아니라 fd 값을 기준으로 파일에 데이터를 쓰고 닫는 과정을 수행한 것이다.

fd 값이 항상 반드시 3으로 고정되는 것은 아니다. 이미 다른 파일이나 자원이 열려 있다면 4, 5처럼 다른 번호가 할당될 수 있다. 중요한 점은 0, 1, 2가 표준 입출력용으로 예약되어 있고, 새로 여는 파일은 사용 가능한 가장 작은 fd 번호를 받는다는 것이다.


Offset-Based File Access(오프셋 기반 파일 접근)

두 번째 실습에서는 파일의 현재 위치(Current Position), 즉 오프셋(Offset)을 기준으로 파일 접근이 이루어지는 과정을 살펴본다.

write()를 수행하면 현재 오프셋 위치에 데이터가 기록되고, 기록이 끝난 뒤 오프셋은 자동으로 다음 위치로 이동한다. 따라서 별도의 위치를 지정하지 않으면 데이터는 기존 내용 뒤에 계속 이어서 기록된다.

하지만 특정 위치에 데이터를 기록하거나, 파일 중간에 있는 내용을 읽고 싶은 경우에는 lseek() 함수를 사용한다. lseek()는 파일의 오프셋을 원하는 위치로 직접 이동시키는 역할을 한다.

결국 파일의 읽기(Read)와 쓰기(Write) 결과는 현재 오프셋이 어디에 위치해 있는지에 따라 결정된다고 이해할 수 있다.

오프셋(Offset)은 열린 파일 안에서 “다음에 읽거나 쓸 위치”를 의미한다. 같은 파일이라도 파일 디스크립터(File Descriptor, fd)를 어떻게 열었는지에 따라 오프셋이 따로 관리될 수 있다.


File Offset and lseek()(파일 오프셋과 lseek())

핵심 코드에서 먼저 다음과 같이 문자열을 파일에 기록한다.

write(fd, "ABCDEFGH\n", 9);

이 코드는 ABCDEFGH와 줄바꿈 문자(Newline)를 포함해 총 9바이트(Byte)를 파일에 저장한다. 이 상태에서 파일에는 문자열이 순서대로 기록된다.

그다음 lseek() 함수가 사용된다.

lseek(fd, 2, SEEK_SET);

여기서 SEEK_SET은 파일의 시작 위치(Start)를 기준으로 한다는 의미이다. 따라서 이 코드는 파일의 처음부터 2바이트 이동한 위치로 파일 오프셋(Offset)을 옮기는 동작이다.

즉, 파일의 처음으로 돌아가는 것이 아니라 파일 중간 위치로 이동하는 것이다.

이후 다음 코드가 실행된다.

write(fd, "XXX", 3);

이 상태에서 "XXX"를 기록하면 기존 데이터가 뒤로 밀리는 것이 아니라, 현재 오프셋 위치부터 기존 내용을 덮어쓰기(Overwrite) 한다.

처음 파일 내용이 다음과 같았다면,

ABCDEFGH

인덱스 기준으로 A는 0번, B는 1번, C는 2번 위치에 있다. lseek(fd, 2, SEEK_SET)은 2번 위치인 C로 이동한다는 의미이다.

따라서 "XXX"를 쓰면 기존의 CDEXXX로 덮어쓰기 되어 최종 결과는 다음과 같이 된다.

ABXXXFGH

즉, lseek()는 파일의 현재 위치를 이동시키고, 이후의 write()는 그 위치부터 데이터를 덮어쓴다고 이해하면 된다.

lseek(fd, 2, SEEK_SET)에서 2는 “두 번째 글자”가 아니라 “파일 시작 지점에서 2바이트 떨어진 위치”를 의미한다. 문자열 인덱스가 0부터 시작하기 때문에 실제로는 세 번째 문자 위치인 C에 해당한다.


File Offset and lseek() Concept

(파일 오프셋과 lseek()의 개념)

파일(File)에는 항상 현재 접근 위치(Current Position)인 오프셋(Offset)이 존재한다. read()write() 연산은 이 오프셋을 기준으로 수행된다.

파일을 읽거나 쓰면 오프셋은 작업이 끝난 위치 다음으로 자동 이동한다. 따라서 별도로 위치를 지정하지 않으면 파일은 현재 위치부터 계속 이어서 처리된다.

원하는 위치에서 파일을 읽거나 쓰고 싶다면 lseek() 함수를 사용해 오프셋을 직접 이동시킬 수 있다.

lseek()에서 기준이 되는 값에는 SEEK_SET, SEEK_CUR, SEEK_END가 있다. SEEK_SET은 파일의 시작 위치, SEEK_CUR은 현재 위치, SEEK_END는 파일의 끝을 기준으로 오프셋을 이동한다.

중요한 점은 lseek() 자체가 파일 내용을 수정하는 함수가 아니라는 것이다. lseek()는 파일의 접근 위치만 바꾸며, 실제 데이터 변경은 이후에 수행되는 read()write()에 의해 이루어진다.

lseek()는 파일 포인터(File Pointer)의 위치를 이동시키는 함수라고 이해하면 쉽다. 다만 여기서 말하는 포인터는 메모리 주소 포인터가 아니라, 파일 안에서 다음 작업이 이루어질 위치를 의미한다.


Overwriting Data with lseek()(특정 위치 이동 후 데이터 덮어쓰기)

이 예제는 파일의 특정 위치로 이동한 뒤 데이터를 덮어쓰는 과정을 확인하는 코드이다.

먼저 open()을 통해 lab2.txt 파일을 연다. 이후 첫 번째 write()를 수행하면 문자열이 파일에 순서대로 기록된다.

그다음 lseek()를 사용해 파일의 시작 위치(Start)를 기준으로 2번 위치로 이동한다. 이때 오프셋(Offset)이 파일의 중간 위치로 변경된다.

이 상태에서 두 번째 write()를 수행하면 기존 데이터를 뒤로 밀어 넣는 것이 아니라, 현재 오프셋 위치인 2번 위치부터 덮어쓰기(Overwrite)가 이루어진다.

마지막으로 다시 lseek()를 사용해 파일의 처음으로 이동한 뒤, read()를 통해 변경된 파일 내용을 확인한다.

즉, 이 예제는 lseek()로 파일의 접근 위치를 이동시키고, 그 위치에서 write()를 수행하면 기존 데이터가 덮어쓰기 된다는 점을 보여준다.

write()는 삽입(Insert)이 아니라 덮어쓰기(Overwrite) 방식으로 동작한다. 따라서 중간 위치에서 데이터를 쓰면 기존 내용이 뒤로 밀리는 것이 아니라, 해당 위치의 데이터가 새 데이터로 바뀐다.


lseek() Execution Result(lseek() 실행 결과)

파일의 실행 결과를 보면 기존 내용이 ABCDEFGH에서 ABXXFGH로 변경된 것을 확인할 수 있다.

처음에는 파일에 ABCDEFGH가 순서대로 저장되어 있었다. 이후 lseek()를 통해 파일의 시작을 기준으로 2번 위치로 이동한 뒤, XX를 기록하면서 기존의 CD 부분이 덮어쓰기 된 것이다.

여기서 중요한 점은 lseek()가 기존 데이터를 뒤로 밀어내는 기능이 아니라는 것이다. lseek()는 파일의 현재 접근 위치(Offset)를 이동시키는 역할만 한다. 이후 write()가 실행되면 현재 오프셋 위치부터 기존 데이터가 덮어쓰기(Overwrite) 된다.

따라서 파일은 단순히 끝에만 이어서 기록되는 구조가 아니라, 현재 오프셋(Offset)을 기준으로 읽기(Read)와 쓰기(Write)가 수행되는 구조라고 이해할 수 있다.

결과가 ABXXFGH라면 실제로는 2바이트인 XX가 기록된 경우에 해당한다. 만약 2번 위치에서 XXX처럼 3바이트를 기록했다면 기존의 CDE가 덮어쓰기 되어 결과는 ABXXXFGH가 된다.


Descriptor Sharing with dup()(디스크립터 공유)

이번 실습은 파일 디스크립터(File Descriptor, fd)를 어떻게 생성하느냐에 따라 파일 접근 방식이 달라지는 것을 확인하는 내용이다.

먼저 open()을 두 번 호출하면 서로 다른 fd가 생성된다. 이 경우 각 fd는 독립적인 파일 상태(File State)와 오프셋(Offset)을 가진다. 따라서 한 fd에서 파일 위치가 이동하더라도 다른 fd에는 영향을 주지 않는다.

반면 dup()을 사용하면 기존 fd를 복제하지만, 내부적으로는 같은 열린 파일 상태(Open File Description)를 공유한다. 이 경우 파일 위치를 나타내는 오프셋(Offset)도 함께 공유된다.

따라서 한쪽 fd에서 read(), write(), lseek() 등을 수행해 오프셋이 변경되면, 다른 fd에도 그 변화가 영향을 미친다.

결국 중요한 것은 fd 값 자체가 아니라, 두 fd가 내부적으로 같은 파일 상태를 공유하는지 여부이다.

open()을 두 번 호출하면 같은 파일을 열더라도 서로 독립적인 열린 파일 상태(Open File Description)가 만들어진다.

하지만 dup()은 fd 번호만 새로 만들고, 기존 fd가 가리키던 열린 파일 상태를 함께 사용한다. 이 차이는 파일 오프셋(Offset) 공유 여부를 이해하는 데 중요한 부분이므로 기억해두는 것이 좋다.


dup() Descriptor Sharing Example

핵심 코드를 보면 다음과 같이 dup()을 사용한다.

fd3 = dup(fd1);

dup(fd1)은 기존 파일 디스크립터(File Descriptor)인 fd1을 복제하여 새로운 파일 디스크립터 fd3을 생성한다. 이때 fd1fd3은 번호는 다르지만, 내부적으로는 같은 열린 파일 상태(Open File Description)를 공유한다.

따라서 두 fd는 같은 파일 오프셋(Offset)도 공유한다.

예를 들어 다음 코드가 실행된다고 하자.

write(fd1, "A", 1);

처음 오프셋이 0인 상태에서 fd1을 통해 "A"를 1바이트 기록하면, 0번 위치에 A가 저장되고 오프셋은 1번 위치로 이동한다.

이후 fd3을 통해 다음 코드가 실행된다.

write(fd3, "B", 1);

fd3fd1과 같은 오프셋을 공유하기 때문에, 현재 오프셋인 1번 위치에 B가 기록된다.

즉, 서로 다른 fd를 사용하더라도 dup()으로 생성된 fd들은 같은 파일 상태와 오프셋을 공유하므로, 하나의 파일에 이어서 기록되는 것처럼 동작한다.

중요한 점은 fd1fd3의 숫자값이 같은 것이 아니라, 두 fd가 같은 열린 파일 상태(Open File Description)를 가리킨다는 것이다.

그래서 dup()으로 복제된 fd 사이에서는 오프셋 변화가 서로 영향을 준다. 이 부분은 파일 디스크립터 공유를 이해하는 데 중요한 특징이므로 기억해두는 것이 좋다.


Descriptor Sharing with dup()

dup()은 기존 파일 디스크립터(File Descriptor)를 복제해서 새로운 fd를 생성하는 함수이다.

이때 단순히 fd 번호만 복사되는 것이 아니라, 동일한 열린 파일 상태(Open File Description)를 함께 공유한다. 따라서 파일의 현재 위치를 나타내는 오프셋(Offset)도 함께 공유된다.

예를 들어 한쪽 fd에서 write()를 수행하면 오프셋이 이동하고, 다른 fd에서도 그 이동된 오프셋을 기준으로 이어서 기록된다. 즉, 서로 다른 fd를 사용하더라도 같은 파일 상태를 공유하기 때문에 하나의 흐름처럼 동작한다.

반면 open()을 두 번 호출하면 같은 파일을 열더라도 각각 독립적인 파일 상태를 가진다. 이 경우 오프셋도 따로 관리되므로, 한쪽 fd의 위치 변화가 다른 fd에 영향을 주지 않는다.

결국 중요한 것은 fd 값 자체가 아니라, 내부적으로 파일 상태를 공유하는지 여부이다. 이 부분은 dup()open()의 차이를 이해하는 데 중요한 특징이므로 기억해두는 것이 좋다.

dup()으로 생성된 fd는 기존 fd와 같은 열린 파일 상태(Open File Description)를 가리킨다. 그래서 오프셋(Offset)뿐만 아니라 파일 상태 플래그(File Status Flags)도 공유될 수 있다. 다만 fd 번호 자체와 fd별 설정은 서로 다를 수 있다.


File Descriptor and Offset Example

이 예제는 파일 디스크립터(File Descriptor)에 따라 오프셋(Offset)이 어떻게 달라지는지 확인하는 실습이다.

먼저 fd1open() 함수를 통해 lab3.txt 파일을 한 번 연다. 그리고 fd2도 마찬가지로 open() 함수를 통해 같은 lab3.txt 파일을 다시 연다.

즉, open()을 두 번 호출하면 같은 파일이라도 서로 독립적인 열린 파일 상태(Open File Description)를 가지게 된다. 따라서 fd1fd2의 오프셋(Offset)은 각각 따로 관리된다.

반면 fd3dup() 함수를 통해 fd1을 복제해서 만들어진다. 이 경우 fd1fd3은 fd 번호는 다르지만, 동일한 열린 파일 상태를 공유한다. 따라서 두 fd는 같은 오프셋을 함께 사용한다.

그다음 코드에서는 write()를 수행한 뒤, lseek()를 통해 off1, off2, off3 값을 확인한다. 이 값들은 각각 fd1, fd2, fd3의 현재 위치, 즉 오프셋을 나타낸다.

정리하면 fd1fd3dup()으로 연결되어 같은 오프셋을 공유하고, fd2는 같은 파일을 열었더라도 별도로 open()된 것이기 때문에 독립적인 오프셋을 가진다.

lseek(fd, 0, SEEK_CUR)을 사용하면 현재 오프셋(Current Offset)을 확인할 수 있다. 여기서 SEEK_CUR은 현재 위치를 기준으로 한다는 뜻이고, 이동 값이 0이므로 위치를 바꾸지 않고 현재 위치만 확인하는 방식이다.


dup() Execution Result

실행 결과를 보면 먼저 fd1을 통해 A를 기록했을 때, fd1의 오프셋(Offset)이 1로 이동한다.

이때 fd3도 함께 1로 이동한 것을 확인할 수 있다. 이는 fd3dup(fd1)을 통해 생성되었기 때문에, fd1과 동일한 열린 파일 상태(Open File Description)를 공유하기 때문이다.

반면 fd2는 같은 lab3.txt 파일을 가리키더라도 open()을 통해 별도로 열린 파일 디스크립터(File Descriptor)이므로, 오프셋이 0으로 유지된다.

즉, 실행 결과를 통해 fd1fd3은 같은 오프셋을 공유하고, fd2는 독립적으로 동작한다는 것을 확인할 수 있다.

dup()으로 생성된 fd는 기존 fd와 같은 오프셋(Offset)을 공유한다. 반면 같은 파일이라도 open()을 다시 호출해서 얻은 fd는 독립적인 오프셋을 가진다. 이 차이는 파일 디스크립터 공유를 이해하는 데 중요한 부분이므로 기억해두는 것이 좋다.


dup() Code Explanation 2

그다음 코드에서 fd2를 기준으로 다음 쓰기 작업을 수행한다.

write(fd2, "B", 1);

fd2open()을 통해 별도로 생성된 파일 디스크립터(File Descriptor)이기 때문에, fd1, fd3와는 독립적인 오프셋(Offset)을 가진다. 따라서 이 작업을 수행하면 fd2의 오프셋만 1로 이동하고, fd1fd3의 오프셋은 그대로 유지된다.

마지막으로 다음 코드가 실행된다.

write(fd3, "C", 1);

fd3dup(fd1)을 통해 생성되었기 때문에 fd1과 같은 열린 파일 상태(Open File Description)를 공유한다. 따라서 fd3를 통해 쓰기 작업을 수행하면 fd3의 오프셋뿐만 아니라 fd1의 오프셋도 함께 이동한다.

결국 이 예제는 open()으로 생성된 fd2는 독립적으로 동작하고, dup()으로 연결된 fd1fd3는 오프셋을 포함한 파일 상태(File State)를 공유한다는 점을 보여준다.

같은 파일을 가리키더라도 open()으로 따로 열었는지, dup()으로 복제했는지에 따라 오프셋 공유 여부가 달라진다. 이 부분은 파일 디스크립터(File Descriptor)의 동작을 이해하는 데 중요한 내용이므로 기억해두는 것이 좋다.


File State Sharing with fork()(fork()에서의 파일 상태 공유)

이번에는 서로 다른 프로세스(Process)에서 파일 상태(File State)가 어떻게 동작하는지 살펴보는 예제이다.

먼저 fork()가 수행되면 자식 프로세스(Child Process)는 부모 프로세스(Parent Process)의 파일 디스크립터(File Descriptor)를 그대로 상속받는다.

이때 단순히 fd 값만 복사되는 것이 아니라, 동일한 열린 파일 상태(Open File Description)를 공유한다. 따라서 한쪽 프로세스에서 write()를 수행하면 오프셋(Offset)이 변경되고, 이 변화는 다른 프로세스에도 반영된다.

즉, 서로 다른 프로세스라 하더라도 같은 열린 파일 상태를 공유한다면 하나의 파일에 이어서 접근하는 것처럼 동작한다.

결국 파일 상태는 각 프로세스가 따로 관리하는 것이 아니라, 커널(Kernel) 내부에서 관리된다. 여러 프로세스는 이 커널의 파일 상태를 함께 공유할 수 있다.

fork() 후 부모와 자식 프로세스는 같은 열린 파일 상태(Open File Description)를 공유하므로 오프셋도 공유된다. 이 부분은 dup()과 비슷하게 이해할 수 있으며, 파일 공유 동작을 이해하는 데 중요한 내용이므로 기억해두는 것이 좋다.


File Sharing with fork()(fork()에서의 파일 공유)

이번 예제는 서로 다른 프로세스(Process)에서 파일 상태(File State)가 어떻게 공유되는지 확인하는 실습이다.

fork()는 이전 주차에서도 사용했기 때문에 기본 동작은 익숙할 수 있다. 하지만 여기서의 핵심은 프로세스를 나누는 동작 자체가 아니라, fork() 이후 부모 프로세스(Parent Process)와 자식 프로세스(Child Process)가 파일 자원(File Resource)을 어떻게 공유하는지 확인하는 것이다.

코드를 보면 fork() 이후 자식 프로세스는 "CHILD\n"를 파일에 기록하고, 부모 프로세스는 "PARENT\n"를 파일에 기록한다.

이때 자식 프로세스는 부모 프로세스의 파일 디스크립터(File Descriptor)를 그대로 상속받는다. 단순히 fd 값만 복사되는 것이 아니라, 동일한 열린 파일 상태(Open File Description)를 공유한다.

따라서 두 프로세스의 write()는 서로 완전히 독립적으로 수행되는 것이 아니라, 하나의 파일에 이어서 기록되는 형태로 동작한다. 한쪽 프로세스에서 write()를 수행하면 오프셋(Offset)이 이동하고, 이 변경된 오프셋은 다른 프로세스에도 반영된다.

즉, 이전에는 fork()의 프로세스 생성에 초점을 두었다면, 이번 실습에서는 fork() 이후 파일 상태가 어떻게 공유되는지를 확인하는 것이 핵심이다.

fork() 이후 부모와 자식은 같은 열린 파일 상태(Open File Description)를 공유하기 때문에 오프셋(Offset)도 공유한다. 이 부분은 dup()으로 생성된 fd가 오프셋을 공유하는 것과 비슷하게 이해할 수 있다.

다만 부모와 자식 중 어느 쪽이 먼저 실행될지는 스케줄링(Scheduling)에 따라 달라질 수 있다. 그래서 파일에는 두 문자열이 순차적으로 기록되지만, 실행 순서에 따라 CHILD가 먼저 기록될 수도 있고 PARENT가 먼저 기록될 수도 있다.


File Sharing with fork() Code Example

이 코드는 fork() 이후 부모 프로세스(Parent Process)와 자식 프로세스(Child Process)가 파일 상태(File State)를 어떻게 공유하는지 확인하는 예제이다.

먼저 open()을 통해 lab4.txt 파일을 연다. 이때 파일 디스크립터(File Descriptor, fd)가 생성되고, 이후 fork()가 수행되면서 부모와 자식 프로세스로 나뉜다.

fork() 이후 자식 프로세스는 write(fd, "CHILD\n", 6)을 수행하여 파일에 CHILD를 기록한다. 부모 프로세스는 wait(NULL)을 통해 자식 프로세스가 먼저 실행되도록 기다린 뒤, write(fd, "PARENT\n", 7)을 수행한다.

이때 자식 프로세스는 부모 프로세스의 fd를 상속받기 때문에, 두 프로세스는 동일한 열린 파일 상태(Open File Description)와 오프셋(Offset)을 공유한다.

따라서 자식이 먼저 CHILD를 기록하면 오프셋이 그 다음 위치로 이동하고, 이후 부모가 같은 오프셋을 기준으로 PARENT를 이어서 기록한다. 즉, 서로 다른 프로세스라도 같은 파일 상태를 공유하면 하나의 파일에 순서대로 이어 쓰는 것처럼 동작한다.

정리하면 이 예제의 핵심은 fork() 이후 부모와 자식 프로세스가 파일 디스크립터를 상속받고, 동일한 열린 파일 상태와 오프셋을 공유한다는 점이다.

첨부된 코드에서는 open()이 먼저 실행된 뒤 fork()가 수행된다. 그래서 자식 프로세스가 부모의 fd를 상속받을 수 있다.

또한 부모 프로세스에서 wait(NULL)을 사용했기 때문에 자식이 먼저 기록하고, 그다음 부모가 기록된다. 만약 wait()가 없다면 부모와 자식 중 누가 먼저 실행될지는 스케줄링(Scheduling)에 따라 달라지게 된다.


fork() File Sharing Execution Result

프로세스와 파일 공유(fork) 실행 결과를 보면, 화면에는 자식 프로세스(Child Process)와 부모 프로세스(Parent Process)의 완료 메시지가 출력된다.

lab4.txt 파일의 내용을 확인하면 자식 프로세스가 먼저 CHILD를 기록하고, 그다음 부모 프로세스가 PARENT를 이어서 기록한 것을 볼 수 있다.

이때 두 프로세스는 같은 파일 디스크립터(File Descriptor)를 상속받았기 때문에 동일한 열린 파일 상태(Open File Description)를 공유한다. 따라서 오프셋(Offset)도 함께 공유된다.

자식 프로세스가 먼저 파일에 데이터를 기록하면 오프셋이 그 다음 위치로 이동하고, 부모 프로세스는 이동된 오프셋을 기준으로 데이터를 이어서 기록한다.

즉, 부모와 자식 프로세스가 각각 파일을 따로 쓰는 것이 아니라, 하나의 파일에 순차적으로 기록되는 구조로 이해할 수 있다.

이 예제에서는 부모 프로세스가 wait()를 사용하기 때문에 자식 프로세스가 먼저 실행된 뒤 부모 프로세스가 기록한다. 만약 wait()가 없다면 실행 순서는 운영체제의 스케줄링(Scheduling)에 따라 달라지게 된다.


Summary of File Access Structure(파일 접근 구조 정리)

파일 접근(File Access)은 파일 이름(File Name)이 아니라 파일 디스크립터(File Descriptor, fd)를 통해 이루어진다. 프로그램은 open()을 통해 파일을 열고 fd를 반환받은 뒤, 이후의 read(), write(), close() 작업을 fd 기준으로 수행한다.

또한 파일 내부에서 읽고 쓰는 현재 위치는 오프셋(Offset)으로 관리된다. write()read()가 수행되면 오프셋은 자동으로 이동하며, lseek()를 사용하면 원하는 위치로 오프셋을 직접 이동시킬 수 있다.

이번 실습에서는 dup()fork()를 통해 파일 디스크립터와 오프셋이 여러 주체 간에 공유될 수 있다는 점도 확인했다. dup()으로 생성된 fd는 기존 fd와 동일한 열린 파일 상태(Open File Description)를 공유하고, fork() 이후 부모와 자식 프로세스도 같은 열린 파일 상태를 공유할 수 있다.

따라서 파일은 단순히 데이터가 저장된 공간이 아니라, 파일 디스크립터(fd), 오프셋(Offset), 커널 내부의 열린 파일 상태(Open File Description)까지 함께 포함된 하나의 상태 객체(State Object)로 이해할 수 있다.

파일 이름(File Name)은 파일을 처음 찾을 때 사용되고, 파일을 연 뒤에는 fd를 통해 접근한다. 중요한 점은 fd 번호 자체보다, 그 fd가 어떤 열린 파일 상태(Open File Description)를 가리키고 있는지이다. 이 부분은 open(), dup(), fork()의 차이를 이해하는 핵심이므로 기억해두는 것이 좋다.


Ubuntu Practice Results

File Descriptor Practice Result (파일 디스크립터 실습 결과)

실습 결과를 보면 fd = 3으로 출력된 것을 확인할 수 있다. 이 값은 open()을 통해 lab1.txt 파일을 열었을 때 운영체제(Operating System)가 반환한 파일 디스크립터(File Descriptor) 번호이다.

버추얼머신(Virtual Machine)의 sf_shared 폴더를 확인하면 lab1.txt 파일이 생성되어 있는 것을 볼 수 있다. 파일 안에는 다음과 같이 두 개의 문자열이 순서대로 기록되어 있다.

Operating Systems
File Descriptor Test

겉으로 보기에는 단순히 파일에 문자열을 쓰는 작업처럼 보인다. 하지만 실제 내부에서는 파일 이름(File Name)이 아니라 파일 디스크립터(fd)를 기준으로 파일에 접근하고 데이터를 기록한다.

즉, open()을 통해 파일이 생성되고, write()를 통해 데이터가 기록되며, 마지막으로 close()를 통해 파일 사용이 종료되고 관련 자원(Resource)이 반환된다.

이 실습을 통해 파일은 실행 중에 이름이 아니라 fd를 기준으로 관리되며, 파일 접근은 시스템 호출(System Call)을 통해 이루어진다는 것을 확인할 수 있었다.


lseek() Practice Result (lseek() 실습 결과)

두 번째 실습은 파일의 특정 위치로 이동한 뒤 데이터를 덮어쓰는 과정을 확인하는 실습이다.

실행 결과를 보면 처음에는 ABCDEFGH가 파일에 저장되어 있었지만, lseek()를 통해 2번 위치로 이동한 뒤 XXX를 기록하면서 기존의 일부 데이터가 덮어쓰기 된 것을 확인할 수 있었다.

이후 lab2.txt 파일이 생성되었는지 sf_shared 폴더에서 확인할 수 있었고, 파일 안의 문자열이 중간 위치부터 변경된 것도 확인할 수 있었다.

겉으로 보기에는 문자열이 단순히 수정된 것처럼 보이지만, 실제로는 오프셋(Offset)을 이동한 뒤 해당 위치에서 write()가 수행된 결과이다.

중요한 점은 데이터가 뒤로 밀리는 것이 아니라, 현재 오프셋 위치부터 그대로 덮어쓰기(Overwrite) 된다는 것이다. 이 실습을 통해 파일 접근은 단순한 순차 구조가 아니라 오프셋을 기준으로 이루어지며, lseek()를 통해 파일 내 위치를 직접 제어할 수 있다는 것을 확인할 수 있었다.

lseek()는 파일 내용을 직접 바꾸는 함수가 아니라, 파일의 현재 접근 위치(Current Offset)를 이동시키는 함수이다. 실제 데이터 변경은 그 이후에 실행되는 write()에 의해 이루어진다.


dup() Practice Result (dup() 실습 결과)

세 번째 실습은 파일 디스크립터(File Descriptor)의 생성 방식에 따라 오프셋(Offset)이 어떻게 달라지는지 확인하는 예제였다.

실행 결과를 보면 A 기록 후에는 fd1fd3의 오프셋이 함께 1로 이동하고, fd2는 0으로 유지된다. 이는 fd3dup(fd1)으로 생성되어 fd1과 동일한 열린 파일 상태(Open File Description)를 공유하기 때문이다.

그다음 fd2에서 B를 기록하면 fd2의 오프셋만 1로 이동한다. fd2는 같은 lab3.txt 파일을 열었지만, open()을 통해 별도로 생성되었기 때문에 독립적인 오프셋을 가진다.

마지막으로 fd3에서 C를 기록하면 fd3와 파일 상태를 공유하는 fd1의 오프셋도 함께 2로 이동한다. 반면 fd2는 독립적으로 동작한다.

실제로 lab3.txt 파일의 내용이 BC로 나타나는 것은, fd2가 독립적인 오프셋 0번 위치에서 B를 기록하면서 앞부분을 덮어쓰고, 이후 fd3가 공유된 오프셋 위치에서 C를 이어서 기록했기 때문이다.

이 실습을 통해 파일 디스크립터는 단순한 정수값이 아니라, 내부적으로 어떤 열린 파일 상태를 공유하는지에 따라 동작이 달라진다는 것을 확인할 수 있었다.

open()을 다시 호출해 생성한 fd는 같은 파일을 가리키더라도 독립적인 오프셋을 가진다. 반면 dup()으로 생성한 fd는 기존 fd와 열린 파일 상태(Open File Description)를 공유하므로 오프셋도 함께 이동한다. 이 차이는 중요한 부분이므로 기억해두는 것이 좋다.


File Sharing with fork() Practice Result

이 예제는 fork() 이후 부모 프로세스(Parent Process)와 자식 프로세스(Child Process)가 파일을 어떻게 공유하는지 확인하는 실습이었다.

실행 결과를 보면 화면에는 child: write complete, parent: write complete 메시지가 출력된다. 또한 lab4.txt 파일이 생성되었고, 파일 내용을 확인하면 먼저 CHILD가 기록되고 그다음 PARENT가 이어서 기록된 것을 볼 수 있다.

겉으로는 부모 프로세스와 자식 프로세스가 각각 파일에 쓰는 것처럼 보인다. 하지만 실제로는 두 프로세스가 같은 파일 디스크립터(File Descriptor)를 상속받고, 동일한 열린 파일 상태(Open File Description)와 오프셋(Offset)을 공유하면서 하나의 파일에 순차적으로 기록하는 구조이다.

이 실습에서는 wait()를 사용했기 때문에 자식 프로세스가 먼저 실행되어 CHILD를 기록하고, 이후 부모 프로세스가 PARENT를 이어서 기록되었다.

이 실습을 통해 fork() 이후에도 파일 상태(File State)는 공유될 수 있으며, 여러 프로세스(Process)가 하나의 파일을 함께 사용할 수 있다는 점을 확인할 수 있었다.

fork() 이후 부모와 자식은 같은 열린 파일 상태(Open File Description)를 공유하므로 오프셋(Offset)도 함께 공유된다. 따라서 한쪽 프로세스가 파일에 쓰면 오프셋이 이동하고, 다른 프로세스는 이동된 위치부터 이어서 작업하게 된다.

다만 wait()가 없다면 부모와 자식 중 누가 먼저 실행될지는 스케줄링(Scheduling)에 따라 달라질 수 있다.


More from this blog

My dev journey

146 posts