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

[PintOS, Project 4] fat.c 구현

by 불냥이_ 2021. 3. 4.

※ 이 글은 Project 4를 구현중인 불확실과 추론의 영역입니다.

    특히 이부분은 다른 곳을 구현하면서 수정할 여지가 높은 곳입니다.

    착한 어린이들은 따라하지 마세요. 

 

※ fat.c 공부때랑 좀 달라진 곳이 많다. 혹시나 참고하셨던분들은 죄송합니다. ㅎㅎ;;

 

void
fat_fs_init (void) {
	/* TODO: Your code goes here. */
	/* ---------------------------- >> Project.4 FAT >> ---------------------------- */
    fat_fs->fat = NULL;
    fat_fs->fat_length = fat_fs->bs.fat_sectors;
    fat_fs->data_start = fat_fs->bs.fat_start+fat_fs->bs.fat_sectors;
    fat_fs->last_clst = fat_fs->bs.total_sectors-1;
    lock_init(&fat_fs->write_lock);
	/* ---------------------------- << Project.4 FAT << ---------------------------- */
}

 fat_fs를 초기화하는 곳이다. 

fat은 fat_create에서 따로 받을 것이기 때문에 NULL을 지정하겠다.

fat_length는 filesystem disk안에 들어가는 disk sector들의 총 갯수이다. 이를 fs_boot에서 받아오자. 

data_start는 실제 데이터가 들어가는 곳인데, 여기에 FAT을 포함시켜야되는지 고민이 많았다.

현재 내가 생각하고 있는 sector배치는

0 : fs_boot 구조체

1 : root_directory

2 ~ x : FAT

x+1 ~ : 실제 data 

로 생각하고 있기 때문에, 2에 fat_stasrt, x+1에 data_start가 들어갈 예정이다. 다행히, FAT의 (섹터 단위) 크기는 fa_sectors에 저장되어있기 때문에 fat_start + fat_sectors로 하면 실제 data의 시작 sector index가 들어갈 것 같다.

 

 

last_clst 는 아직 미정인데, 현재는 disk가 보유하고 있는 sector 중 마지막 sector를 가리키는 걸로 할까 싶다. 나중에 바뀔수도 있겠지만, 일단은 이렇게 하자.  (-1을 한 이유는 array의 인덱스를 가리키고 있기 때문이다.)

 

lock_init 으로 wrtie_lock을 설정하자.

 

그리고 나는 빈 섹터들을 하나의 linked list로 묶을 생각이다. 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);

	/* ---------------------------- >> Project.4 FAT >> ---------------------------- */
	uint32_t i = 0;
	for (i ; i < fat_fs->last_clst ; i++)
	{
		fat_fs->fat[i] = i+1;
	}
	fat_put(0, EOChain);
	fat_put(1, EOChain);
	fat_put(fat_fs->bs.fat_start+fat_fs->bs.fat_sectors-1, EOChain);
	fat_put(fat_fs->last_clst, EOChain);
	/* ---------------------------- << Project.4 FAT << ---------------------------- */
}

 이렇게 하면, data_start부터 끝까지 하나의 linked list가 구현되고, 마지막은 EOC이 새겨진다. 

일단, 처음부터 끝까지 하나의 연결리스트를 구현하고, EOC를 박는걸로 chain을 끊어주자.

이러면, fat은 하나의 chain이 되고, 빈 블록들도 하나의 chain이 된다. 

 

 

 

생각해보니 free_blocks들의 갯수로 저장해놔야할 것 같다. 이도 구현놓자.

/* 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;
	/* ---------------------------- >> Project.4 FAT >> ---------------------------- */
	unsigned int init_free_blocks;
	/* ---------------------------- << Project.4 FAT << ---------------------------- */
};
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 = ROOT_DIR_CLUSTER + 1,
	    .fat_sectors = fat_sectors,
	    .root_dir_cluster = ROOT_DIR_CLUSTER,
		/* ---------------------------- >> Project.4 FAT >> ---------------------------- */
		// free_blocks = total_sectors - fat_sectors - boot_sector - root_dir - head and taile
		.init_free_blocks = disk_size (filesys_disk) - fat_sectors - 4
		/* ---------------------------- << Project.4 FAT << ---------------------------- */
	};
}

 fat_boot에 init_free_blocks를 추가해놓고 초기 값을 넣어준다. 

이는 전체 섹터 - FAT의 섹터 - ROOT_DIR 섹터 - BOOT 정보 섹터 - free_list의 HEAD 블록 (data_start) - TAIL 블록 (last_clst) 로 계산했다. 

 

 

 

 그러면 fat_fs_init()도 수정해야한다. 

/* FAT FS */
struct fat_fs {
	struct fat_boot bs;
	cluster_t *fat;
	unsigned int fat_length;
	disk_sector_t data_start;
	cluster_t last_clst;
	struct lock write_lock;
	/* ---------------------------- >> Project.4 FAT >> ---------------------------- */
	unsigned int free_blocks;
	/* ---------------------------- << Project.4 FAT << ---------------------------- */
};
void
fat_fs_init (void) {
	/* TODO: Your code goes here. */
	/* ---------------------------- >> Project.4 FAT >> ---------------------------- */
	fat_fs->fat = NULL;
	fat_fs->fat_length = fat_fs->bs.total_sectors;
	fat_fs->data_start = fat_fs->bs.fat_start+fat_fs->bs.fat_sectors;
	fat_fs->last_clst = fat_fs->bs.total_sectors-1;
	lock_init(&fat_fs->write_lock);
	fat_fs->free_blocks = fat_fs->bs.init_free_blocks;
	/* ---------------------------- << Project.4 FAT << ---------------------------- */
}

 fat_fs_init()에서 초기값을 넣어준다. 

 

 

 

 

그러고보니 빈 블록 리스트에서 빈 블록을 하나 가져오는 함수를 만들어야할 것 같다. 

함수를 만들어보자. 

/* ---------------------------- >> Project.4 FAT >> ---------------------------- */
cluster_t
get_free_cluster(void)
{
	cluster_t start = fat_fs->data_start;
	cluster_t free_clst = fat_get(start);
	if (free_clst == fat_fs->last_clst)
	{
		return -1;
	}
	cluster_t start_next = fat_get(free_clst);
	fat_put(start, start_next);
	fat_put(free_clst, EOChain);
	fat_fs->free_blocks -= 1
	return free_clst;
}
/* ---------------------------- << Project.4 FAT << ---------------------------- */

 data_start는 항상 고정이다. 그리고 이 다음 블록부터 할당하도록 할 것이다. 그리고 할당한 뒤에는 끊어버린다. 

만약 free_clst에 last_clst가 들어왔다면, 더 이상 할당할 수 없다는 말이니 -1을 반환하자.

 

 만약 이 상위 함수에서 free_clst가 필요없어지면 다시 free_block_list에 붙이는 것을 잊지 말자. 그리고 새로운 free_block을 넣을 때는 data_start 바로 뒤에 붙이자. (last_clst가 항상 마지막에 있어야하기 때문에)

 

 

 

 

 

/* Add a cluster to the chain.
 * If CLST is 0, start a new chain.
 * Returns 0 if fails to allocate a new cluster. */
cluster_t
fat_create_chain (cluster_t clst) {
	/* TODO: Your code goes here. */
	/* ---------------------------- >> Project.4 FAT >> ---------------------------- */
	cluster_t new = get_free_cluster();
	if (new == -1)
	{
		return 0;
	}

	if(clst != 0){
		fat_put(clst, new);
	}

	return new;
	/* ---------------------------- << Project.4 FAT << ---------------------------- */
}

 우선 chain을 새로 만들거나 늘리는 과정이다. clst에 어떤 clst가 들어오는지 헷갈리는데, file의 시작 clst가 들어오는지, 마지막 clst가 들어오는지 아니면 임의의 clst가 들어오는 지 애매하다. 

 

 범용성을 위해서 임의의 clst가 들어오고, 만약 이 clst가 chain의 중간 clst라 하더라도, 상위함수에서 이미 chain을 끊어놓은 상태라고 가정한다.  즉, clst는 '현재' chain의 마지막 clst라고 가정한다. 

 

 만약 free_clst를 할당하는데 실패한다면 0을 반환한다. 

할당에 성공하고, clst가 0이라면 새로운 chain을 만들고, 아니라면 clst에 free_clst를 이어준다. 

 

 

 

/* Remove the chain of clusters starting from CLST.
 * If PCLST is 0, assume CLST as the start of the chain. */
void
fat_remove_chain (cluster_t clst, cluster_t pclst) {
	/* TODO: Your code goes here. */
	/* ---------------------------- >> Project.4 FAT >> ---------------------------- */
	if (pclst != 0)
	{
		fat_put(pclst, EOChain);
	}

	cluster_t start = fat_fs->data_start;
	cluster_t start_next = fat_get(start);

	cluster_t cur = clst;
	cluster_t next = fat_get(cur);

	fat_put(cur,start_next);
	fat_put(start, cur);
	fat_fs->free_blocks += 1;

	while (next != EOChain)
	{
		cur = fat_get(next);
		start_next = fat_get(start);
		fat_put(next, start_next);
		fat_put(start, next);
		fat_fs->free_blocks += 1;
	}
	/* ---------------------------- << Project.4 FAT << ---------------------------- */
}

 fat_remove_chain은 이렇게 이해했다. 

plcst와 clst를 끊어서, plcst로 끝나는 한 체인과, clst로부터 시작하는 한 체인을 만드는 것이라고 생각했다. 

 

생각해보니 chain을 끊어서 두개로 만드는 것이면, 그냥 pclst에 EOC를 넣으면 끝이다.

아마 file삭제나 수정을 위해서 clst부터 cluster를 삭제하는 함수인 것 같다. 

 

그리고 삭제한 cluster는 free_block_list에 넣는다. 

 

 

 

/* Update a value in the FAT table. */
void
fat_put (cluster_t clst, cluster_t val) {
	/* TODO: Your code goes here. */
	/* ---------------------------- >> Project.4 FAT >> ---------------------------- */
	fat_fs[clst] = val;
	/* ---------------------------- << Project.4 FAT << ---------------------------- */
}

/* Fetch a value in the FAT table. */
cluster_t
fat_get (cluster_t clst) {
	/* TODO: Your code goes here. */
	/* ---------------------------- >> Project.4 FAT >> ---------------------------- */
	return fat_fs[clst];
	/* ---------------------------- << Project.4 FAT << ---------------------------- */
}

/* Covert a cluster # to a sector number. */
disk_sector_t
cluster_to_sector (cluster_t clst) {
	/* TODO: Your code goes here. */
	/* ---------------------------- >> Project.4 FAT >> ---------------------------- */
	return clst;
	/* ---------------------------- << Project.4 FAT << ---------------------------- */
}

 나머지는 간단하니 함수로 표현하겠다. 나중에 생각나면 예외처리도 덧붙여야겠다. 

 

 

+추가사항+

fat_fs는 static이라 다른 파일에서 호출할 수 없다. 

free_blocks를 관리하기 위해 함수를 하나 만든다. 

 

/* ---------------------------- >> Project.4 FAT >> ---------------------------- */
size_t
ctrl_free_blocks_EA(uint32_t a)
{
	fat_fs->free_blocks += a;
	return fat_fs->free_blocks;
}
/* ---------------------------- << Project.4 FAT << ---------------------------- */

a를 넣으면, free_blocks에 a를 더하고 free_blocks를 반환한다.

만약 a에 0을 넣으면 free_blocks를 그대로 반환할 것이다. 

 

 

 

댓글