. [PintOS, Project 4] fat.c 공부
본문 바로가기
Pintos Project/Project 4

[PintOS, Project 4] fat.c 공부

by 불냥이_ 2021. 3. 3.

※ 이 글은 Project 4를 본격적으로 시작하기 전, 추론의 영역입니다.

 

/* FAT FS */
struct fat_fs {
	struct fat_boot bs;
	unsigned int *fat;
	unsigned int fat_length; 
	disk_sector_t data_start;
	cluster_t last_clst;
	struct lock write_lock;
};

FAT FS라고 함은 한 파일 내에서 사용하는 FAT인 것 같다.

fat_boot :  전체 파일 시스템의 정보가 담겨있는 것이라고 생각된다.

fat_length : FS 안에 들어가있는 sector의 갯수

data_start : 비어있는 첫 sector

last_clst : file이 할당받은 cluster 중, 마지막 cluster? 

write_lock : file은 오직 하나의 프로세스만 열 수 있어서 lock이 있는 것일까?

 

 

 

void
fat_init (void) {
	fat_fs = calloc (1, sizeof (struct fat_fs));
	if (fat_fs == NULL)
		PANIC ("FAT init failed");

	// Read boot sector from the disk
	unsigned int *bounce = malloc (DISK_SECTOR_SIZE);
	if (bounce == NULL)
		PANIC ("FAT init failed");
	disk_read (filesys_disk, FAT_BOOT_SECTOR, bounce);
	memcpy (&fat_fs->bs, bounce, sizeof (fat_fs->bs));
	free (bounce);

	// Extract FAT info
	if (fat_fs->bs.magic != FAT_MAGIC)
		fat_boot_create ();
	fat_fs_init ();
}

fat_fs는 메모리 상에 올라가 있는 물건이다. 그래서 calloc (임의 사이즈를 여러번 malloc 받을 수 있도록 하는 함수)을 받는다.

 

1. sector 크기만큼 malloc을 받는다.

2. disk_read()로, 디스크의 FAT_BOOT_SECTOR에 있는 내용을 bounce로 옮긴다. 

여기서 fat.h를 보면 FAT_BOOT_SECTOR는 0으로 고정되어있다. 그렇다면 FAT_BOOT_SECTOR는 disk 전체를 다루는 FAT_BOOT인 것일까. 그렇다면 fat_fs도 한 파일 만이 아닌, 파일 시스템 디스크 전체를 다루는 것 같다.

3. 그리고 memcpy()로 bounce의 내용을 bounce의 크기만큼 fat_fs->bs로 복사한다. 아마, bounce의 첫번째 구조체가 fat_boot이라서 이렇게 하면, fat_fs가 복사되는 것 같다.

4. fat_fs를 복사했으면, bounce를 free()해준다.

 

 

void
fat_create (void) {
	// Create FAT boot
	fat_boot_create ();
	fat_fs_init ();

	// Create FAT table
	fat_fs->fat = calloc (fat_fs->fat_length, sizeof (cluster_t));
	if (fat_fs->fat == NULL)
		PANIC ("FAT creation failed");

	// Set up ROOT_DIR_CLST
	fat_put (ROOT_DIR_CLUSTER, EOChain);

	// Fill up ROOT_DIR_CLUSTER region with 0
	uint8_t *buf = calloc (1, DISK_SECTOR_SIZE);
	if (buf == NULL)
		PANIC ("FAT create failed due to OOM");
	disk_write (filesys_disk, cluster_to_sector (ROOT_DIR_CLUSTER), buf);
	free (buf);
}

fat_boot_create()로 fat_fs->bs를 만들어준다.

fat_fs_init() : fat_fs를 만들어준다.

 

fat_fs->fat : cluster 크기 * cluster 갯수만큼의 메모리를 할당한다. 즉, fat에는 디스크의 cluster의 주소 정보가 모두 들어간다는 것이다. (cluster 그 자체는 디스크의 섹터를 가리키는 주소를 의미한다.)

 

fat_put() : ROOT_DIR_CLUSTER를 EOChain으로 바꾼다는 말인데, 양쪽 다 cluster_t가 들어간다. 현재

ROOT_DIR_CLUSTER에는 1이 들어가있다. 이것은 idx 1의 inode? 는 ROOT_DIR_CLUSTER가 들어가있는데 여기에 EOchain을 넣는다는 말인데, 현재 디스크에는 아무것도 없으므로 시작 FAT? 이 가리키는 cluster는 바로 End Of Chain이 된다는 말 같다 (EOC는 0x0FFFFFFF의 주소 형식으로 되어있다.). 개념을 정리하기가 어렵다. 

 

지금 내가 생각하는 FAT 시스템은 이렇다. 

 

 그리고 fat은 일종의 array이고, cluster_t는 인덱스를 말한다. 그리고, fat[clst]는 disk sector의 번호를 가리키거나 혹은 다음 블록을 나타내는 것이 아닐까 싶다. 그러고보니 FAT의 정의에 block의 시작 부분에 다음 블록의 번호를 기록한다고 되어있었고, 마지막 block에는 EOF가 있었다. 

 

 그렇다면, disk sector 갯수와 fat의 배열 갯수는 1:1 대응하지 않을까? 한번 살펴보자.

우선 fat_create()에서 cluster_t * cluster_t 갯수로 받았다. 그런데 현재, cluster는 sectore 1개를 나타내므로 1:1 대응을 하고 있는 것 같다. 이 가설이 맞다면 일단 디스크 섹터를 기준으로 생각해도 되지 않을까.

 

디스크의 첫번째 섹터에는 FAT_BOOT_SECTOR가 들어가있다. 아마 해당 디스크에 대한 정보가 들어가있을 것 같다. 이는 fat_fs->bs에 나타나있는데, 

/* Should be less than DISK_SECTOR_SIZE */
struct fat_boot {
	unsigned int magic;
	unsigned int sectors_per_cluster; /* Fixed to 1 */
	unsigned int total_sectors;
	unsigned int fat_start;
	unsigned int fat_sectors; /* Size of FAT in sectors. */
	unsigned int root_dir_cluster;
};

 반드시 disk_sector_size보다 작아야한다고 하니 sector 한 개를 잡아먹는 것 같다. 

 

그리고 idx 1 sector에는 ROOT_DIR_CLUSTER가 들어가는데, 루트 디렉토리가 들어가있는 것 같다. 여기서부터 우리는 시작할 수 있을 것이다. (디렉토리는 하나의 파일이고, 이 디렉토리에 들어가는 파일들의 첫 포인터를 담아놓는 것일 것이다.) 

 

 

 

 어떤 디렉토리 안에 sale-R이라는 파일이 들어있고, 거기에는 4라는 정보도 같이 있다. 이말은 sale-R은 4번 섹터부터 시작한다는 것이다. 하지만 이 정보만으로는 4번 말고 어떤 섹터가 sale-R의 정보를 담아놓고 있는지는 알 수 없다. 그래서 FAT[4]로 들어가본다. 4에는 9가 있고, 9에는 6이 있고, 6에는 10이 있고, 10에는 12가 있고, 12에는 EOF가 있다.

 

 즉, Sale-R은 섹터 4, 9, 6, 10, 12 순으로 연결한 파일이라는 것이다. 

 

 추측으로는 디렉토리는 EOF가 있을 것 같고, (디렉토리가 엄청 크다면 다음 idx를 가질 수도 있겠지만) 디렉토리 안에 파일 이름과 시작 sector idx가 있는게 아닐까 생각한다.

 

  다시 함수로 돌아와서 fat_boot_create를 보자.

 

void
fat_boot_create (void) {
	unsigned int fat_sectors =
	    (disk_size (filesys_disk) - 1)
	    / (DISK_SECTOR_SIZE / sizeof (cluster_t) * SECTORS_PER_CLUSTER + 1) + 1;
	fat_fs->bs = (struct fat_boot){
	    .magic = FAT_MAGIC,
	    .sectors_per_cluster = SECTORS_PER_CLUSTER,
	    .total_sectors = disk_size (filesys_disk),
	    .fat_start = 1,
	    .fat_sectors = fat_sectors,
	    .root_dir_cluster = ROOT_DIR_CLUSTER,
	};
}

 magic : 이 디스크가 잘못되어서 이상하게 오염되었는지를 판단하는 기준이다. 

 sectors_per_clusters : SECTORS_PER_CLUSTERS 를 적는데, 이는 1로 고정되어있다.

 total_sectors : filesys_disk가 가지고 있는 sector의 갯수다.

 fat_start : 0번 sector는 디스크의 정보를 가지고 있었다. 그리고 1번 sector는 root_dir를 말하는 것이었는데, 여기서부터 써도 되나? 루트 디렉토리 기준이라 그런가? 

 fat_sectors : Size of FAT in sectors 라고 되어있는데 sectors들이 가진 FAT의 크기의 총합인가?

 root_dir_cluster : root_dir을 가리키므로 1이다.

 

 

 뭔가 감이 오는데, 다음 fat_create()를 보자

 

void
fat_create (void) {
	// Create FAT boot
	fat_boot_create ();
	fat_fs_init ();

	// Create FAT table
	fat_fs->fat = calloc (fat_fs->fat_length, sizeof (cluster_t));
	if (fat_fs->fat == NULL)
		PANIC ("FAT creation failed");

	// Set up ROOT_DIR_CLST
	fat_put (ROOT_DIR_CLUSTER, EOChain);

	// Fill up ROOT_DIR_CLUSTER region with 0
	uint8_t *buf = calloc (1, DISK_SECTOR_SIZE);
	if (buf == NULL)
		PANIC ("FAT create failed due to OOM");
	disk_write (filesys_disk, cluster_to_sector (ROOT_DIR_CLUSTER), buf);
	free (buf);
}

 FAT를 만드는 것 같은데, fat_init()이랑은 좀 다르다. 다시 fat_init()을 정리하고 가자. 이 함수의 목적은 fat_fs를 만들고, 디스크의 정보를 fat_fs->bs에 담는 것이다.

 

 그리고 여기에는 fat_fs_init()을 사용하는데, 이는 fat_length와 fat_fs의 data_start 를 초기화 하는 것이다. 이 정보는 fat_fs->bs에서 꺼내오면 될 것 같은데. data_start는 비어있는 첫 sector를 나타내는 것 같다. (당연히 디렉토리는 포함되지 않겠지. 아닌가? fat_fs->bs에 fat_start를 써야하나? 그런데 1은 root_dir인데. 뭔가 이상하다.)

 

 그리고 calloc으로 fat에 cluster_t (정수) 개만큼 공간을 할당받는다. 그렇다면 fat_length는 디스크 안의 cluster 갯수가 맞는 것 같다.

 

 fat_put()으로 root_dir_cluster (idx : 1)에 EOC를 넣는다. EOC는 주소값이었는데, 시스템적으로 이 주소에 도달하면 끝이라고 인식해주는 뭔가가 있나보다.

 

 그리고 buf에 sector 한개 크기만큼 할당을 받는다. 

 disk_wirte()로 buf에 있는 것을 cluster_to_sector 함수를 사용하여 clst에 대응하는 sector num으로 바꿔줘서 (현재 1cluster는 1sector에 해당하니 1:1대응한다.) ,  root_dir에 해당하는 섹터에 buf의 내용을 집어넣는데, 현재 buf은 막 할당받은 곳 아닌가? 그런데 주석을 보니

Fill up ROOT_DIR_CLUSTER region with 0라고 적혀져있다. root_dir의 sector를 0으로 채워놓나보다.

 

즉, fat_create는 FAT를 만들고 fat_fs->fat에 그 주소를 저장한다. 하지만 실제로 만드는 작업은 하지 않고, 메모리만 할당받고 아무거도 하지 않는다. 이 작업을 fat_fs_init()이 하는 것인가? 그런데 위치상으로는 calloc()다음에 와야할 거 같은데.. 아니면, disk에 직접 만드나?

 

 

다음은 fat_open()으로 가보자.

void
fat_open (void) {
	fat_fs->fat = calloc (fat_fs->fat_length, sizeof (cluster_t));
	if (fat_fs->fat == NULL)
		PANIC ("FAT load failed");

	// Load FAT directly from the disk
	uint8_t *buffer = (uint8_t *) fat_fs->fat;
	off_t bytes_read = 0;
	off_t bytes_left = sizeof (fat_fs->fat);
	const off_t fat_size_in_bytes = fat_fs->fat_length * sizeof (cluster_t);
	for (unsigned i = 0; i < fat_fs->bs.fat_sectors; i++) {
		bytes_left = fat_size_in_bytes - bytes_read;
		if (bytes_left >= DISK_SECTOR_SIZE) {
			disk_read (filesys_disk, fat_fs->bs.fat_start + i,
			           buffer + bytes_read);
			bytes_read += DISK_SECTOR_SIZE;
		} else {
			uint8_t *bounce = malloc (DISK_SECTOR_SIZE);
			if (bounce == NULL)
				PANIC ("FAT load failed");
			disk_read (filesys_disk, fat_fs->bs.fat_start + i, bounce);
			memcpy (buffer + bytes_read, bounce, bytes_left);
			bytes_read += bytes_left;
			free (bounce);
		}
	}
}

 fat_fs->fat에 fat table 만큼의 크기를 할당한다. fat table은 항상 메모리에 있는 것이 아닌가?

일단 buffer와 fat을 일치시킨다. 주석에 disk로부터 FAT을 불러온다고 되어있다. 

그리고 byte_read와 byte_left 를 설정한다. fat을 읽어오려는 거 같다.

 

 fat_size_in_bytes로 clst 갯수 * clst 한개 사이즈를 넣어준다. 즉, 말 그대로 fat의 크기이다.

for문을 0부터 fat_sectors 만큼 도는데, fat_sectors는 아직 의문인 상태이다. 

 

 이제 for문을 하나씩 돌아보자.

 

 bytes_left 가 sector_size (512bytes) 보다 크다면, buffer+bytes_read에 내용을 쓴다. 

작다면, bounce라는 이름으로 512byte만큼 메모리를 받아서 거기에 disk_read를 하고, 다시 buffer에 옮긴다.

 

 왜 sector_size보다 작다면, bounce를 거쳐서 옮기는 것일까? 아마 disk_read는 512bytes 통째로 읽어오기때문에, 남는 공간만큼 메모리의 낭비가 생기니깐 그런것 같다.

 

 그렇다면 FAT은 디스크에 저장되어있는 친구고, fat_sectors는 FAT이 디스크에 몇 sector 만큼 차지하고 있는지 나타내는 것 아닐까? 그래서 fat_read()는 disk에 있는 FAT을 읽어오는 것이고.

 

 

 이제 fat_close()로 가보자.

void
fat_close (void) {
	// Write FAT boot sector
	uint8_t *bounce = calloc (1, DISK_SECTOR_SIZE);
	if (bounce == NULL)
		PANIC ("FAT close failed");
	memcpy (bounce, &fat_fs->bs, sizeof (fat_fs->bs));
	disk_write (filesys_disk, FAT_BOOT_SECTOR, bounce);
	free (bounce);

	// Write FAT directly to the disk
	uint8_t *buffer = (uint8_t *) fat_fs->fat;
	off_t bytes_wrote = 0;
	off_t bytes_left = sizeof (fat_fs->fat);
	const off_t fat_size_in_bytes = fat_fs->fat_length * sizeof (cluster_t);
	for (unsigned i = 0; i < fat_fs->bs.fat_sectors; i++) {
		bytes_left = fat_size_in_bytes - bytes_wrote;
		if (bytes_left >= DISK_SECTOR_SIZE) {
			disk_write (filesys_disk, fat_fs->bs.fat_start + i,
			            buffer + bytes_wrote);
			bytes_wrote += DISK_SECTOR_SIZE;
		} else {
			bounce = calloc (1, DISK_SECTOR_SIZE);
			if (bounce == NULL)
				PANIC ("FAT close failed");
			memcpy (bounce, buffer + bytes_wrote, bytes_left);
			disk_write (filesys_disk, fat_fs->bs.fat_start + i, bounce);
			bytes_wrote += bytes_left;
			free (bounce);
		}
	}
}

 우선, sector_size 한개만큼 calloc받아 bounce라고 명한다. 그리고, memcpy()로 bs를 bounce로 옮긴다.

그리고 나서 bounce를 FAT_BOOT_SECTOR에 옮겨쓴다. 이러면 FAT_BOOT_SECTOR는 bs와 동일한 것이라는 것을 알 수 있다.

 

 그리고 이제 FAT를 옮긴다. 이는 open의 반대 동작이므로 생략하겠다.

 

 

그러면 이제 어떤 함수를 만들어야할까?

구현해야할 함수는 아래 포스팅 참조.

[PintOS, Project 4] Indexed and Extensible Files (tistory.com)

cluster_t fat_fs_init 

FAT field를 초기화한다. FAT field는 다음과 같다.

 

/* FAT FS */
struct fat_fs {
	struct fat_boot bs;
	unsigned int *fat;
	unsigned int fat_length;
	disk_sector_t data_start;
	cluster_t last_clst;
	struct lock write_lock;
};

여기서 bs는 따로 초기화하고 (fat_boot_create())

fat_length 는 fat_boot_create()로 만든 struct fat_boot bs 안에 들어있다.

data_start 또한 마찬가지이다. 

last_clst가 아직 감이 안오는데, 아마 비어있는 clst중 첫번째 친구가 아닌가 싶다.

그리고 write_lock을 여기서 만들어주면 될 것 같다. 

 

 

 

cluster_t fat_create_chain (cluster_t clst);

 인자clst에 새로운 clst를 추가하는 것이다. 

그렇다면 FAT[clst] 에 last_clst를 넣고, FAT[last_clst]에는 EOC를 넣으면 되지 않을까?

그리고 last_clst를 반환하면 될 것 같다.

 

그리고 clst가 0인 경우에는 새로운 chain을 만들라고 되어있다.

FAT[0] 은 fat_boot을 나타내므로 거기에는 뭘 할당하면 안된다. 그래서 예외처리 + 플래그 느낌으로 0이면 새로운 chain을 시작하라는 것 같다.

 

그래서 0일 때는 그냥 last_clst에 EOC를 넣고 last_clst를 반환하면 될 것 같다. 

 

 

 

void fat_remove_chain (cluster_t clst, cluster_t pclst);

  clst부터 마지막 clst까지 chain에서 빼버리는 것이다. 그리고 pclst에 EOF를 넣어준다. 

plcst는 clst 직전 클러스터가 된다. 즉, FAT[plcst]는 clst이다. 아마, 이중 연결 리스트가 아니라 단방향 연결 리스트라 clst의 직전 clst를 구할 방법이 없어서 인자로 넣어주는 것 같다.

 

 

void fat_put (cluster_t clst, cluster_t val);

 FAT[clst] = val.

참 쉽죠?

 

 

cluster_t fat_get (cluster_t clst);

return FAT[clst].

참 쉽죠?

 

 

disk_sector_t cluster_to_sector (cluster_t clst);

clst를 대응하는 sector number로 바꿔서 반환하라는데, 현재는 sector랑 cluster가 1:1 대응이기 때문에 그대로 반환해도 되지 않을까 싶다.

 

 

 

프로젝트가 끝나면 다시 정리해서 포스팅하겠다. 

 

댓글