파일 시스템 기초 ⭐⭐⭐
파일 시스템 기초 ⭐⭐⭐¶
개요¶
파일 시스템은 운영체제가 데이터를 디스크에 저장하고 관리하는 방법을 정의합니다. 파일의 개념, 속성, 연산, 디렉토리 구조, 접근 방법 등 핵심 개념을 학습합니다.
목차¶
1. 파일의 개념¶
1.1 파일이란?¶
┌─────────────────────────────────────────────────────────────────────────┐
│ 파일의 정의 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ 파일 = 연관된 정보의 이름 붙은 집합 │
│ (Named collection of related information) │
│ │
│ 운영체제 관점: │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 디스크 (물리적 저장장치) │ │
│ │ ┌─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┐ │ │
│ │ │블록0│블록1│블록2│블록3│블록4│블록5│블록6│블록7│ │ │
│ │ └─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┘ │ │
│ │ ↑ ↑ ↑ ↑ ↑ ↑ │ │
│ │ └────┬────┘ └────┬────┘ └──┬──┘ │ │
│ │ │ │ │ │ │
│ │ 파일 A 파일 B 파일 C │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ 사용자 관점: │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 논리적 저장 단위 │ │
│ │ │ │
│ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │
│ │ │ report.docx │ │ photo.jpg │ │ program.exe │ │ │
│ │ │ (문서) │ │ (이미지) │ │ (실행파일) │ │ │
│ │ └──────────────┘ └──────────────┘ └──────────────┘ │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ 파일 시스템: 이 두 관점을 연결하는 추상화 계층 │
│ │
└─────────────────────────────────────────────────────────────────────────┘
1.2 파일 유형¶
┌─────────────────────────────────────────────────────────────────────────┐
│ 파일 유형 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌───────────────┬──────────────┬─────────────────────────────────┐ │
│ │ 유형 │ 확장자 │ 설명 │ │
│ ├───────────────┼──────────────┼─────────────────────────────────┤ │
│ │ 실행 파일 │ .exe, .com │ 기계어 코드 │ │
│ │ │ .bin, (없음) │ │ │
│ ├───────────────┼──────────────┼─────────────────────────────────┤ │
│ │ 오브젝트 │ .o, .obj │ 컴파일된 목적 코드 │ │
│ ├───────────────┼──────────────┼─────────────────────────────────┤ │
│ │ 소스 코드 │ .c, .py, .js │ 프로그래밍 언어 소스 │ │
│ ├───────────────┼──────────────┼─────────────────────────────────┤ │
│ │ 텍스트 │ .txt, .md │ 일반 텍스트 │ │
│ ├───────────────┼──────────────┼─────────────────────────────────┤ │
│ │ 라이브러리 │ .a, .so │ 정적/동적 라이브러리 │ │
│ │ │ .lib, .dll │ │ │
│ ├───────────────┼──────────────┼─────────────────────────────────┤ │
│ │ 이미지 │ .jpg, .png │ 그래픽 데이터 │ │
│ │ │ .gif, .bmp │ │ │
│ ├───────────────┼──────────────┼─────────────────────────────────┤ │
│ │ 압축 │ .zip, .tar │ 압축된 아카이브 │ │
│ │ │ .gz, .7z │ │ │
│ └───────────────┴──────────────┴─────────────────────────────────┘ │
│ │
│ 유닉스에서는 확장자가 필수가 아님 (Convention만 존재) │
│ 내용에 따른 유형 판단: file 명령어 │
│ │
│ $ file /bin/ls │
│ /bin/ls: ELF 64-bit LSB shared object, x86-64... │
│ │
│ $ file report.pdf │
│ report.pdf: PDF document, version 1.4 │
│ │
└─────────────────────────────────────────────────────────────────────────┘
2. 파일 속성¶
2.1 기본 속성 (Metadata)¶
┌─────────────────────────────────────────────────────────────────────────┐
│ 파일 속성 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────┬───────────────────────────────────────────────┐ │
│ │ 속성 │ 설명 │ │
│ ├─────────────────┼───────────────────────────────────────────────┤ │
│ │ 이름 (Name) │ 사용자가 읽을 수 있는 파일명 │ │
│ ├─────────────────┼───────────────────────────────────────────────┤ │
│ │ 식별자 │ 파일 시스템 내 고유 번호 (inode 번호) │ │
│ ├─────────────────┼───────────────────────────────────────────────┤ │
│ │ 유형 (Type) │ 일반파일, 디렉토리, 심볼릭링크, 디바이스 등 │ │
│ ├─────────────────┼───────────────────────────────────────────────┤ │
│ │ 위치 (Location) │ 디스크에서의 저장 위치 (블록 포인터) │ │
│ ├─────────────────┼───────────────────────────────────────────────┤ │
│ │ 크기 (Size) │ 파일의 바이트 수 │ │
│ ├─────────────────┼───────────────────────────────────────────────┤ │
│ │ 보호 (Protection)│ 접근 권한 (rwxrwxrwx) │ │
│ ├─────────────────┼───────────────────────────────────────────────┤ │
│ │ 소유자 (Owner) │ 파일 소유자 (UID) │ │
│ ├─────────────────┼───────────────────────────────────────────────┤ │
│ │ 그룹 (Group) │ 파일 그룹 (GID) │ │
│ ├─────────────────┼───────────────────────────────────────────────┤ │
│ │ 시간 정보 │ 생성, 수정, 접근 시간 │ │
│ └─────────────────┴───────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
2.2 Unix/Linux stat 구조체¶
#include <sys/stat.h>
#include <time.h>
// stat 구조체 (파일 속성 저장)
struct stat {
dev_t st_dev; // 장치 ID
ino_t st_ino; // inode 번호
mode_t st_mode; // 파일 모드 (유형 + 권한)
nlink_t st_nlink; // 하드 링크 수
uid_t st_uid; // 소유자 UID
gid_t st_gid; // 그룹 GID
dev_t st_rdev; // 장치 ID (특수 파일)
off_t st_size; // 총 크기 (바이트)
blksize_t st_blksize; // I/O 블록 크기
blkcnt_t st_blocks; // 할당된 512B 블록 수
// 시간 정보
struct timespec st_atim; // 마지막 접근 시간
struct timespec st_mtim; // 마지막 수정 시간
struct timespec st_ctim; // 마지막 상태 변경 시간
};
// 파일 속성 조회 예제
void print_file_info(const char* path) {
struct stat sb;
if (stat(path, &sb) == -1) {
perror("stat");
return;
}
printf("파일: %s\n", path);
printf("inode: %lu\n", sb.st_ino);
printf("크기: %ld 바이트\n", sb.st_size);
printf("블록: %ld\n", sb.st_blocks);
printf("권한: %o\n", sb.st_mode & 0777);
printf("소유자: %d\n", sb.st_uid);
printf("링크: %lu\n", sb.st_nlink);
// 파일 유형
if (S_ISREG(sb.st_mode)) printf("유형: 일반 파일\n");
if (S_ISDIR(sb.st_mode)) printf("유형: 디렉토리\n");
if (S_ISLNK(sb.st_mode)) printf("유형: 심볼릭 링크\n");
if (S_ISCHR(sb.st_mode)) printf("유형: 문자 장치\n");
if (S_ISBLK(sb.st_mode)) printf("유형: 블록 장치\n");
printf("수정 시간: %s", ctime(&sb.st_mtime));
}
2.3 ls -l 출력 해석¶
┌─────────────────────────────────────────────────────────────────────────┐
│ ls -l 출력 분석 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ $ ls -l │
│ -rw-r--r-- 1 user group 4096 Jan 15 10:30 document.txt │
│ │├──┤├─┤│ │ │ │ │ │ │ └── 파일명 │
│ ││ │ │ │ │ │ │ │ │ └── 시간 │
│ ││ │ │ │ │ │ │ │ └── 날짜 │
│ ││ │ │ │ │ │ │ └── 크기 (바이트) │
│ ││ │ │ │ │ │ └── 그룹 │
│ ││ │ │ │ │ └── 소유자 │
│ ││ │ │ │ └── 하드 링크 수 │
│ ││ │ │ └── 기타 권한 │
│ ││ │ └── 그룹 권한 │
│ ││ └── 소유자 권한 │
│ │└── 파일 유형 │
│ └── - 일반파일, d 디렉토리, l 링크, c 문자장치, b 블록장치 │
│ │
│ 권한: │
│ r (4) = 읽기 │
│ w (2) = 쓰기 │
│ x (1) = 실행 (디렉토리는 접근) │
│ │
│ 예: rw-r--r-- = 644 = 소유자: rw, 그룹: r, 기타: r │
│ │
└─────────────────────────────────────────────────────────────────────────┘
3. 파일 연산과 시스템 콜¶
3.1 기본 파일 연산¶
┌─────────────────────────────────────────────────────────────────────────┐
│ 파일 연산 종류 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────┬───────────────────────────────────────────────┐ │
│ │ 연산 │ 설명 │ │
│ ├─────────────────┼───────────────────────────────────────────────┤ │
│ │ 생성 (Create) │ 새 파일 생성, 디렉토리에 엔트리 추가 │ │
│ │ 열기 (Open) │ 파일 접근 준비, 파일 디스크립터 반환 │ │
│ │ 읽기 (Read) │ 현재 위치에서 데이터 읽기 │ │
│ │ 쓰기 (Write) │ 현재 위치에 데이터 쓰기 │ │
│ │ 이동 (Seek) │ 파일 내 현재 위치 변경 │ │
│ │ 닫기 (Close) │ 파일 접근 종료, 리소스 해제 │ │
│ │ 삭제 (Delete) │ 파일 제거, 디렉토리에서 엔트리 삭제 │ │
│ │ 자르기 (Truncate)│ 파일 크기를 0으로 (내용 삭제) │ │
│ └─────────────────┴───────────────────────────────────────────────┘ │
│ │
│ 추가 연산: │
│ - Append: 파일 끝에 데이터 추가 │
│ - Rename: 파일명 변경 │
│ - Get/Set Attributes: 속성 조회/변경 │
│ │
└─────────────────────────────────────────────────────────────────────────┘
3.2 시스템 콜 예제¶
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
// 파일 생성 및 쓰기
void create_and_write(const char* path) {
// O_CREAT: 없으면 생성
// O_WRONLY: 쓰기 전용
// O_TRUNC: 기존 내용 삭제
// 0644: 권한 (rw-r--r--)
int fd = open(path, O_CREAT | O_WRONLY | O_TRUNC, 0644);
if (fd == -1) {
perror("open");
exit(1);
}
const char* data = "Hello, File System!\n";
ssize_t written = write(fd, data, strlen(data));
if (written == -1) {
perror("write");
} else {
printf("%zd 바이트 기록됨\n", written);
}
close(fd);
}
// 파일 읽기
void read_file(const char* path) {
int fd = open(path, O_RDONLY);
if (fd == -1) {
perror("open");
return;
}
char buffer[1024];
ssize_t bytes_read;
// read()는 읽은 바이트 수 반환, 0은 EOF
while ((bytes_read = read(fd, buffer, sizeof(buffer) - 1)) > 0) {
buffer[bytes_read] = '\0';
printf("%s", buffer);
}
if (bytes_read == -1) {
perror("read");
}
close(fd);
}
// 파일 위치 이동 (Seek)
void seek_example(const char* path) {
int fd = open(path, O_RDONLY);
if (fd == -1) return;
// 파일 끝으로 이동하여 크기 확인
off_t size = lseek(fd, 0, SEEK_END);
printf("파일 크기: %ld 바이트\n", size);
// 처음으로 되돌아가기
lseek(fd, 0, SEEK_SET);
// 10바이트 앞으로 이동
lseek(fd, 10, SEEK_CUR);
char buffer[100];
read(fd, buffer, 10);
buffer[10] = '\0';
printf("10바이트 이후 10글자: %s\n", buffer);
close(fd);
}
// 파일 복사 구현
int copy_file(const char* src, const char* dst) {
int src_fd = open(src, O_RDONLY);
if (src_fd == -1) return -1;
int dst_fd = open(dst, O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (dst_fd == -1) {
close(src_fd);
return -1;
}
char buffer[4096];
ssize_t bytes;
while ((bytes = read(src_fd, buffer, sizeof(buffer))) > 0) {
ssize_t written = write(dst_fd, buffer, bytes);
if (written != bytes) {
close(src_fd);
close(dst_fd);
return -1;
}
}
close(src_fd);
close(dst_fd);
return 0;
}
int main() {
create_and_write("test.txt");
read_file("test.txt");
seek_example("test.txt");
copy_file("test.txt", "test_copy.txt");
return 0;
}
3.3 Open File Table¶
┌─────────────────────────────────────────────────────────────────────────┐
│ Open File Table 구조 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ 프로세스 A 프로세스 B │
│ ┌──────────────────┐ ┌──────────────────┐ │
│ │ fd table │ │ fd table │ │
│ ├──────────────────┤ ├──────────────────┤ │
│ │ 0: stdin │ │ 0: stdin │ │
│ │ 1: stdout │ │ 1: stdout │ │
│ │ 2: stderr │ │ 2: stderr │ │
│ │ 3: ─────────────┐│ │ 3: ─────────────┐│ │
│ │ 4: ────────────┐││ └─────────────────┼┘ │
│ └────────────────┼┼┘ │ │
│ ││ │ │
│ ▼▼ │ │
│ System-wide Open File Table │ │
│ ┌─────────────────────────────────────────┐ │ │
│ │ 엔트리 1: │ │ │
│ │ - 파일 위치 (offset): 100 │◀┘ │
│ │ - 접근 모드: O_RDONLY │ │
│ │ - 참조 카운트: 2 │ │
│ │ - inode 포인터: ──────────────────────┼───┐ │
│ ├─────────────────────────────────────────┤ │ │
│ │ 엔트리 2: │ │ │
│ │ - 파일 위치 (offset): 500 │ │ │
│ │ - 접근 모드: O_RDWR │ │ │
│ │ - 참조 카운트: 1 │ │ │
│ │ - inode 포인터: ──────────────────────┼───┼───┐ │
│ └─────────────────────────────────────────┘ │ │ │
│ │ │ │
│ In-Memory inode Table │ │ │
│ ┌─────────────────────────────────────────┐ │ │ │
│ │ inode 1234: │◀──┘ │ │
│ │ - 파일 크기: 4096 │ │ │
│ │ - 디스크 블록 위치 │ │ │
│ │ - 권한, 소유자 등 │ │ │
│ ├─────────────────────────────────────────┤ │ │
│ │ inode 5678: │◀──────┘ │
│ │ - 파일 크기: 8192 │ │
│ │ - ... │ │
│ └─────────────────────────────────────────┘ │
│ │
│ fork() 시: fd table 복사, open file entry 공유 (offset 공유!) │
│ │
└─────────────────────────────────────────────────────────────────────────┘
4. 디렉토리 구조¶
4.1 단일 레벨 디렉토리¶
┌─────────────────────────────────────────────────────────────────────────┐
│ 단일 레벨 디렉토리 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ 루트 디렉토리 │ │
│ ├─────────────────────────────────────────────────────────────────┤ │
│ │ cat │ bo │ a │ test │ data │ mail │ cont │ hex │ records │ │ │
│ └──┬──┴──┬─┴──┬─┴───┬──┴───┬──┴───┬──┴───┬──┴──┬──┴────┬────┘ │ │
│ │ │ │ │ │ │ │ │ │ │ │
│ ▼ ▼ ▼ ▼ ▼ ▼ ▼ ▼ ▼ │ │
│ 파일 파일 파일 파일 파일 파일 파일 파일 파일 │ │
│ │
│ 문제점: │
│ - 파일명 충돌: 모든 사용자가 같은 이름 사용 불가 │
│ - 관리 어려움: 파일 수 증가시 찾기 어려움 │
│ - 그룹화 불가: 관련 파일을 묶을 수 없음 │
│ │
│ 초기 운영체제 (CP/M)에서 사용 │
│ │
└─────────────────────────────────────────────────────────────────────────┘
4.2 2단계 디렉토리¶
┌─────────────────────────────────────────────────────────────────────────┐
│ 2단계 디렉토리 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ 마스터 파일 디렉토리 (MFD) │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ user1 user2 user3 │ │
│ └───────────────┬────────────────┬─────────────────┬──────────────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ 사용자 파일 디렉토리 (UFD) │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
│ │ cat test a │ │ cat data b │ │ hex mail │ │
│ └──┬──────┬────┬──┘ └──┬──────┬────┬──┘ └──┬──────┬──────┘ │
│ │ │ │ │ │ │ │ │ │
│ ▼ ▼ ▼ ▼ ▼ ▼ ▼ ▼ │
│ │
│ 장점: │
│ - 사용자별 독립적 이름 공간 │
│ - user1의 cat ≠ user2의 cat │
│ │
│ 단점: │
│ - 사용자 간 파일 공유 어려움 │
│ - 하위 디렉토리 없음 │
│ │
└─────────────────────────────────────────────────────────────────────────┘
4.3 트리 구조 디렉토리¶
┌─────────────────────────────────────────────────────────────────────────┐
│ 트리 구조 디렉토리 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ / │
│ │ │
│ ┌──────────────────┼──────────────────┐ │
│ │ │ │ │
│ bin home etc │
│ │ │ │ │
│ ┌─────┴─────┐ ┌───────┼───────┐ ┌────┴────┐ │
│ ls cat │ │ │ passwd hosts │
│ user1 user2 user3 │
│ │ │ │ │
│ ┌────┴────┐ │ documents │
│ docs code data │ │
│ │ │ │ ┌──┴──┐ │
│ ┌───┼───┐ main.c │ report notes │
│ a.txt b.txt file.txt │
│ │
│ 특징: │
│ - 임의 깊이의 하위 디렉토리 허용 │
│ - 절대 경로: /home/user1/docs/a.txt │
│ - 상대 경로: ./docs/a.txt (현재 디렉토리 기준) │
│ - 현재 디렉토리 (.), 상위 디렉토리 (..) │
│ │
│ 현재 대부분의 운영체제가 사용 │
│ │
└─────────────────────────────────────────────────────────────────────────┘
4.4 비순환 그래프 디렉토리 (Acyclic Graph)¶
┌─────────────────────────────────────────────────────────────────────────┐
│ 비순환 그래프 디렉토리 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ / │
│ │ │
│ ┌──────────────────┼──────────────────┐ │
│ │ │ │ │
│ home shared │ │
│ │ │ │ │
│ ┌─────┴─────┐ project ◀─────────┘ │
│ user1 user2 │ ▲ │
│ │ │ ┌─────┴─────┐ │
│ myproj ───────────────│ │ │
│ │ 공유 파일 │
│ ourproj ────┘ │
│ │
│ 파일/디렉토리 공유 방법: │
│ │
│ 1. 하드 링크 (Hard Link): │
│ - 같은 inode를 가리키는 다른 이름 │
│ - ln target link_name │
│ - 삭제 시 링크 카운트만 감소 │
│ - 디렉토리에는 사용 불가 (순환 방지) │
│ │
│ 2. 심볼릭 링크 (Symbolic Link): │
│ - 다른 파일의 경로를 저장 │
│ - ln -s target link_name │
│ - 원본 삭제 시 끊어진 링크 (dangling link) │
│ - 디렉토리에도 사용 가능 │
│ │
│ $ ln /shared/project myproj # 하드 링크 │
│ $ ln -s /shared/project myproj # 심볼릭 링크 │
│ │
└─────────────────────────────────────────────────────────────────────────┘
5. 파일 접근 방법¶
5.1 순차 접근 (Sequential Access)¶
┌─────────────────────────────────────────────────────────────────────────┐
│ 순차 접근 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ 파일 │
│ ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐ │
│ │ A │ B │ C │ D │ E │ F │ G │ H │ I │ J │ │
│ └───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘ │
│ ↑ │
│ 현재 위치 │
│ │
│ 연산: │
│ - read(): 현재 위치에서 읽고, 위치 전진 │
│ - write(): 현재 위치에 쓰고, 위치 전진 │
│ - reset(): 처음으로 되돌리기 │
│ │
│ 특징: │
│ - 테이프 기반 시스템에서 유래 │
│ - 편집기, 컴파일러에서 주로 사용 │
│ - 로그 파일 처리에 적합 │
│ │
│ 예: 파일 끝까지 순차 읽기 │
│ while (read(fd, &buf, size) > 0) { │
│ process(buf); │
│ } │
│ │
└─────────────────────────────────────────────────────────────────────────┘
5.2 직접 접근 (Direct/Random Access)¶
┌─────────────────────────────────────────────────────────────────────────┐
│ 직접 접근 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ 파일 (논리적 블록 단위) │
│ 블록: 0 1 2 3 4 5 6 7 8 9 │
│ ┌─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┐ │
│ │ │ │ │ │ │ │ │ │ │ │ │
│ └─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┘ │
│ ↑ ↑ │
│ 블록 1 블록 5 │
│ 읽기 읽기 │
│ │
│ 연산: │
│ - read(n): 블록 n 읽기 │
│ - write(n): 블록 n 쓰기 │
│ - seek(n): 블록 n으로 이동 │
│ │
│ 특징: │
│ - 디스크 기반 시스템 │
│ - 데이터베이스에 적합 │
│ - 순서 없이 임의 위치 접근 │
│ │
│ 예: 레코드 직접 접근 │
│ #define RECORD_SIZE 100 │
│ lseek(fd, record_num * RECORD_SIZE, SEEK_SET); │
│ read(fd, &record, RECORD_SIZE); │
│ │
└─────────────────────────────────────────────────────────────────────────┘
5.3 인덱스 접근 (Indexed Access)¶
┌─────────────────────────────────────────────────────────────────────────┐
│ 인덱스 접근 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ 인덱스 파일 데이터 파일 │
│ ┌────────────────────┐ ┌─────────────────────────┐ │
│ │ 키 │ 블록 포인터│ │ 블록 0 │ │
│ ├─────────┼───────────┤ ├─────────────────────────┤ │
│ │ "Apple" │ 7 │────────────────▶│ 블록 7: Apple 데이터 │ │
│ │ "Banana"│ 3 │────────────┐ ├─────────────────────────┤ │
│ │ "Cherry"│ 12 │──────────┐ │ │ 블록 3: Banana 데이터 │◀──┤
│ │ "Date" │ 5 │────────┐ │ │ ├─────────────────────────┤ │
│ │ ... │ ... │ │ │ │ │ 블록 12: Cherry 데이터 │◀──┘
│ └─────────┴───────────┘ │ │ │ ├─────────────────────────┤
│ │ │ │ │ 블록 5: Date 데이터 │◀──┐
│ │ │ └──▶│ │ │
│ │ │ └─────────────────────────┘ │
│ │ │ │
│ └─┼────────────────────────────────────┘
│ │
│ └── (인덱스에서 위치를 찾아 직접 접근)
│
│ 특징: │
│ - 큰 파일의 빠른 검색 │
│ - 인덱스 자체가 너무 크면 다단계 인덱스 │
│ - 데이터베이스의 B+트리 인덱스 │
│ │
└─────────────────────────────────────────────────────────────────────────┘
6. 파일 시스템 마운트¶
6.1 마운트 개념¶
┌─────────────────────────────────────────────────────────────────────────┐
│ 파일 시스템 마운트 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ 마운트 전: │
│ │
│ 루트 파일 시스템 (/) USB 드라이브 │
│ / 독립된 트리 │
│ ╱│╲ │ │
│ bin home etc ╱ ╲ │
│ │ photo doc │
│ user1 │
│ │ │
│ mnt (빈 디렉토리) │
│ │
│ 마운트 후 (mount /dev/sdb1 /home/user1/mnt): │
│ │
│ / │
│ ╱│╲ │
│ bin home etc │
│ │ │
│ user1 │
│ │ │
│ mnt ◀─────────── 마운트 포인트 │
│ ╱ ╲ │
│ photo doc ← USB 드라이브의 내용이 여기에 보임 │
│ │
└─────────────────────────────────────────────────────────────────────────┘
6.2 마운트 명령어¶
# 마운트 상태 확인
$ mount
/dev/sda1 on / type ext4 (rw,relatime)
/dev/sdb1 on /mnt/usb type vfat (rw,user)
# USB 드라이브 마운트
$ sudo mount /dev/sdb1 /mnt/usb
# 옵션과 함께 마운트
$ sudo mount -t ext4 -o ro,noexec /dev/sdc1 /mnt/backup
# 마운트 옵션:
# ro: 읽기 전용
# rw: 읽기/쓰기
# noexec: 실행 파일 실행 불가
# nosuid: setuid 무시
# user: 일반 사용자가 마운트 가능
# 언마운트
$ sudo umount /mnt/usb
# /etc/fstab - 부팅 시 자동 마운트 설정
# 장치 마운트포인트 타입 옵션 덤프 패스
/dev/sda1 / ext4 defaults 1 1
/dev/sda2 /home ext4 defaults 1 2
/dev/sdb1 /mnt/data ext4 defaults,nofail 0 2
UUID=xxxx-xxxx /mnt/usb vfat user,noauto 0 0
연습 문제¶
문제 1: 파일 속성 해석¶
ls -l 출력이 다음과 같을 때 각 필드를 해석하시오.
-rwxr-x--- 2 alice developers 8192 Mar 15 14:30 script.sh
정답 보기
- : 일반 파일 (d면 디렉토리, l이면 링크)
rwx : 소유자(alice) 권한 - 읽기/쓰기/실행
r-x : 그룹(developers) 권한 - 읽기/실행
--- : 기타 사용자 권한 - 없음
2 : 하드 링크 수 (이 파일에 대한 이름이 2개)
alice : 소유자
developers : 소유 그룹
8192 : 파일 크기 (8KB)
Mar 15 14:30 : 마지막 수정 시간
script.sh : 파일 이름
권한 숫자: 750 (7=rwx, 5=r-x, 0=---)
문제 2: 시스템 콜 시퀀스¶
파일 /tmp/log.txt에 "Hello World"를 추가(append)하는 시스템 콜 시퀀스를 작성하시오.
정답 보기
int fd = open("/tmp/log.txt", O_WRONLY | O_APPEND | O_CREAT, 0644);
// O_APPEND: 항상 파일 끝에 쓰기
// O_CREAT: 파일이 없으면 생성
// 0644: 권한 설정
if (fd == -1) {
perror("open failed");
exit(1);
}
const char* msg = "Hello World\n";
ssize_t written = write(fd, msg, strlen(msg));
if (written == -1) {
perror("write failed");
}
close(fd);
문제 3: 하드 링크 vs 심볼릭 링크¶
다음 시나리오에서 각 링크 유형의 동작을 설명하시오.
$ echo "original" > file.txt
$ ln file.txt hardlink.txt # 하드 링크
$ ln -s file.txt symlink.txt # 심볼릭 링크
$ rm file.txt
$ cat hardlink.txt
$ cat symlink.txt
정답 보기
$ cat hardlink.txt
original
→ 하드 링크는 같은 inode를 가리킴
→ file.txt 삭제해도 데이터는 여전히 존재
→ 링크 카운트가 1 감소했을 뿐
$ cat symlink.txt
cat: symlink.txt: No such file or directory
→ 심볼릭 링크는 "file.txt"라는 경로를 저장
→ file.txt가 삭제되면 끊어진 링크 (dangling link)
→ 가리키는 대상이 없어서 오류
하드 링크 특징:
- 같은 inode 번호 공유
- 원본과 동등한 지위
- 디렉토리에는 사용 불가
심볼릭 링크 특징:
- 별도의 inode (경로 문자열 저장)
- 원본에 의존적
- 디렉토리에도 사용 가능
- 다른 파일 시스템도 가리킬 수 있음
문제 4: 디렉토리 탐색¶
/home/user/docs/../code/./main.c 경로를 정규화하시오.
정답 보기
/home/user/docs/../code/./main.c
1. /home/user/docs 까지 이동
2. .. 으로 상위로 이동: /home/user
3. code 로 이동: /home/user/code
4. . 은 현재 디렉토리 (무시): /home/user/code
5. main.c 파일: /home/user/code/main.c
정규화된 경로: /home/user/code/main.c
Linux에서 확인:
$ realpath /home/user/docs/../code/./main.c
/home/user/code/main.c
문제 5: Open File Table¶
프로세스 A가 파일을 열고 fork()로 프로세스 B를 생성합니다. 두 프로세스가 같은 파일에 각각 100바이트를 write()하면 파일의 최종 크기는?
정답 보기
fork() 시 자식은 부모의 fd table을 복사합니다.
두 프로세스의 fd는 같은 open file entry를 공유합니다.
따라서 같은 offset을 공유합니다!
시나리오:
1. 부모 open(), offset = 0
2. fork(), 자식도 같은 open file entry 공유
3. 부모 write(100), offset = 100
4. 자식 write(100), offset = 200 (이어서 쓰기)
최종 파일 크기: 200 바이트
만약 자식이 별도로 open()했다면:
- 별도의 open file entry 생성
- 별도의 offset
- 둘 다 0부터 시작하면 덮어쓰기!
- 최종 크기: 100 바이트
주의: 실제로는 write()의 원자성과 버퍼링에 따라
결과가 다를 수 있음. O_APPEND 사용 권장.
다음 단계¶
17_File_System_Implementation.md에서 파일 시스템의 내부 구현을 배워봅시다!
참고 자료¶
- Silberschatz, "Operating System Concepts" Chapter 13
- Linux man pages:
open(2),read(2),write(2),stat(2) - POSIX 파일 시스템 표준
- The Linux Programming Interface by Michael Kerrisk