Skip to content

필사 모드: [운영체제] 13. 파일 시스템 인터페이스

한국어
0%
정확도 0%
💡 왼쪽 원문을 읽으면서 오른쪽에 따라 써보세요. Tab 키로 힌트를 받을 수 있습니다.
원문 렌더가 준비되기 전까지 텍스트 가이드로 표시합니다.

파일 시스템 인터페이스

파일 시스템은 운영체제에서 가장 눈에 띄는 부분 중 하나입니다.

사용자가 데이터를 저장하고 관리하는 논리적 인터페이스를 제공합니다.

이 글에서는 파일 개념, 접근 방법, 디렉토리 구조, 그리고 파일 보호 메커니즘을 살펴봅니다.

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 디렉토리에 설정되어 모든 사용자가 파일을 만들 수 있지만

다른 사용자의 파일은 삭제할 수 없게 합니다.

현재 단락 (1/276)

파일 시스템은 운영체제에서 가장 눈에 띄는 부분 중 하나입니다.

작성 글자: 0원문 글자: 6,337작성 단락: 0/276