일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- 셋업
- 파란 장미
- 가테
- 글루민
- multi-oom
- 핀토스 프로젝트
- Project 1
- 끝
- PINTOS
- 일지 시작한 지 얼마나 됐다고
- 마일섬
- 핀토스 프로젝트 3
- 핀토스 프로젝트 2
- alarm clock
- 제발흰박
- botw
- 내일부터
- 아직도 실험 중
- 자 이제 시작이야
- 바빠지나?
- 글리치
- 황금 장미
- 노가다
- 핀토스 프로젝트 4
- 핀토스 프로젝트 1
- Today
- Total
거북이의 쉼터
(2022.03.27) Indexed & Extensible 가이드라인 (2) 본문
이번에는 File Growth를 살펴볼 것이다. 분량은 짧긴 한데 지난 포스팅에 이어서 계속 작성하기에는 FAT에서 급작스럽게 파일 extension으로 넘어가는 것이라 흐름이 애매했다.
지금까지 핀토스에서 사용된 코드를 보면 파일을 생성할 때부터 어느 정도를 사용할 지 미리 지정해서 생성한 뒤, 할당된 부분만을 사용하였다. 현실에서는 전혀 그렇지 않다. 우리가 메모장을 열 때 얼마를 사용할지 지정하고 쓰지 않듯이, 핀토스에서도 파일을 생성할 때는 크기 0으로 생성한 뒤, 필요한 만큼 크기를 키우는 과정을 구현해야 한다.
파일이 자랄 수 있는데에는 크기 한계가 마땅히 없다. 메타 데이터를 제외하고 파일 시스템이 허용하는 만큼 커질 수 있으며, 루트 디렉토리에도 같은 논리에 의해 16개보다 많은 파일이 저장될 수 있어야 한다.
파일이 자라는 정확한 시점은 언제인가를 매뉴얼이 설명한다. "파일의 EOF를 넘어서 쓰는 시점"에 파일이 자라면 된다. 이 말인즉, 파일의 EOF를 넘어 seek하는 행동, EOF 이후의 파일을 읽는 행동은 파일 성장에 아무런 영향을 주지 않으며, 쓰기 시작한 시점에 새롭게 섹터를 할당해서 파일의 내용을 작성하도록 하면 된다는 것이다. 이 때, 기존 EOF와 쓰기 시작한 지점 사이의 공간은 모두 0으로 채워 넣어야 한다고 설명한다. 이 과정에서 아무런 내용은 없으나, 0으로 채워져야 하는 블록이 여럿 생성될 수 있다. 이를 실제로 할당을 할지, 아니면 필요할 때까지 기다렸다가 할당할 지는 구현상의 자유라고 한다. 필요할 때까지 기다렸다가 할당하는 방식을 sparse file을 지원하는 파일 시스템이라고 하는데, 이는 lazy loading과 그 궤를 같이 하는 것 같다. 여기까지 해당 방식을 적용했다간 FAT를 구현함에 있어 내 머리가 조져질 것 같아서 간단한 방식인 바로 할당을 채택하겠다.
파일 성장을 지원해야 할 부분은 크게 두 곳이 있다. 하나는 파일을 생성하는 부분이다. 새롭게 파일이 생성될 때 파일의 내용이 여러 섹터에 걸쳐있어야 할 정도로 긴 경우 필요한 만큼의 섹터를 할당하는 것이 필요하다. 다른 하나는 매뉴얼에 언급된 대로 파일에 쓰는 부분이다. 근본적으로 파일의 내용을 다루게 되는 곳은 inode이며, 이를 관리하는 함수들은 모두 inode.c에 집결해 있기에 해당 코드를 살펴보도록 한다.
파일을 생성할 때는 inode_create가 내부적으로 호출된다. 해당 코드는 아래와 같다.
bool
inode_create (disk_sector_t sector, off_t length) {
struct inode_disk *disk_inode = NULL;
bool success = false;
ASSERT (length >= 0);
/* If this assertion fails, the inode structure is not exactly
* one sector in size, and you should fix that. */
ASSERT (sizeof *disk_inode == DISK_SECTOR_SIZE);
disk_inode = calloc (1, sizeof *disk_inode);
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);
if (sectors > 0) {
static char zeros[DISK_SECTOR_SIZE];
size_t i;
for (i = 0; i < sectors; i++)
disk_write (filesys_disk, disk_inode->start + i, zeros);
}
success = true;
}
free (disk_inode);
}
return success;
}
파일의 메타 정보에 해당하는 내용을 담을 sector를 해당 함수가 호출되기 전에 free_map_allocate를 통해 할당한다. 해당 함수 내에서는 메타 정보를 할당된 sector에 담고, 필요한 파일 길이만큼의 disk 공간을 할당해 0으로 초기화한 뒤, 파일 내용이 담겨있는 시작 섹터의 index를 저장해놓는다. free_map_allocate는 연이은 빈 섹터를 할당하는 방식이므로, 이를 FAT 방식으로 바꾸어주는 과정이 필요하다. 주어진 길이만큼의 빈 섹터들을 할당받아 이전에 구현해 놓은 FAT 기능성 함수들로 연결고리를 만들고, disk_inode->start에 시작 섹터 index를 저장하도록 변경하면 될 것이다.
다음으로는 file_write_at에서 호출되는 inode_write_at이다.
off_t
inode_write_at (struct inode *inode, const void *buffer_, off_t size,
off_t offset) {
const uint8_t *buffer = buffer_;
off_t bytes_written = 0;
uint8_t *bounce = NULL;
if (inode->deny_write_cnt)
return 0;
while (size > 0) {
/* Sector to write, starting byte offset within sector. */
disk_sector_t sector_idx = byte_to_sector (inode, offset);
int sector_ofs = offset % DISK_SECTOR_SIZE;
/* Bytes left in inode, bytes left in sector, lesser of the two. */
off_t inode_left = inode_length (inode) - offset;
int sector_left = DISK_SECTOR_SIZE - sector_ofs;
int min_left = inode_left < sector_left ? inode_left : sector_left;
/* Number of bytes to actually write into this sector. */
int chunk_size = size < min_left ? size : min_left;
if (chunk_size <= 0)
break;
if (sector_ofs == 0 && chunk_size == DISK_SECTOR_SIZE) {
/* Write full sector directly to disk. */
disk_write (filesys_disk, sector_idx, buffer + bytes_written);
} else {
/* We need a bounce buffer. */
if (bounce == NULL) {
bounce = malloc (DISK_SECTOR_SIZE);
if (bounce == NULL)
break;
}
/* If the sector contains data before or after the chunk
we're writing, then we need to read in the sector
first. Otherwise we start with a sector of all zeros. */
if (sector_ofs > 0 || chunk_size < sector_left)
disk_read (filesys_disk, sector_idx, bounce);
else
memset (bounce, 0, DISK_SECTOR_SIZE);
memcpy (bounce + sector_ofs, buffer + bytes_written, chunk_size);
disk_write (filesys_disk, sector_idx, bounce);
}
/* Advance. */
size -= chunk_size;
offset += chunk_size;
bytes_written += chunk_size;
}
free (bounce);
return bytes_written;
}
기존 함수는 우선 free_map_allocate 방식에 맞춰져 있기 때문에 메타 정보를 들고 있는 inode에서 파일의 특정 pos에 해당하는 sector index를 얻기 위해 단순한 구조의 byte_to_sector 함수를 사용해 변환을 하고 있다. 여기서 기존 크기보다 큰 offset이 들어올 가정을 전혀 하고 있지 않기 때문에 보완이 필요하다.
일단 해당 함수들을 살펴보면서, 기존 free_map 방식에서 바꿔주어야 할 함수들이 많다는 것을 알았다. 여기에 소개하지는 않았지만, read나 파일의 내용과 관련된 함수들은 파일 시스템이 바뀜에 따라 같이 변경이 일어나야 할 것이다. 이 또한 해당 과정을 진행하면서 같이 바꿔보도록 하자. 참고할까 해서 free_map이 붙은 곳이 몇 곳이나 있나 봤다. free-map.c, free-map.h는 제외하고 남은 곳을 전부 세면 5곳이다. 이 정도면 할 만 한데? 다음 포스팅부터 해당 파트 구현을 시작하자.
(2023.01.11 수정, 구현에서 일부 발췌)
구체적인 설명은 가이드라인에서 거의 다 한 것 같으니 한 가지만 다시 짚고 넘어가도록 하자. disk에 저장되는 섹터의 구조를 살펴보면, 0번 섹터는 boot sector로 활용이 되며, 1 ~ f는 FAT를 저장하는 용도로, f + 1부터 n - 1까지의 섹터는 데이터를 저장하는 용도로 사용된다. 이제 cluster를 어떻게 할당할지를 생각해보자. f + 1번 섹터가 1번 cluster라고 한다면 n - 1번 섹터는 (n - f - 1)번 cluster가 된다. 이러한 매핑은 임의로 정한 것이지만, 가장 단순한 형태이며, 역산도 간단하기 때문에 이렇게 결정하였다. 번외로 0번 섹터는 0번 cluster에 그대로 매핑이 되지만, FAT에서는 0번 cluster를 다루지 않을 것이다. 그림으로 정리하면 다음과 같다.
'코딩 삽질 > KAIST PINTOS (CS330)' 카테고리의 다른 글
(2022.03.29) Indexed & Extensible 구현 (2) (0) | 2022.03.29 |
---|---|
(2022.03.28) Indexed & Extensible 구현 (1) (0) | 2022.03.28 |
(2022.03.27) Indexed & Extensible 가이드라인 (1) (0) | 2022.03.27 |
(2022.03.27) Synchronization 가이드라인 (1) (0) | 2022.03.27 |
(2022.03.26) 프로젝트 4 Introduction 맛보기 (0) | 2022.03.26 |