※ 이 글은 Project 4를 본격적으로 시작하기 전, 추론의 영역입니다.
기존시스템은 bitmap으로 디스크를 관리하고 있다.
그리고 inode.c는 bitmap 기반으로 짜여져있다.
그런데 이제 FAT를 도입했으니 bitmap을 다 지워고 FAT 기반으로 다 깔아야된다.
그 말은 filesys나 file에 관련된 것들을 전부 갈아엎어야한다는 것이다.
심지어 bitmap은 연속할당이었으나, FAT은 연속할당도 아니다.
마치 1x1 레고 블록을 드렸으니 이걸로 도시를 만들어보세요라는 느낌이다.
시발
일단 inode.c부터 공부해보자.
/* On-disk inode.
* Must be exactly DISK_SECTOR_SIZE bytes long. */
struct inode_disk
{
disk_sector_t start; /* First data sector. */
off_t length; /* File size in bytes. */
unsigned magic; /* Magic number. */
uint32_t unused[125]; /* Not used. */
};
disk에 저장되어있는 inode 정보이다. 참고로 inode들은 disk의 한 섹터안에 저장되어있다.
그리고 이게 sector size가 되어야한다는데 이 단일 inode_disk 크기가 512byte라는 것인지, 아니면 inode_disk들이 512byte가 되어야된다는 것인지.
그런데 unused가 있는데, 내 생각에는 이 배열에 이 파일이 소유하고있는 cluster 번호가 와야되는 것 같다. 그러면 대충 512byte먹지 않을까.
그러면 file을 열 때, inode는 어떻게 찾을 수 있을까. 물론, userprog에서는 file에 inode가 저장되있을 거다. 문제는 이 inode는 포인터라서 (memory 상의) inode 를 가리키고 있을 것인데, 우리가 필요한 것은 inode의 sector 번호이다. 대충 찾아보니 이는 directory.c 의 dir_entry에 나타나있다.
/* A single directory entry. */
struct dir_entry
{
disk_sector_t inode_sector; /* Sector number of header. */
char name[NAME_MAX + 1]; /* Null terminated file name. */
bool in_use; /* In use or free? */
};
이런식으로. 이런게 있다라고 생각하고 계속 inode를 가보자.
/* Returns the number of sectors to allocate for an inode SIZE
* bytes long. */
static inline size_t
bytes_to_sectors(off_t size)
{
return DIV_ROUND_UP(size, DISK_SECTOR_SIZE);
}
byte를 sector 단위로 바꿔주는 것이다. 512byte단위로 올린다고 생각하면 될 것이다.
만약 781byte는 2 sector로.
/* In-memory inode. */
struct inode
{
struct list_elem elem; /* Element in inode list. */
disk_sector_t sector; /* Sector number of disk location. */
int open_cnt; /* Number of openers. */
bool removed; /* True if deleted, false otherwise. */
int deny_write_cnt; /* 0: writes ok, >0: deny writes. */
struct inode_disk data; /* Inode content. */
};
memory 상의 inode 구조체이다. elem이 있으니 어딘가에 inode를 관리하는 list가 있을 것이다. file 구조체가 관리하고 있을 줄 알았는데 아니었다. 흠.... 어딘가에 있겠지.
sector는 inode_disk가 있는 곳을 말하는 것일거고.
open_cnt는 inode가 현재 몇번 참조되고 있는 것인지를 말하는 것 아닐까?
removed는 inode가 지워졌는지 아닐까
deny_write는 이전에 구현한 deny_write라고 생각한다.
그리고 inode_disk는 inode_disk를 disk에서 읽어와서 여기다가 올려놓는 것일 것이다.
난 천재?
구조체는 다 봤으니 함수는 간단히 어떤 역할만 하는지 보도록하자. 갈길이 멀다.
/* Returns the disk sector that contains byte offset POS within
* INODE.
* Returns -1 if INODE does not contain data for a byte at offset
* POS. */
static disk_sector_t
byte_to_sector(const struct inode *inode, off_t pos)
{
ASSERT(inode != NULL);
if (pos < inode->data.length)
return inode->data.start + pos / DISK_SECTOR_SIZE;
else
return -1;
}
byte를 pos로 받는다. 그래서 그에 해당하는 sector가 어디인지를 검색한다.
즉, 어떤 파일이 3,4,5,6 sector를 차지한다고 하자. 그리고 이 파일의 765byte는 어느 sector에 있을까?
이 file의 start는 3이고 765byte는 2번째 섹터에 있을 것이므로 3+1 = 4가 되서, 4를 반환한다.
문제는 FAT은 연속할당이 아니라서 아마 inode_disk의 배열에서 찾아야될 것 같다.
/* List of open inodes, so that opening a single inode twice
* returns the same `struct inode'. */
static struct list open_inodes;
아하 여깄었구나!
열려진 inode들을 관리하는 list이다. inode의 elem을 여기다 넣으면 될 것 같다.
/* Initializes an inode with LENGTH bytes of data and
* writes the new inode to sector SECTOR on the file system
* disk.
* Returns true if successful.
* Returns false if memory or disk allocation fails. */
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;
}
file의 길이를 받으면, 그거를 sector수로 반환하고 (bytes_to_sectors() 함수가 있다!) 비어있는 sector에 그것을 쓴다.
문제는 이 역시 연속 할당이 아니기 때문에 비어있는 sector를 가져와서 넣는 식으로 해야될 것이다.
그것을 구현할 생각하니깐 어지럽다.
/* Reads an inode from SECTOR
* and returns a `struct inode' that contains it.
* Returns a null pointer if memory allocation fails. */
struct inode *
inode_open(disk_sector_t sector)
{
struct list_elem *e;
struct inode *inode;
/* Check whether this inode is already open. */
for (e = list_begin(&open_inodes); e != list_end(&open_inodes);
e = list_next(e))
{
inode = list_entry(e, struct inode, elem);
if (inode->sector == sector)
{
inode_reopen(inode);
return inode;
}
}
/* Allocate memory. */
inode = malloc(sizeof *inode);
if (inode == NULL)
return NULL;
/* Initialize. */
list_push_front(&open_inodes, &inode->elem);
inode->sector = sector;
inode->open_cnt = 1;
inode->deny_write_cnt = 0;
inode->removed = false;
disk_read(filesys_disk, inode->sector, &inode->data);
return inode;
}
inode_disk가 있는 sector에서 데이터를 읽어와서 inode에 저장하는 함수이다.
/* Closes INODE and writes it to disk.
* If this was the last reference to INODE, frees its memory.
* If INODE was also a removed inode, frees its blocks. */
void inode_close(struct inode *inode)
{
/* Ignore null pointer. */
if (inode == NULL)
return;
/* Release resources if this was the last opener. */
if (--inode->open_cnt == 0)
{
/* Remove from inode list and release lock. */
list_remove(&inode->elem);
/* Deallocate blocks if removed. */
if (inode->removed)
{
free_map_release(inode->sector, 1);
free_map_release(inode->data.start,
bytes_to_sectors(inode->data.length));
}
free(inode);
}
}
반대로 inode를 닫고, 갱신사항을 inode_disk에 적는다.
/* Marks INODE to be deleted when it is closed by the last caller who
* has it open. */
void inode_remove(struct inode *inode)
{
ASSERT(inode != NULL);
inode->removed = true;
}
inode가 삭제된다면 이를 체크하고, 이를 이용해서 close할 때, free까지 해준다.
/* Reads SIZE bytes from INODE into BUFFER, starting at position OFFSET.
* Returns the number of bytes actually read, which may be less
* than SIZE if an error occurs or end of file is reached. */
off_t inode_read_at(struct inode *inode, void *buffer_, off_t size, off_t offset)
{
uint8_t *buffer = buffer_;
off_t bytes_read = 0;
uint8_t *bounce = NULL;
while (size > 0)
{
/* Disk sector to read, 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 copy out of 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)
{
/* Read full sector directly into caller's buffer. */
disk_read(filesys_disk, sector_idx, buffer + bytes_read);
}
else
{
/* Read sector into bounce buffer, then partially copy
* into caller's buffer. */
if (bounce == NULL)
{
bounce = malloc(DISK_SECTOR_SIZE);
if (bounce == NULL)
break;
}
disk_read(filesys_disk, sector_idx, bounce);
memcpy(buffer + bytes_read, bounce + sector_ofs, chunk_size);
}
/* Advance. */
size -= chunk_size;
offset += chunk_size;
bytes_read += chunk_size;
}
free(bounce);
return bytes_read;
}
byte_to_sector는, inode와 offset을 받았을 때, 해당 offset에 해당하는 sector를 반환한다.
그 sector에서부터 size만큼 disk에 쓴다. 역시나 문제는 연속할당이 아니라서 sector_idx를 고쳐야된다는 것이다.
inode_length()는 밑에서 설명하겠지만, inode가 가리키는 file의 총 크기를 의미한다.
요약하면 disk에 있는 file을 inode로 찾아서, 해당 offset에서 size만큼 읽어온다는 뜻이다.
/* Writes SIZE bytes from BUFFER into INODE, starting at OFFSET.
* Returns the number of bytes actually written, which may be
* less than SIZE if end of file is reached or an error occurs.
* (Normally a write at end of file would extend the inode, but
* growth is not yet implemented.) */
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;
}
inode_write_at도 read_at과 기본은 똑같은 함수일 것이다.
하지만, file 뒷부분이나 중간에 data가 새로이 들어왔으면 어떡할 것인가?
이를 구현하려니 어지럽다.
아마 while문 돌기 전에 필요한 sector의 갯수를 알고, 필요하다면 획득해야될 것 같다.
/* Disables writes to INODE.
May be called at most once per inode opener. */
void inode_deny_write(struct inode *inode)
{
inode->deny_write_cnt++;
ASSERT(inode->deny_write_cnt <= inode->open_cnt);
}
/* Re-enables writes to INODE.
* Must be called once by each inode opener who has called
* inode_deny_write() on the inode, before closing the inode. */
void inode_allow_write(struct inode *inode)
{
ASSERT(inode->deny_write_cnt > 0);
ASSERT(inode->deny_write_cnt <= inode->open_cnt);
inode->deny_write_cnt--;
}
inode에 동기화를 설정해주는 함수다. 생략하겠다.
/* Returns the length, in bytes, of INODE's data. */
off_t inode_length(const struct inode *inode)
{
return inode->data.length;
}
inode가 가리키는 file의 크기를 반환하는 함수이다.
이제 inode에 대해서 공부가 끝났으니 내일은 구현을 시작해보도록 하겠다.
'Pintos Project > Project 4' 카테고리의 다른 글
[PintOS, Project 4] Filesys.c 구현 (0) | 2021.03.04 |
---|---|
[PintOS, Project 4] inode.c 구현 (0) | 2021.03.04 |
[PintOS, Project 4] fat.c 구현 (0) | 2021.03.04 |
[PintOS, Project 4] fat.c 공부 (2) | 2021.03.03 |
[PintOS, Project 4] Indexed and Extensible Files (0) | 2021.03.02 |
댓글