# buffer cache

간결하고 이해가 쉬운 구조를 유지해서 커널 코드에 대한 진입장벽을 낮추는 것,
그래서 기본 기능으로부터 쉽고 빠르게 요구된 기능을 구현/확장할 수 있도록 하는
것이 주요 목표. 버퍼, 캐시와 같은 최적화 기술과 거듭 추상화 단계를 거친
자료구조는 코드에 대한 진입장벽을 높이기 때문에 최대한 그 수준을 낮게
유지하고자 했지만 블럭 디바이스를 다루면서 최소한의 최적화는 불가피해보인다.
그렇지 않고서는 연산 오버헤드가 클 뿐더러 코드의 일관성을 유지하기 힘들어보이기
때문이다.

블럭 디바이스의 경우 커널로부터 버퍼를 할당받아 사용.

커널에서 버퍼를 관리. 드라이버에서 버퍼를 요청하면(`request_buffer()`) 인자로
넘어온 사이즈의 버퍼를 할당해서 `buf_t` 리턴.

최근에 사용된 버퍼는 head 바로 뒤에, 가장 오래된 버퍼는 끝에 위치하게 된다.
따라서 사용자가 요청한 블럭이 이미 버퍼로 읽혀졌는지 리스트를 순회할 때, 마지막
버퍼에 이르기까지 해당 블럭이 존재하지 않는다면 마지막 버퍼에 자료를 읽어들이면
된다. 마지막 버퍼가 사용된지 가장 오래된 버퍼이기 때문이다.

`buf_t *request_buffer(nr, BLOCK_SIZE)` - nr: 요청 버퍼 갯수. 리턴값이 NULL
이면 오류. 가용공간이 부족한 경우, 요청된 버퍼 갯수보다 적은 버퍼가 할당될 수
있음.

`void release_buffer(buf_t)`

`void *getblk(unsigned int n, struct device *dev)` - 블럭 디바이스 dev의 n번째
블럭을 읽어들임. 리턴되는 버퍼캐시는 유저 스페이스로 복사된 것이 아닌 공통
버퍼. ~~고려해야할 문제가 하나 있는 데 여러 태스크가 하나의 버퍼캐시에 접근하는
경우 버퍼의 내용을 사용자 공간으로 복사하는 와중에 해당 버퍼가 lru로 채택되어
다른 값이 써질 수 있다. 파일시스템 쪽에서 락을 걸면 간단히 해결되긴 하는데
버퍼캐시 내에서 어떻게 해결할 수 없나...~~ `getblk_lock()`사용 할 것.

`void *getblk_lock(unsigned int n, struct device *dev)` - `getbuf_lru()`에서
개별 버퍼락을 걸고 해당 버퍼 락을 해제하는 `putblk_unlock()` 함수를 대칭적으로
사용해야 함.

`void putblk_unlock(unsigned int nblock, struct device *dev)` - 해당 블럭
버퍼캐시 락 해제

~~!! sleep 가능한 뮤텍스를 사용하므로 시스템 콜을 선점할 방법을 강구해야 함.
혹은 커널 스레드로 업무를 넘기던가.~~

`void updateblk(unsigned int nblock, struct device *dev)` - 버퍼를 수정한 경우
사용. 더티 플래그를 셋함.

아래는 사용자에게 보이지 않는 함수들(deprecated).

`struct buffer_cache *getbuf(block_no, buf_t)` - 버퍼 리스트에서(`buf_t`) 해당
블럭이(`block_no`) 존재하는 버퍼를 리턴. ~~리턴 받은 버퍼와 블럭번호가 일치하지
않다면, 버퍼는 사용된지 가장 오래된 버퍼임. 고로 리턴된 버퍼에 새로운 블럭을
읽어오면 됨.~~ NULL 이면 오류.

`unsigned int getbuf_lru(buf_t, struct buffer_cache *)` - 인자로 넘어간 버퍼
변수에 사용한지 가장 오래된 버퍼를 얻음. 얻어진 버퍼는 더이상 유효하지 않음.
다른 곳에서 사용되지 않는다는 것이 보장되어야 함. 리턴값은 버퍼의 블럭넘버.
리턴값이 -1(0xffffffff) 이면 오류. (자료구조를 더블리 링크드 리스트로 변경하면
검색시간을 O(1)로 대폭 줄일 수 있지만, 검색연산 대비 자료구조의 크기 증가 및
사용되는 버퍼 갯수를 고려할 때 자료구조의 경량화가 소형 시스템에 더 이득을
주리라 생각됨). write back 할 시점으로 적당함. dirty를 추가할지 무조건
라이트할지..

!! 주기적으로 sync해주지 않으면 자료유실 가능성.

`int putbuf(struct buffer_cache *buffer, block_no, buf_t)` - 버퍼에 할당된 블럭
넘버를 기입. lru 업데이트. 리턴값이 0이 아니면 오류.

`getblk()`

`putblk()`

## 블럭 디바이스 드라이버, 블럭 버퍼 사용하기

	blkbuf_t *buffer;

	if ((buffer = request_blkbuf(nr, block_size)) == NULL)
		error;

	char *data = blkbuf_get_lru(buffer);
	read(fd, data, block_size);
	blkbuf_put(buffer, data, block_no);
