- Authors

- Name
- Youngju Kim
- @fjvbn20031
파일 시스템 인터페이스
파일 시스템은 운영체제에서 가장 눈에 띄는 부분 중 하나입니다. 사용자가 데이터를 저장하고 관리하는 논리적 인터페이스를 제공합니다. 이 글에서는 파일 개념, 접근 방법, 디렉토리 구조, 그리고 파일 보호 메커니즘을 살펴봅니다.
1. 파일 개념
파일은 보조 저장장치에 기록된 관련 정보의 이름 있는 집합입니다.
파일 속성
| 속성 | 설명 |
|---|---|
| 이름(Name) | 사람이 읽을 수 있는 식별자 |
| 식별자(Identifier) | 파일 시스템 내 고유 번호 (inode 번호 등) |
| 유형(Type) | 확장자 또는 매직 넘버로 식별 |
| 위치(Location) | 장치상 물리적 위치 포인터 |
| 크기(Size) | 현재 파일 크기 (바이트, 블록) |
| 보호(Protection) | 읽기, 쓰기, 실행 권한 |
| 타임스탬프 | 생성, 수정, 접근 시간 |
| 소유자(Owner) | 파일 소유 사용자 |
파일 연산
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
int main() {
// 1. 파일 생성 및 열기
int fd = open("example.txt", O_CREAT | O_WRONLY | O_TRUNC, 0644);
if (fd == -1) {
perror("open failed");
return 1;
}
// 2. 파일 쓰기
const char *data = "Hello, File System!";
write(fd, data, 19);
// 3. 파일 닫기
close(fd);
// 4. 읽기 모드로 다시 열기
fd = open("example.txt", O_RDONLY);
// 5. 파일 읽기
char buffer[256];
ssize_t bytes_read = read(fd, buffer, sizeof(buffer) - 1);
buffer[bytes_read] = '\0';
printf("읽은 내용: %s\n", buffer);
// 6. 파일 위치 이동 (seek)
lseek(fd, 0, SEEK_SET); // 파일 시작으로 이동
// 7. 파일 닫기
close(fd);
// 8. 파일 삭제
unlink("example.txt");
return 0;
}
오픈 파일 테이블
운영체제는 열린 파일을 관리하기 위해 테이블을 유지합니다.
프로세스 A 프로세스 B
┌──────────────┐ ┌──────────────┐
│ fd 테이블 │ │ fd 테이블 │
│ 0: stdin │ │ 0: stdin │
│ 1: stdout │ │ 1: stdout │
│ 2: stderr │ │ 2: stderr │
│ 3: ─────────┐│ │ 3: ─────────┐│
└─────────────┘│ └─────────────┘│
↓ ↓
┌──────────────────────────────────────┐
│ 시스템 전역 오픈 파일 테이블 │
│ ┌─────────────────────────────────┐ │
│ │ 파일 A: offset=100, refCount=1 │ │
│ │ 파일 B: offset=0, refCount=2 │ ← 공유│
│ └─────────────────────────────────┘ │
└──────────────────────────────────────┘
│
↓
┌──────────────────┐
│ inode 테이블 │
│ (디스크 위치 등) │
└──────────────────┘
2. 접근 방법
순차 접근 (Sequential Access)
파일의 처음부터 끝까지 순서대로 접근합니다. 테이프 모델에 기반합니다.
┌───┬───┬───┬───┬───┬───┬───┐
│ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ ← 레코드
└───┴───┴───┴───┴───┴───┴───┘
↑
현재 위치 포인터
(앞으로만 이동하거나 되감기)
// 순차 접근 예시
FILE *fp = fopen("log.txt", "r");
char line[1024];
// 처음부터 끝까지 순서대로 읽기
while (fgets(line, sizeof(line), fp) != NULL) {
printf("%s", line);
}
// 되감기 (처음으로)
rewind(fp);
fclose(fp);
직접 접근 (Direct / Random Access)
임의의 위치에 직접 접근할 수 있습니다. 디스크 모델에 기반합니다.
┌───┬───┬───┬───┬───┬───┬───┐
│ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ ← 블록 번호
└───┴───┴───┴───┴───┴───┴───┘
↑ ↑ ↑
직접 접근 가능 (어느 블록이든)
// 직접 접근 예시 - 데이터베이스의 레코드 접근
#include <stdio.h>
typedef struct {
int id;
char name[50];
double balance;
} Account;
// 특정 레코드 번호로 직접 접근
Account read_account(FILE *fp, int record_num) {
Account acc;
// 레코드 번호로 오프셋 계산하여 직접 이동
fseek(fp, record_num * sizeof(Account), SEEK_SET);
fread(&acc, sizeof(Account), 1, fp);
return acc;
}
void write_account(FILE *fp, int record_num, Account *acc) {
fseek(fp, record_num * sizeof(Account), SEEK_SET);
fwrite(acc, sizeof(Account), 1, fp);
}
인덱스 접근
인덱스 파일을 통해 데이터 파일의 레코드를 찾는 방식입니다.
인덱스 파일: 데이터 파일:
┌──────┬─────────┐ ┌─────────────────┐
│ 키 │ 포인터 │ │ 레코드 0 │
├──────┼─────────┤ ┌───→│ Smith, 1000 │
│ Kim │ ──────────────┐ │ ├─────────────────┤
│ Lee │ ────────────┐ │ │ │ 레코드 1 │
│ Park │ ──────┐ │ │ │ │ Lee, 2000 │
│ Smith│ ──────┼─────┼─┼──┘ ├─────────────────┤
└──────┴───────┘ │ │ │ 레코드 2 │
│ └──────→│ Kim, 3000 │
│ ├─────────────────┤
└────────→│ 레코드 3 │
│ Park, 4000 │
└─────────────────┘
3. 디렉토리 구조
디렉토리는 파일 이름을 해당 파일의 메타데이터로 매핑하는 테이블입니다.
단일 수준 디렉토리
┌──────────────────────────────────────┐
│ 루트 디렉토리 │
│ │
│ cat bo a test data mail hex │
│ │ │ │ │ │ │ │ │
│ ○ ○ ○ ○ ○ ○ ○ │
└──────────────────────────────────────┘
모든 파일이 같은 디렉토리에 존재
→ 이름 충돌 문제, 그룹화 불가
2단계 디렉토리
┌────────────────────────────────────┐
│ 마스터 파일 디렉토리 │
│ │
│ user1 user2 user3 │
│ │ │ │ │
│ ┌──┴──┐ ┌──┴──┐ ┌──┴──┐ │
│ │ UFD │ │ UFD │ │ UFD │ │
│ │cat a│ │cat b│ │ a │ │
│ └─────┘ └─────┘ └─────┘ │
└────────────────────────────────────┘
사용자별 별도 디렉토리 → 이름 충돌 해결
트리 구조 디렉토리
현대 파일 시스템의 기본 구조입니다.
/ (루트)
╱ │ ╲
home etc var
╱ ╲ │ │
user1 user2 passwd log
│ │ │
docs code syslog
│ │ │
a.txt b.txt main.c
# 트리 구조에서의 경로
# 절대 경로: 루트부터 시작
/home/user1/docs/a.txt
# 상대 경로: 현재 디렉토리 기준
# 현재 디렉토리가 /home/user1 일 때
docs/a.txt
비순환 그래프 디렉토리
파일이나 디렉토리를 여러 곳에서 공유할 수 있습니다. (하드 링크, 심볼릭 링크)
/
│
home
╱ ╲
user1 user2
│ │
shared ──┘ (심볼릭 링크 또는 하드 링크)
│
project
// 하드 링크 생성 (같은 inode를 가리킴)
#include <unistd.h>
int main() {
// 하드 링크: 동일한 inode에 대한 새 이름
link("/home/user1/shared/project",
"/home/user2/project");
// 심볼릭(소프트) 링크: 경로명을 가리키는 특수 파일
symlink("/home/user1/shared/project",
"/home/user2/project_link");
return 0;
}
하드 링크 vs 심볼릭 링크
| 특성 | 하드 링크 | 심볼릭 링크 |
|---|---|---|
| inode | 원본과 동일 | 별도 inode |
| 파일 시스템 간 | 불가 | 가능 |
| 디렉토리 링크 | 불가 (루프 방지) | 가능 |
| 원본 삭제 시 | 데이터 유지 | 깨진 링크 |
| 크기 | 디렉토리 엔트리만 | 경로명 크기 |
4. 파일 공유
다중 사용자 환경에서의 파일 공유
사용자 A (소유자)
│
├── 파일 X (읽기/쓰기/실행)
│ │
│ ├── 사용자 B (그룹) → 읽기/실행
│ │
│ └── 기타 사용자 → 읽기만
│
└── 그룹: developers
파일 잠금 (File Locking)
#include <fcntl.h>
#include <unistd.h>
int main() {
int fd = open("shared_data.txt", O_RDWR);
struct flock lock;
lock.l_type = F_WRLCK; // 쓰기 잠금
lock.l_whence = SEEK_SET;
lock.l_start = 0; // 시작 오프셋
lock.l_len = 0; // 전체 파일
// 잠금 획득 (다른 프로세스는 대기)
fcntl(fd, F_SETLKW, &lock);
// 파일 작업 수행
write(fd, "critical data", 13);
// 잠금 해제
lock.l_type = F_UNLCK;
fcntl(fd, F_SETLK, &lock);
close(fd);
return 0;
}
잠금 유형
| 유형 | 설명 | 호환성 |
|---|---|---|
| 공유 잠금 (Read Lock) | 여러 프로세스가 동시 읽기 가능 | 공유 잠금끼리 호환 |
| 배타 잠금 (Write Lock) | 하나의 프로세스만 접근 가능 | 모든 잠금과 비호환 |
| 강제 잠금 (Mandatory) | 커널이 강제 적용 | 시스템 의존 |
| 권고 잠금 (Advisory) | 프로세스가 자발적 준수 | POSIX 기본 |
5. 보호 (Protection)
접근 제어 리스트 (ACL)
파일: /project/secret.txt
ACL (Access Control List):
┌──────────┬─────────────────┐
│ 사용자 │ 권한 │
├──────────┼─────────────────┤
│ admin │ 읽기, 쓰기, 실행 │
│ user1 │ 읽기, 쓰기 │
│ user2 │ 읽기 │
│ group:dev│ 읽기, 실행 │
│ others │ (없음) │
└──────────┴─────────────────┘
UNIX 권한 모델
UNIX는 소유자(owner), 그룹(group), 기타(others)의 3가지 범주와 읽기(r), 쓰기(w), 실행(x)의 3가지 권한을 조합합니다.
$ ls -l example.txt
-rwxr-x--- 1 user1 developers 1024 Mar 19 10:00 example.txt
│││││││││
│├┤├┤├┤
│ │ │ └── others: --- (권한 없음)
│ │ └──── group: r-x (읽기, 실행)
│ └────── owner: rwx (읽기, 쓰기, 실행)
└──────── 파일 유형: - (일반 파일)
#include <sys/stat.h>
int main() {
// 8진수로 권한 설정
// 7=rwx, 5=r-x, 0=---
chmod("example.txt", 0750);
// 소유자 변경
chown("example.txt", 1000, 100); // uid=1000, gid=100
// 새 파일 생성 시 기본 권한 마스크
umask(0022); // 기본 권한에서 group/others의 쓰기 제거
return 0;
}
특수 권한 비트
| 비트 | 심볼 | 효과 |
|---|---|---|
| setuid | s (소유자 실행) | 실행 시 파일 소유자 권한으로 실행 |
| setgid | s (그룹 실행) | 실행 시 파일 그룹 권한으로 실행 |
| sticky bit | t (기타 실행) | 디렉토리 내 파일은 소유자만 삭제 가능 |
# setuid 설정 예시 (passwd 명령어)
# 일반 사용자도 root 소유 파일을 수정 가능
$ ls -l /usr/bin/passwd
-rwsr-xr-x 1 root root 68208 ... /usr/bin/passwd
^
setuid 비트 설정됨
# sticky bit 설정 예시 (/tmp 디렉토리)
$ ls -ld /tmp
drwxrwxrwt 15 root root 4096 ... /tmp
^
sticky bit 설정됨
6. 정리
- 파일 개념: 이름 있는 정보 집합으로, 속성과 연산이 정의됨
- 접근 방법: 순차(테이프 모델), 직접(디스크 모델), 인덱스 접근
- 디렉토리 구조: 단일 수준에서 비순환 그래프까지 발전. 현대 시스템은 트리 + 링크
- 파일 공유: 잠금 메커니즘(공유/배타, 강제/권고)으로 동시 접근 관리
- 보호: UNIX 권한 모델(rwx, owner/group/others)과 ACL로 접근 제어
퀴즈: 파일 시스템 인터페이스
Q1. 하드 링크와 심볼릭 링크의 차이점을 설명하세요.
A1. 하드 링크는 원본 파일과 동일한 inode를 공유하므로 원본이 삭제되어도 데이터에 접근할 수 있습니다. 심볼릭 링크는 별도의 inode를 가지며 경로명을 저장하므로, 원본이 삭제되면 깨진 링크가 됩니다. 하드 링크는 파일 시스템 경계를 넘을 수 없고 디렉토리에 적용할 수 없지만, 심볼릭 링크는 두 가지 모두 가능합니다.
Q2. UNIX 파일 권한 0754는 어떤 의미인가요?
A2. 소유자(7=rwx): 읽기, 쓰기, 실행 모두 가능. 그룹(5=r-x): 읽기와 실행 가능, 쓰기 불가. 기타(4=r--): 읽기만 가능.
Q3. sticky bit의 용도는 무엇인가요?
A3. sticky bit이 디렉토리에 설정되면, 해당 디렉토리 내의 파일은 파일의 소유자, 디렉토리의 소유자, 또는 root만 삭제할 수 있습니다. /tmp 디렉토리에 설정되어 모든 사용자가 파일을 만들 수 있지만 다른 사용자의 파일은 삭제할 수 없게 합니다.