본문 바로가기

[pintOS] 파일 시스템 찍먹하기

@정소민fan2025. 6. 8. 18:18

vm이 2주일치 과제였지만 1주일만에 끝낸 관계로 파일 시스템을 찍먹해보기로 결정했다. 사실 학부 때 파일 시스템을 제대로 공부하지 않았기도 했고, 동기들이 pintOS를 할 때 나만무 준비를 위해 다른 공부를 하는 것도 위화감을 조성할 수 있다고 생각해서 내린 결정이다. 

아래는 GPT가 번역을 해준 파일 시스템 gitbook이다.

https://verdant-bathtub-bae.notion.site/2097bb7778c980408c21d42da9fd62b4?source=copy_link

 

과제 설명서 | Notion

🗂️ Project 4: 파일 시스템

verdant-bathtub-bae.notion.site

구현에 들어가기에 앞서, 이론적인 개념과 구조체들을 먼저 살펴보자.

FAT

우리는 pintos의 파일 시스템을 fat 기반으로 바꾸어야 한다. 현재는 bitmap을 사용해서 디스크 내의 섹터들을 관리하고 있는데, 항상 섹터들을 연속적으로 할당해주어서 외부 단편화가 생길 수가 있다. 이제 파일의 내용을 담은 섹터들을 파편화시켜서 여기저기 흩어놓아야 하는데, 파편화된 섹터들을 관리하는 것이 File Allocate Table, FAT이다.

그림으로 보면 다음과 같다.

위와 같이 FAT는 각 파일의 조각들이 다음에 어디로 연결되어있는지 관리한다.

FAT에서 저 파일 조각 한 칸을 클러스터라고 한다. pintos에서 각 클러스터는 실제 디스크의 하나의 섹터와 일대일로 매핑된다.

FAT에서 클러스터들이 연결되어 있는 것을 클러스터 체인이라고 한다. EOC는 End of Chain, 이 클러스터 체인의 끝을 나타낸다.

  • File A -> FAT[0] -> 3, FAT[3] -> 5, FAT[5] -> EOC
  • File B -> FAT[1] -> 4, FAT[4] -> EOC
  • File C -> FAT[2] -> 6, FAT[6]

그러면 각 파일 클러스터 체인의 시작점은 어딘가에 저장되어야 한다. 이는 struct inode의 data이 start 필드에 저장되게 된다.

구조체 살펴보기

우리가 먼저 살펴봐야 할 구조체는 inode_disk와 inode 이 두개다.

inode_disk

struct inode_disk
{
	disk_sector_t start; /* 첫 클러스터 번호. */
	off_t length;		 /* 파일 크기(바이트). */
	unsigned magic;		 /* 매직 넘버. */
	bool isdir;
	char unused[496];
};
  1. start : 위에서 말했듯이 이 파일 클러스터 체인의 시작 번호다. 기존 파일 시스템에서는 disk_sector_t 자료형으로 되어있는데, 파일시스템을 구현을 위해 cluster_t로 바꿔주자. 둘다 같은 자료형을 타입만 다시 정의한거라 안바꿔도 크게 상관은 없다.
  2. length : 파일의 사이즈
  3. magic : 뭐냐 이건? 구분하기 위한 넘버인가... INODE_MAGIC 이라고 상수가 정의되어있는데, 여기에 대입된다.
  4. isdir : 이 아이노드가 디렉토리의 아이노드인지 구분하기 위한 인자다
  5. unused : inode_disk는 섹터 하나에 저장되어야 한다. 섹터 하나는 512바이트이기 떄문에 이를 위해 패딩값을 둔다.

하나 구분해야 될 게, start가 이 inode_disk가 저장되는 클러스터는 아니라는 것이다. 아이노드는 파일의 메타데이터를 위한 구조체일 뿐이고, inode_disk는 아이노드가 파일의 메타데이터를 디스크에 저장되기 편하도록 만들어진 구조체일 뿐이다. start는 이 아이노드를 메타데이터로 갖는 파일의 실제 데이터들이 저장된 클러스터 체인의 시작 번호일 뿐이다.

inode

struct inode
{
	struct list_elem elem;	/* inode 리스트의 요소. */
	disk_sector_t sector;	/* 디스크 상 위치(섹터 번호). */
	int open_cnt;			/* 열려 있는 횟수. */
	bool removed;			/* 삭제된 경우 true. */
	int deny_write_cnt;		/* 0이면 쓰기 허용, 0보다 크면 금지. */
	struct inode_disk data; /* inode 내용. */
};

 

그러면 inode 구조체는 대체 무엇이지? 이 inode는 파일이 오픈될 때 그 메타데이터들을 메모리에 상주시키기 위한 구조체일 뿐이다 !! 

  1. elem : pintos에서는 이 inode 구조체들을 연결리스트로 관리하기 때문에 필요하다
  2. sector : 이 섹터야말로 inode_disk가 저장되어있는 위치를 가리키는 섹터이다.
  3. open_cnt : 파일을 같은 프로세스든, 다른 프로세스든 여러번 열릴 수 있다. 열려있는 갯수를 세기위한 인자이다.
  4. removed : 얘는 아직 파악을 못했다. 이 파일이 닫혔을 때 true인가? 오픈 카운트가 0일 때 true인가?
  5. deny_write_cnt : 이 파일의 쓰기를 금지하기 위한 카운트이다
  6. data : 위에서 보았던 inode_disk를 디스크 섹터로부터 읽어 여기에 저장한다 !!

그러면 inode가 특정 섹터에 저장되어있다는 것을 대체 어떻게 알 수 있을까?? 자 파일 시스템을 다시 한번 생각해보자. 우리는 파일을 찾을 때나 저장할 때나 항상 루트 디렉토리부터 시작한다.(상대 경로도 가능하지만, 지금은 잊자)

루트 디렉토리의 아이노드가 담긴 섹터 번호나, 실제 데이터가 담긴 클러스터 번호는 이미 상수로 정의되어 있다. 그래서 어디서든 참조가 가능한 것이다!!

/* include/filesys/filesys.h */ 
#define ROOT_DIR_SECTOR 1 /* Root directory file inode sector. */

/* include/filesys/fat.h */
#define ROOT_DIR_CLUSTER 1    /* Cluster for the root directory */

 

 


이제 우리는 fat 시스템을 부팅하고, 사용하기 위한 구조체를 볼 것이다.

fat_boot

/* filesys/fat.c */

struct fat_boot
{
	unsigned int magic;				  /* FAT 파일 시스템임을 나타내는 숫자 */
	unsigned int sectors_per_cluster; /* 항상 1로 고정 */
	unsigned int total_sectors;		  /*디스크 전체 섹터 수*/
	unsigned int fat_start;			  /*FAT 영역의 시작 섹터 번호*/
	unsigned int fat_sectors;		  /* FAT 크기(섹터 단위) */
	unsigned int root_dir_cluster;	  /*루트 디렉토리의 시작 클러스터 번호*/
};
  1. sector_per_clusert : 원래의 FAT 시스템에서는 각 클러스터마다 여러개의 섹터가 할당되어있다고 한다. 하지만 pintos에서는 복잡도를 떨어뜨리기 위해 각 클러스터마다 하나의 섹터만 할당되도록 만들었다.
  2. total_sectors : 디스크 전체를 512 바이트로 나누어 총 섹터의 갯수를 구했다.
  3. fat_start : 항상 1로 고정된다.
  4. fat_sectors : FAT 테이블 자체가 disk에서 차지하는 섹터의 갯수다.
  5. root_dir_clustet : 위에서 나온 상수 ROOT_DIR_CLUSTER로 고정된다.

fat_fs

struct fat_fs
{
	struct fat_boot bs;		  /* FAT 부트 섹터: FAT 파일 시스템의 메타데이터 (섹터 수, 위치 등) */
	unsigned int *fat;		  /* FAT 테이블을 메모리에 로드한 포인터 (cluster 연결 정보를 담음) */
	unsigned int fat_length;  /* FAT 테이블의 섹터 수 (== bs.fat_sectors) */
	disk_sector_t data_start; /* 실제 파일/디렉터리 데이터가 저장되는 영역의 시작 섹터 번호 */
	cluster_t last_clst;	  /* FAT에서 최근에 할당된 마지막 클러스터 번호 (새 클러스터 할당 시 사용) */
	struct lock write_lock;	  /* FAT 갱신 시 동기화를 위한 락 (write 동시성 제어용) */
};

우리가 fat 시스템에서 사용할 정보들이 담긴 구조체이다. 우리는 이 정보들을 fat_init 함수를 통해 초기화되도록 구현해야 한다.

 

자 그러면 파일시스템 맛보기는 여기까지 !!

다음 포스팅은 구현기로

'크래프톤 정글' 카테고리의 다른 글

[pintOS] 파일 확장 기능 구현 - 1  (0) 2025.06.11
[pintOS] FAT 구현기  (0) 2025.06.10
[pintOS] Copy-on-write 구현기  (4) 2025.06.07
[pintOS] swap-in/out 구현기  (5) 2025.06.05
[pintOS] mmap 구현기  (0) 2025.06.05
정소민fan
@정소민fan :: 코딩은 관성이야

코딩은 관성적으로 해야합니다 즐거운 코딩 되세요

목차