이전 포스팅에서 이어서 구현해보자.

이러한 커널 패닉이 뜨는 것까지 확인하고 포스팅을 마무리했었다.
로그에 free_map_allocate 함수가 껴있는 것을 볼 수 있을 것이다. 우리는 분명히 free_map 대신 fat를 사용해야 한다고 인지하고 있다. 그러면 free_map_allocate가 당연히 문제가 될거라고 추측할 수 있다.
bool free_map_allocate(size_t cnt, disk_sector_t *sectorp)
{
disk_sector_t sector = bitmap_scan_and_flip(free_map, 0, cnt, false);
if (sector != BITMAP_ERROR && free_map_file != NULL && !bitmap_write(free_map, free_map_file))
{
bitmap_set_multiple(free_map, sector, cnt, false);
sector = BITMAP_ERROR;
}
if (sector != BITMAP_ERROR)
*sectorp = sector;
return sector != BITMAP_ERROR;
}
이 함수를 보면 bitmap_scan_and_filp 을 사용하는 것을 볼 수 있다. 여기서 문제가 터졌다는 건데..
이전 포스팅에서 free_map_init 대신에 fat_init를 사용했었다. free_map_init는 free_map을 초기화시켜주는 함수였다.
그런데 free_map_init를 사용하지를 않으니 free_map 자체가 초기화되지가 않아 발생하는 문제였다.
따라서 fat_allocate를 만들어서 free_map_allocate를 사용하는 곳에 대신 넣어줘야 한다.
그러면 fat_allocate가 어떤 함수인지부터 알아야 한다. 함수를 분석해보면 인자로 받은 cnt만큼 비트맵에서 여유 비트를 찾아 세트해주고, 첫번째 비트를 인자로 받은 sectorp에 할당해준다. 간단히 cnt개의 섹터를 할당해서 첫번째 섹터 번호를 가져온다고 보면된다.
fat_allocate
bool fat_allocate(size_t cnt, cluster_t *clusterp)
{
if (cnt == 0)
return true;
ASSERT(cnt > 0);
cluster_t start = fat_create_chain(0); // 첫번째 클러스터
cnt--;
cluster_t prev = start;
while (prev != 0 && cnt > 0)
{
/* 클러스터 체인 만들기 */
prev = fat_create_chain(prev);
cnt--;
}
/* 섹터에 충분한 공간이 남지 않았으면 */
if (cnt != 0)
{
/* 만들던 체인 모두 삭제 */
fat_remove_chain(start, 0);
}
else
/* 클러스터 체인 시작 번호 대입 */
*clusterp = start;
return start != 0;
}
그러면 fat 방식으로 allocate를 구현해보자. 우선 fat_create_chain(0)으로 새 체인을 하나 만들고, cnt를 하나 감소시켜주자. 만약 섹터 하나만 할당하는 의도었으면 여기서 바로 종료될 것이다.
새 체인은 start 변수에 저장해두는데, 이건 인자로 받은 clusterp에 저장해둘 인자이니, 이 start 클러스터 번호에 이어 클러스터를 이어줄 prev 변수를 하나 만들고, while 루프를 통해 cnt가 다 감소할때까지 클러스터 체인을 확장한다.
만약 남은 공간을 모두 사용해서 클러스터 체인을 확장했는데, cnt가 남아있다면 클러스터 체인을 모두 확장하지 못했다는 뜻이므로 실패로 간주하고 만들어둔 클러스터 체인을 fat_remove_chain으로 모두 해제해야 한다. 모두 해제하면 start에는 0이 들어가고 이는 false의 반환으로 이어진다.
cnt가 0이라면 클러스터 체인의 확장이 완료된 것이므로 start 번호를 인자로 받은 clusterp에 저장해주고, true를 반환한다.
free_map을 fat로 교체하기
그럼 이렇게 만들어두긴 했는데... 어디다가 넣어줘야 할까??
간단하다. free_map_allocate를 사용하는 모든 곳에다가 대신 넣어주자.
먼저 filesys.c의 filesys_create 함수를 보자
bool
filesys_create (const char *name, off_t initial_size) {
disk_sector_t inode_sector = 0;
struct dir *dir = dir_open_root ();
bool success = (dir != NULL
&& name !=NULL
&& strlen(name)<=14
&& free_map_allocate (1, &inode_sector)
&& inode_create (inode_sector, initial_size)
&& dir_add (dir, name, inode_sector));
if (!success && inode_sector != 0)
free_map_release (inode_sector, 1);
dir_close (dir);
return success;
}
이 함수는 파일을 만들 때 해당 파일의 inode_disk를 만들어 디스크에 저장한 다음, initial_size만큼의 파일 크기를 만들기 위해 클러스터 체인을 확장하는 함수이다.
고쳐야 할 점을 찬찬히 뜯어보자. 기존의 파일 시스템은 클러스터를 free_map_allcate에서 섹터 번호를 바로 할당받았었다. 우리는 섹터와 일대일 매핑되는 클러스터를 할당받아야 하니까 섹터를 사용하는 부분도 모두 클러스터로 바꾸어야 한다.
bool filesys_create(const char *name, off_t initial_size)
{
cluster_t inode_clst = 0;
bool success = (cur_dir != NULL &&
name != NULL && strlen(name) <= 14 &&
fat_allocate(1, &inode_clst) &&
inode_create(cluster_to_sector(inode_clst), initial_size, false) &&
dir_add(cur_dir, name, cluster_to_sector(inode_clst)));
if (!success && inode_sector != 0)
dprintf("[%s] fail to filesys_create !!\n", name);
return success;
}
이렇게 바꿔주면 된다 !!
실패했을 경우에는 fat_release를 만들어서 달아줘도 되고, 애초에 fat_allocate에서 할당을 해제해주니까 별다른 작업 없이 디버깅 코드만 넣어도 된다.
그러면 또 어디를 바꿔주냐? filesys_create에서 사용했던 inode_create에 들어가보자.
bool inode_create(disk_sector_t sector, off_t length)
{
...
if (disk_inode != NULL)
{
size_t sectors = bytes_to_sectors(length);
disk_inode->length = length;
disk_inode->magic = INODE_MAGIC;
if (free_map_allocate(sectors, &disk_inode->start))
{
disk_write(filesys_disk, sector, disk_inode);
...
}
여기서도 free_map_allocate를 사용한다!!
저 부분도
if (fat_allocate(sectors, &disk_inode->start))
요렇게 바꿔주고, 저 코드 밑에 보면
for (i = 0; i < sectors; i++)
disk_write(filesys_disk, disk_inode->start + i, zeros);
이 부분이 있을텐데, 기존의 disk_inode의 start는 시작 섹터 번호였지만, 이제는 시작 클러스터 번호니까
for (i = 0; i < sectors; i++)
disk_write(filesys_disk, cluster_to_sector(disk_inode->start) + i, zeros);
이렇게 바꿔주자.
다시 inode_write_at으로
이제 정말 끝인거 같지만, 1편에서 중요한 한가지를 놓쳤다. inode_write_at에서 파일 확장을 하며 파일의 길이를 추가해줬지만, 이 정보는 메모리에서만 살아있다. 이를 디스크에 write해주지 않으면 이 파일을 부를 때 이전 정보만 남아있을 것이다.
따라서 갱신된 inode의 정보를 디스크에 써주는 로직이 필요하다.
inode_flush 만들기
void inode_flush(struct inode *inode)
{
disk_write(filesys_disk, inode->sector, &inode->data);
}
사실 별거 없어 보이니까 disk_write를 바로 다이렉트로 꽂아버리면 되는 거 아니냐고 생각할 수도 있지만, 개인적으로 함수는 재사용성 뿐만 아니라 코드의 의도를 명확하게 해주기 위한 네이밍 용도도 있다고 생각한다.
아무튼 이 함수를 inode_write_at의 마지막에 추가해주자.
off_t inode_write_at(struct inode *inode, const void *buffer_, off_t size,
off_t offset)
{
...
free(bounce);
inode_flush(inode);
return bytes_written;
}
이러면 이제 파일 확장은 끝난다 !!
사실 1편 2편을 봐도 왜 이게 동작하는지 모르는 사람들도 많을거라고 생각한다. 뭐든지 코드의 흐름을 파악하고 코딩을 시작하자.
내가 어디를 구현해야 하는지, 어디를 어떻게 수정해야 하는지 감이 잡힐 것이다. 코드 흐름 파악이 어렵다면 gdb로 실행 과정을 따라가보는 것이 큰 도움이 될 것이다.
'크래프톤 정글' 카테고리의 다른 글
| 크래프톤 정글 수료 후기 (7) | 2025.08.13 |
|---|---|
| [나만무] 나만무 프로젝트 끝 (5) | 2025.07.31 |
| [pintOS] 파일 확장 기능 구현 - 1 (0) | 2025.06.11 |
| [pintOS] FAT 구현기 (0) | 2025.06.10 |
| [pintOS] 파일 시스템 찍먹하기 (4) | 2025.06.08 |