Storage Virtualization
지난 시간까지 우리는 HDD와 SSD라는 하드웨어 자체에 대해 알아보았다. 회전하는 플래터, 전자를 가두는 셀, 그리고 이들을 제어하기 위한 하드웨어적인 컨트롤러들의 이야기였다. 하지만 우리가 실제로 컴퓨터를 사용할 때 "3번 트랙 5번 섹터에 데이터를 써줘"라고 명령하지 않는다. 대신 "보고서.docx를 저장해 줘"라고 말한다.
여기서 나오는 개념이 바로 운영체제(OS)의 스토리지 가상화(Storage Virtualization)다. 오늘은 OS가 어떻게 복잡한 저장 장치를 우리에게 친숙한 '파일(File)'과 '디렉토리(Directory)'라는 개념으로 하는 Virtualize 하는지 정리해보고자 한다.
| Hierarchical arrangement of File & Directory |
1. 스토리지 가상화의 두 개념 (File, Directory), 그리고 Inode
운영체제는 물리적인 저장 매체(Tape, CD-ROM, HDD, SSD 등)를 감추고, 사용자에게는 논리적인 뷰(Logical View)를 제공한다. 이 가상화의 핵심은 크게 두 가지 개념으로 나뉘며, , 그 중심에는 Inode가 있다.
(1) 파일 (File): 바이트의 선형 배열
사용자 입장에서 파일은 그저 '읽고 쓸 수 있는 바이트들의 선형 배열(Linear array of bytes)' 일 뿐이다. 텍스트인지 바이너리인지는 OS가 아니라 파일을 만든 창조자(Creator)가 정의한다.
하지만 OS 내부적으로 파일은 사람이 부르는 이름 대신, 주민등록번호와 같은 고유 식별자인 Inode Number로 관리된다. Inode는 파일 시스템의 가장 중요한 자료구조로, 파일 이름과 분리되어 존재한다.
메타데이터의 저장소: Inode는 파일의 크기, 소유자, 권한, 생성/수정/접근 시간 등 파일에 대한 모든 정보(메타데이터)를 담고 있다.
데이터 위치 포인터: 파일의 실제 데이터가 디스크의 어느 블록에 저장되어 있는지 가리키는 포인터 정보를 포함한다.
파일 시스템의 핵심: 파일 이름은 단지 Inode를 가리키는 껍데기일 뿐이며, 실제 접근과 관리는 Inode를 통해 이루어진다. 따라서 하나의 Inode를 여러 파일 이름이 가리키는 하드 링크(Hard Link) 가 가능하다.
(2) 디렉토리 (Directory): (이름, Inode) 쌍의 목록
디렉토리 또한 근본적으로는 파일이다. 다만 그 내용이 조금 특별할 뿐이다.
디렉토리는 (사용자가 읽을 수 있는 이름, Inode 번호) 쌍의 리스트를 담고 있는 파일이다.
우리가 흔히 보는 폴더 구조(트리, 그래프 등)는 이 디렉토리 파일들이 서로를 참조하며 만들어내는 계층적 구조다.
디렉토리 자체도 Inode 번호로 식별된다.
2. 계층적 이름 공간 (Hierarchical Namespace)
왜 우리는 선형적인 메모리 주소 대신 복잡한 계층 구조를 사용할까? 수백만 개의 파일을 평면적으로(Flat) 관리한다고 상상해 보면 답은 명확하다. 이름 충돌을 피하고 데이터를 논리적으로 그룹화하기 위해 디렉토리 트리(Directory Tree) 가 탄생했다.
| Unix Directory Tree |
루트(Root, /): 모든 것의 시작점이다.
경로(Pathname):
절대 경로(Absolute):
/home/dgist/file1처럼 루트부터 시작하는 전체 경로.상대 경로(Relative):
dgist/file1처럼 현재 위치(Working Directory)를 기준으로 하는 경로.
이 구조 덕분에 우리는 물리적인 디스크가 몇 개인지, 어떤 방식으로 연결되어 있는지 신경 쓰지 않고 논리적인 주소만으로 데이터에 접근할 수 있다.
3. UNIX 파일 시스템의 철학: 모든 것은 파일이다
UNIX 시스템의 가장 중요한 철학 중 하나는 "모든 것은 파일이다(Everything is a file)" 라는 점이다.
일반적인 데이터 파일뿐만 아니라, 키보드, 모니터, 프린터 같은 I/O 장치들도 파일로 취급된다. (예:
/dev/stdin,/dev/stdout)프로세스 간 통신(IPC) 채널인 파이프나 소켓도 파일처럼 다룬다.
심지어 커널의 데이터 구조도
/proc파일 시스템을 통해 파일처럼 읽을 수 있다.
| Unix File |
이 철학 덕분에 개발자는 장치의 특성을 몰라도 open(), read(), write() 같은 통일된 인터페이스(System Call) 하나로 모든 입출력을 처리할 수 있다. 이는 시스템의 복잡도를 획기적으로 낮춰주는 설계의 미학이다.
4. Inode의 관점에서 본 파일 시스템 API
파일 시스템을 제대로 이해하려면, 파일의 '이름'이 아니라 파일의 '실체'인 Inode에 집중해야 한다. 주요 시스템 콜들이 Inode와 어떻게 상호작용하는지 살펴보자. (시스템 콜에 대한 자세한 내용은 시스템프로그래밍의 Unix I/O를 참고하자)
(1) 생성과 열기: open()
int fd = open("foo", O_CREAT | O_WRONLY | O_TRUNC);
파일을 연다는 것은, 파일 이름("foo")을 통해 해당 파일의 Inode를 찾고, 커널 내의 열린 파일 테이블(Open file table)에 엔트리를 만드는 과정이다.
성공 시 반환되는 파일 디스크립터(File Descriptor, fd) 는 이 테이블을 가리키는 인덱스다.
(2) 읽기와 쓰기: read() / write()
read(fd, buffer, size);
write(fd, buffer, size);
현재 오프셋(Current Offset)에서 데이터를 읽거나 쓰고, 그만큼 오프셋을 이동시킨다. 순차적 접근(Sequential Access)이 기본이다.
(3) 임의 접근: lseek()
파일의 특정 위치로 오프셋을 점프시킨다. 이를 통해 임의 접근(Random Access) 이 가능해진다. 데이터베이스 같은 애플리케이션에서 필수적인 기능이다.
(4) 원자적 이름 변경: rename()
rename("foo.txt.tmp", "foo.txt");
이름 변경은 원자적(Atomic) 이다. 즉, 이름이 바뀌거나 안 바뀌거나 둘 중 하나지, 중간 상태는 없다. 시스템이 셧다운 되더라도 파일이 손상되지 않도록 업데이트할 때 유용하다.
(5) 메타데이터 확인: stat()
파일의 크기, 소유자, 권한, 수정 시간, 그리고 가장 중요한 Inode 번호 등 파일의 '신상 정보(Metadata)'를 보여준다. 이 정보들은 디스크 내의 Inode 구조체에 저장된다.
5. 링크(Links): 하나의 파일, 여러 개의 이름
파일 시스템에서 흥미로운 개념 중 하나는 '링크'다. 같은 파일을 가리키는 여러 가지 방법을 제공하는데, 크게 하드 링크와 심볼릭 링크로 나뉜다.
(1) 하드 링크 (Hard Link)
개념: 디렉토리 엔트리에 새로운 이름만 추가하고, 원본 파일과 동일한 Inode 번호를 가리키게 한다.
특징: 별도의 파일 복사가 일어나지 않는다.
rm으로 파일을 지워도 실제로는 '참조 카운트(Reference Count)'만 줄어든다. 카운트가 0이 되어야 진짜 데이터가 삭제된다(Unlink).제약: 같은 파일 시스템 내에서만 생성 가능하며, 디렉토리에 대해서는 만들 수 없다(순환 참조 방지).
(2) 심볼릭 링크 (Symbolic Link / Soft Link)
개념: 원본 파일의 '경로(Pathname)'를 담고 있는 특별한 파일을 새로 만든다. (윈도우의 바로가기와 유사)
특징: 원본과 다른 Inode 번호를 가진다. 원본 파일이 삭제되면 연결이 끊긴다(Dangling reference).
Dangling reference 장점: 파티션을 넘나들며 링크를 걸 수 있고, 디렉토리도 링크할 수 있어 더 유연하다.
Symbolic Link
5. 소유권과 권한 (Ownership & Permissions)
Inode에는 파일의 위치뿐만 아니라 "누가 이 파일에 접근할 수 있는가?"에 대한 정보도 담겨 있다. 이는 다중 사용자 시스템인 UNIX의 핵심 보안 모델이다.
(1) 소유권 (Ownership)
모든 파일은 특정 사용자와 그룹에 속한다.
User (Owner): 파일을 소유한 사용자.
chown명령어로 변경 가능하다.Group: 파일에 대한 권한을 공유하는 사용자 그룹.
chgrp명령어로 변경 가능하다.
(2) 권한 (Permissions)
권한은 User, Group, Others(나머지) 세 주체에 대해 각각 세 가지 권한을 부여한다.
Read (r): 파일 내용 읽기 (디렉토리의 경우, 파일 목록 읽기)
Write (w): 파일 내용 수정 (디렉토리의 경우, 파일 생성/삭제)
Execute (x): 파일 실행 (디렉토리의 경우, 해당 디렉토리로 이동 가능 여부)
(3) 비트 표현 (Bit Representation)
권한은 보통 3비트씩 묶어 8진수로 표현한다. (예: rwx = 4+2+1 = 7)
rwx r-x --x(User: 읽기/쓰기/실행, Group: 읽기/실행, Others: 실행만 가능)이 정보는 Inode의
st_mode필드에 저장되어 OS가 접근 제어를 수행하는 근거가 된다.
마무리
파일과 디렉토리라는 개념은 우리가 일상 속에서 무의식적으로 사용하고 있었지만, 그 뒤에는 하드웨어의 복잡성을 숨기기 위한 추상화된 설계가 숨어 있다. 선형적인 바이트 배열로 데이터를 다루게 해주고, 계층적 이름으로 질서를 부여하며, Inode를 통해 관리의 효율성을 높였다.
이를 통해 이제 우리는 데이터가 실제로 어떻게 디스크에 놓이고 관리되는지 Inode를 통해 간접적으로나마 꿰뚫어 볼 수 있게 되었다. 다음 시간에는 파일 시스템이 어떻게 실행되는지 더 자세히 알아보고자 한다.
추천글:
[운영체제] Operating System 전체 포스팅 모음집
[운영체제] I/O 시스템, HDD, Disk Scheduling, RAID | CPU와 디스크의 물리적 속도 차이를 극복하는 설계
[시스템프로그래밍] Unix I/O & Signal - File I/O, Network I/O, Signal