Modify the file system to keep a cache of file blocks. When a request is made to read or write a block, check to see if it is in the cache, and if so, use the cached data without going to disk. Otherwise, fetch the block from disk into the cache, evicting an older entry if necessary. You are limited to a cache no greater than 64 sectors in size.
파일 블록의 캐시를 사용하도록 파일 시스템을 수정한다. 블록을 읽거나 쓰는 요청을 받았을 때, 캐시에 저장되어 있는지 확인하고, 캐시에 존재하면 디스크에서 로드하지 않고 캐싱된 데이터를 사용한다. 캐시에 존재하지 않다면 디스크로부터 블록을 캐시로 가져오고, 필요한 경우 캐시 엔트리를 교체한다. 버퍼 캐시는 최대 64블록의 크기를 가진다.
The provided inode code uses a "bounce buffer" allocated with malloc() to translate the disk's sector-by-sector interface into the system call interface's byte-by-byte interface. You should get rid of these bounce buffers. Instead, copy data into and out of sectors in the buffer cache directly.
inode 코드는 디스크의 섹터 별 인터페이스를 시스템 콜의 바이트 별 인터페이스로 변환하기 위해 malloc()을 통해 할당된 바운스 버퍼를 사용한다. 바운스 버퍼를 제거하고 데이터를 버퍼 캐시 안에 존재하는 섹터로 직접 복사한다.
현재 PintOS는 버퍼 캐시가 존재하지 않으므로, 파일 입출력 시 바로 디스크 입출력 동작을 수행한다.
파일 시스템은 블록 디바이스를 일정한 크기의 블록(sector)들로 관리한다.
파일 시스템은 블록의 사용 여부를 비트맵을 통해 관리한다.
PintOS의 8MB 파일 시스템은 총 16,384개(8MB/512 Byte)의 블록으로 구성되어 있다.
0번 블록은 bitmap on-disk inode, 1번 블록은 root dir on-disk inode, 2~5번 블록은 bitmap data, 6번 블록은 root dir entries, 나머지 블록들은 data blocks이다.
inode란 Unix/Linux 파일 시스템에서 각 파일에 대한 정보를 포함하는 구조체로서, 각각의 파일을 관리하기 위해 사용되는 자료구조이다.
파일이 생성되면 하나 이상의 inode 번호가 할당되고 inode 블록이 생성된다. inode 블록을 기반으로 파일의 처리가 이루어진다.
파일명이 다르지만 inode 번호가 같은 경우 두 파일은 동일한 파일이다.
sector: inode가 저장된 블록의 번호
data: disk_inode 데이터
removed: 파일의 삭제 여부
struct inode 구조체는 in-memory inode의 정보를 관리하는 자료구조로 sector, data, removed 필드를 가진다.
start: 파일 데이터의 디스크 블록 시작 주소
length: 파일의 크기
unused[125]: 사용되지 않는 영역
struct inode-disk 구조체는 on-disk inode의 정보를 관리하는 자료구조로 start, length, unused[125] 필드를 가진다.
inode: 디렉터리의 in-memory inode를 포인팅
pos: 디렉터리의 엔트리 오프셋
inode_sector: inode의 디스크 블록번호를 저장
in_use: dir_entry의 사용 여부
free_map_file: 비트맵을 디스크에 파일 형태로 저장
bit_cnt: 파일 시스템 전체의 디스크 블록 개수
inode: 파일의 in-memory inode 포인터
pos: 파일 오프셋
deny_write: write 명령 가능 여부
file 자료구조를 통해 메인 메모리의 inode에 접근한다.
inode를 통해 디스크에 접근한다.
Writes SIZE bytes from BUFFER into FILE, starting at the file's current position. Returns the number of bytes actually written, which may be less than SIZE if end of file is reached.
Writes SIZE bytes from BUFFER into FILE, starting at offset FILE_OFS in the file. Returns the number of bytes actually written, which may be less than SIZE if end of file is reached.
file_write()와 file_write_at()은 쓰기를 시작하는 파일의 위치가 다르다.
byte_to_sector(): 데이터를 기록할 디스크 블록 번호를 얻는다.
sector_ofs: 데이터를 기록할 디스크 블록 내부의 오프셋
memcpy()를 통해 bounce buffer에 partial write를 수행한다.
block_write(): bounce buffer의 데이터를 디스크에 기록한다.
byte_to_sector(): 파일의 시작 블록으로부터 오프셋 값을 더하여 디스크 블록 번호를 반환한다.
block_read(): 디스크 블록 단위로 loop을 돌며 디스크에서 데이터를 읽는다.
byte_to_sector(): 데이터를 읽을 디스크 블록 번호를 얻는다.
sector_ofs: 데이터를 읽을 디스크 블록 내의 오프셋
버퍼 캐시를 관리하기 위한 인터페이스를 구현한다.
최대 64개(64 * 512 Byte = 32KB)의 블록으로 구성한다.
cache_search, patch, select_victim, flush 기능을 구현한다.
LRU, Clock 알고리즘 기반 교체 알고리즘을 구현한다.
write_behind(): 파일 시스템 종료 시, 모든 dirty 블록을 디스크로 flush하고, victim_entry 선정 시, dirty일 경우 블록을 디스크로 flush한다.
해당 엔트리의 dirty 여부를 나타내는 flag
해당 엔트리의 사용 여부를 나타내는 flag
해당 엔트리의 disk_sector 주소
clock 알고리즘을 위한 clock bit
lock 변수 (struct lock)
버퍼 캐시 엔트리를 가리키기 위한 데이터 포인터
buffer_head를 검색하고 엔트리가 존재하는지를 확인한다.
엔트리가 존재하는 경우 버퍼 캐시의 데이터를 버퍼로 읽고 buffer_head를 업데이트 한다.
엔트리가 존재하지 않는 경우 캐시가 비었는지를 확인하여 victim entry를 선정한다.
buffer_head에서 empty entry를 선정하여 디스크에서 버퍼 캐시로 데이터를 block_read() 한다.
선정된 victim entry의 dirty 여부를 확인하여 디스크로 flush 한다.
버퍼 캐시에서 데이터를 읽어 유저 버퍼에 저장하고 읽을 데이터가 버퍼 캐시에 없으면 디스크에서 읽어 버퍼 캐시에 캐싱한다.
memcpy()를 이용해 버퍼 캐시 데이터를 유저 버퍼에 복사하고 buffer_head()를 갱신한다.
파일 read 시, 버퍼 캐시에서 데이터를 읽어오도록 inode_read_at() 함수를 수정한다.
buffer_head를 검색하여 엔트리가 존재하는지 확인한다.
엔트리가 존재하는 경우 버퍼의 데이터를 버퍼 캐시로 기록하고 buffer_head를 업데이트 한다.
엔트리가 존재하지 않는 경우 캐시가 비었는지 확인하고 victim entry를 선정한다.
victim entry의 dirty 여부를 확인하여 victim entry를 디스크로 flush하고 victim entry를 buffer_head에서 해지한다.
버퍼의 데이터를 버퍼 캐시에 기록한다.
버퍼 캐시에 빈 엔트리가 없으면 victim entry를 선정하여 디스크에 기록한다.
memcpy()를 이용해 버퍼의 데이터를 버퍼 캐시에 복사하고 buffer_head를 갱신한다.
파일 write 시, 디스크가 아닌 버퍼 캐시에 데이터를 쓰도록 inode_write_at() 함수를 수정한다.
버퍼 캐시 영역 할당 및 buffer_head 자료구조를 초기화한다.
p_buffer_cache로 버퍼 캐시 영역을 포인팅한다.
buffer_head 자료구조를 초기화한다.
버퍼 캐시에 캐싱 된 데이터를 디스크 블록으로 flush 한다.
버퍼 캐시 영역을 메모리에서 할당 해지한다.
clock 알고리즘을 통해 버퍼 캐시에서 victim entry를 선택한다.
victim entry가 dirty일 경우 데이터를 디스크로 flush 한다.
buffer_head를 순회하며 디스크 블록의 캐싱 여부를 검사한다.
디스크 블록이 캐싱되어 있다면, 버퍼 캐시 엔트리를 반환하고 그렇지 않으면 NULL 값을 반환한다.
인자 sector는 검색할 디스크 블록의 번호를 나타낸다.
block_write() 함수를 통해 버퍼 캐시 데이터를 디스크로 flush 한다.
buffer_head를 순회하며 dirty인 엔트리의 데이터를 디스크로 flush 한다.