Presentation is loading. Please wait.

Presentation is loading. Please wait.

리눅스 커널의 이해 중에서 14장. 디스크 캐시 남재준 네트워크 실험실.

Similar presentations


Presentation on theme: "리눅스 커널의 이해 중에서 14장. 디스크 캐시 남재준 네트워크 실험실."— Presentation transcript:

1 리눅스 커널의 이해 중에서 14장. 디스크 캐시 남재준 네트워크 실험실

2 목차 Part I 버퍼 캐시 - 버퍼 헤드 자료 구조 - getblk()함수 - 버퍼 사용 카운터 - 버퍼 할당
- 버퍼 헤드 자료 구조 - getblk()함수 - 버퍼 사용 카운터 - 버퍼 할당 - 디스크에 더티 버퍼 기록하기 Part II 페이지 캐시 - 페이지 캐시 자료 구조 - 페이지 캐시 처리함수 - 페이지 캐시 튜닝

3 Part I : 버퍼 캐시

4 디스크 캐시 Disk data ram 디스크에 저장한 일부 data를 램으로 옮겨 저장하는 소프트웨어 메커니즘
버퍼 캐시 : 버퍼를 저장 페이지 캐시 : 정규파일의 데이터를 포함한 페이지 프레임을 저장

5 디스크 캐시 입출력 연산 캐시 시스템 콜 커널 함수 버퍼캐시와 페이지 캐시 사용 블록장치 파일을 읽음 버퍼 read()
시스템 콜 커널 함수 블록장치 파일을 읽음 버퍼 read() block_read() 블록장치 파일을 씀 write() block_write() Ext2 디렉토리를 읽음 getdents() ext2_bread() Ext2 정규 파일을 읽음 페이지, 버퍼 generic_file_read() Ext2 정규 파일을 씀 페이지 ext2_file_wirte() 메모리 맵 파일 접근 없음 File_map_nopage() 스왑-아웃된 페이지 접근 do_swap_page() 버퍼캐시와 페이지 캐시 사용

6 디스크로 몰아쓰기(flush) 전에 버퍼량을 관리
버퍼 캐시 버퍼 캐시(buffer cache) 상대적으로 느린 디스크가 데이터를 읽고 쓰는 동안 기다리지 않고 프로세스를 자유롭게 놓아주는 일 입출력 연산이 사용자 프로세스의 성능과 사용자가 느끼는 응답시간에 주는 영향을 최한으로 줄인다. 커널 더티 비트 메모리 내의 버퍼가 관리 버퍼 타임스템프 디스크로 몰아쓰기(flush) 전에 버퍼량을 관리

7 버퍼 헤드 자료 구조 버퍼 헤드 집합 해시테이블 캐시에 들어 있는 버퍼를 설명
커널이 주어진 장치와 블록 번호 쌍에 대응하는 버퍼를 설명하는 버퍼 헤드를 빨리 찾도록 함

8 버퍼 헤드 자료 구조 버퍼 헤드 버퍼캐시 buffer_head 자료구조 bh_cachep 버퍼헤드 객체를 위한 메모리 캐시
디스크와 상호작용하지 않으며, 메모리를 효율적으로 관리하는 방법 (slab할당자 캐시) 버퍼 헤드 버퍼캐시 버퍼에 들어있는 데이터를 위한 디스크 캐시 할당한 버퍼 헤드의 수 즉, 슬랩할당자에서 얻은 객체 수를 nr_buffer_heads변수에 저장

9 버퍼 헤드 상태 사용하지 않는(unused) 버퍼 헤드 프리 버퍼에 대한 버퍼 헤드 비동기 버퍼 헤드
b_data 필드 : 프리 버퍼를 가리킴 b_dev 필드 : B_FREE값(oxffff)을 포함 비동기 버퍼 헤드 b_data 필드 : 페이지 입출력 연산을 구현하기 위해 사용하는 임시 버퍼를 가리킴

10 사용하지 않는 버퍼 헤드 리스트 사용하지 않는 버퍼 헤드의 리스트 unused_list 변수로 참조
b_next_free 필드 : 리스트의 다음 항목 주소를 저장 nr_unused_buffer_heads : 리스트의 현재항목 수를 저장 사용하지 않는 버퍼 헤드 리스트 :버퍼 헤드 객체를 위한 일차 메모리 캐시로 동작 bh_cachep (슬랩 할당자 캐시) :이차 메모리 캐시로 동작

11 사용하지 않는 버퍼 헤드 리스트 MAX_UNUSED_BUFFERS NR_RESERVED 리스트의 항목수가 36일 경우
버퍼헤드를 해제시켜 슬랩할당자에 넣는다. NR_RESERVED 페이지의 입출력 연산을 위해 예약되어 있는 16 항목 비어 있는 버퍼헤드가 없어서 발생될 수 있는 deadlock을 예방

12 사용핗지 않는 버퍼 헤드 리스트 get_unused_buffer_head() 새로운 버퍼헤드를 얻기 위해 호출되는 함수
call recover_reusable_buffer_heads() 리스트 제거 y 사용하지 않는 버퍼헤드 리스트가 NR_RESERVED를 초과하는 항목을 포함하는가? call Kmem_chche_alloc() 새로운 버퍼헤드 할당 주소 반환 n Null값 반환 메모리가 없는 경우 항목 제거 사용하지 않는 버퍼헤드 리스트가 비어있지 않는 경우

13 리스트가 MAX_UNUSED_BUFFERS보다
사용하지 않는 버퍼 헤드 리스트 put_unused_buffer_head() 버퍼 헤드를 해제하는 연산 수행 리스트가 MAX_UNUSED_BUFFERS보다 적은 항목을 포함 하는가? n Y 객체를 해제하여 슬랩할당자에 넣는다 사용하지 않는 버퍼 헤드의 리스트에 객체 삽입

14 프리 버퍼에 대한 버퍼 헤드 리스트 프리 버퍼의 헤드를 모으기 free_list 배열
여러 원형 리스트를 각 버퍼 크기마다 하나씩 사용 리스트 : 메모리 캐시로 동작 (필요할 때 원하는 크기의 프리 버퍼를 얻을 수 있다) 버퍼 헤드 리스트 : 7개 버퍼 크기에 따라 512, 1024, 2048, 4096, 8192, 16384, 32768 실제 pc 아키텍쳐에서는 앞 4개만 사용 free_list 배열 각 리스트에 대해 리스트의 첫째 항목 주소를 포함하는 항목이 있다 (BUFSIZE_INDEX를 사용하여 대응하는 배열 인덱스 생성) Free_list[0] = 512, free_list(1) = 1024, …… 리스트를 b_next_free, b_prev_free를 사용하여 연결

15 캐시 버퍼에 대한 버퍼 헤드 리스트 버퍼캐시에 버퍼를 포함하는 경우 대응하는 버퍼 헤드 플래그가 현재 상태를 나타냄
블록이 캐시에 없는 경우 디스크에 블록을 기록해야 하는 경우

16 디스크에서 블록을 읽어와서 새로운 버퍼를 할당
캐시 버퍼에 대한 버퍼 헤드 리스트 블록이 캐시에 없는 경우 디스크에서 블록을 읽어와서 새로운 버퍼를 할당 BH_Uptodate 플래그 삭제 버퍼의 내용이 의미가 없으므로. 디스크에서 블록을 읽어서 버퍼를 채우는 동안 실행. 버퍼를 해제시키는 현상을 막기 위해 실행. BH_Lock 플래그 = 1 BH_Uptodate 플래그 = 1 읽기 연산을 마친후 실행. BH_Lock 플래그 삭제

17 b_next_free, b_prev_free 구현
캐시 버퍼에 대한 버퍼 헤드 리스트 디스크에 블록을 기록해야 하는 경우 BH_Dirty 플래그 = 1 버퍼 내용이 바뀌므로. BH_Dirty 플래그 삭제 버퍼를 성공적으로 디스크에 기록한 다음에만 실행 사용한 버퍼와 관련한 모든 버퍼를 이중연결 리스트에 저장 b_next_free, b_prev_free 구현

18 캐시 버퍼에 대한 버퍼 헤드 리스트 더티 버퍼를 디스크로 몰아 쓰는 속력을 높이는 리스트 BUF_CLEAN BUF_DIRTY
더티가 아닌 버퍼의 버퍼헤드를 포함한다. 락이 걸려있는 상태거나, 물리적 장치에서 읽기 위 선택된 상태 디스크에 더티 버퍼를 몰아쓰는 함수가 해당 버퍼를 무시한다. BUF_DIRTY 물리적 장치에 기록하기 위해 선택하지 않은 더티 버퍼의 버퍼 헤드를 포함 블록장치 드라이버에 대한 블록 요청에 포함되지 않은 더티 버퍼를 포함 BUF_LOCKED 블록 장치에 기록하기 위해 선택한 더티 버퍼의 버퍼 헤드를 포함

19 캐시 버퍼에 대한 버퍼 헤드 리스트 사용된 버퍼와 관련한 버퍼 헤드
b_list 필드 : 버퍼를 포함하는 리스트의 인덱스를 저장한다. lru_list 배열 : 각 리스트의 첫째 항목 주소를 저장 nr_buffers_types 배열 : 각 리스트의 항목 수를 저장 mark_buffer_dirty(), mark_buffer_clean() 각각 BH_Dirty 플래그를 설정, 삭제 refile_buffer() 함수를 호출하면, 버퍼헤드는 BH_Dirty, BH_Lock 플래그 값에 따라 적당한 리스트로 이동

20 (장치 식별자, 블록번호, 대응하는 버퍼헤드의 주소)
캐시 버퍼에 대한 버퍼 헤드 리스트 캐시된 버퍼 헤드의 해시 테이블 버퍼 캐시 해시 테이블 버퍼 헤드 주소 버퍼 헤드 주소 (장치 식별자, 블록번호, 대응하는 버퍼헤드의 주소) 해당 버퍼 헤드 주소를 인출하기 위해 해시테이블 사용 커널 커널은 버퍼 입출력 연산을 시작하기 전에, 요청한 블록이 이미 버퍼 캐시에 있는지 검사한다. 해시테이블은 커널이 캐시된 버퍼 리스트를 순차적으로 탐색하는 상황을 피하게함.

21 캐시 버퍼에 대한 버퍼 헤드 리스트 캐시된 버퍼 헤드의 해시 테이블
hash_table 배열에 해시테이블은 시스템 초기화시 할당 크기 (64Mb 경우) 64(페이지 프레임수)×4096(페이지 프레임당 바이트수)/4(포인터를 담을 바이트수) = 65,536 nr_hashed_buffer 변수 : 버퍼헤드의 총 수를 저장 b_deb, b_blocknr 필드 : 요청한 버퍼 헤드 주소를 반환 Insert_into_queues(), remove_from_queues() :해쉬 테이블에 항목을 삽입, 제거

22 비동기 버퍼 헤드 리스트 비동기 버퍼 헤드 입출력 파일 연산에 사용 페이지 입출력 연산
페이지를 포함하는 페이지 프레임을 버퍼 그룹으로 본다. 그룹에 들어 있는 버퍼 수는 사용된 블록 크기에 따라 달라진다. 페이지의 모든 버퍼에는 대응하는 비동기 버퍼 헤드가 있어야 한다.

23 비동기 버퍼 헤드 리스트 비동기 버퍼 헤드 1024 black 4096 black 비동기 버퍼 헤드 1K 버퍼 그룹

24 비동기 버퍼 헤드 리스트 end_request() 블록 전송을 마치면 인터럽트 핸들러가 호출
요청 큐에서 블록 요청을 제거하는 일을 처리 요청에 포함된 모든 버퍼 헤드의 b_end_io 메소드를 호출

25 getblk() 함수 버퍼 캐시에 대한 주 서비스 루틴
버퍼가 캐시에 없을 경우, 새로운 엔트리를 캐시에 생성하기 위해 호출하는 함수 매개변수 : 장치식별자, 블록번호, 블록 크기 반환값 : 버퍼와 관련한 버퍼헤드 주소

26 블록 크기로 free_list 배열 인덱스 생성,
getblk() 함수 call find_buffer() 해시테이블을 사용하여 요청한 버퍼가 이미 캐시에 들어 있는지 검사 버퍼 헤드 유 or 무 카운터를 증가, 버퍼 헤드 주소를 반환 n 블록 크기로 free_list 배열 인덱스 생성, 대응하는 free_list가 비어있는가? n 비어있을 경우 비어있지 않을 경우 call refill_freelist() 프리 리스트를 다시 채운다 리스트에서 첫째 버퍼 헤드를 제거 장치식별자, 블록번호, 블록 크기로 버퍼헤드 초기화 b_end_io 필드에 end_buffer_io_sync()함수를 가리키는 포인터를 저장 b_count =1 call insert_into_quests() 버퍼헤드를 해시 테이블과 lru_list[BUF_CLEAN]리스트에 삽입 버퍼헤드 주소를 반환 call find_buffer() 다른 프로세스가 버퍼를 캐시에 넎었는가?

27 버퍼 사용 카운터 b_count 필드 대응하는 버퍼에 대한 사용 카운터
버퍼에서 연산을 수행하기 전에 증가하고, 수행한 다음 감소 커널 제어 경로가 버퍼에 접근하기 위해 사용 카운터를 증가 getblk()함수를 호출하여 처리 커널 제어 경로가 버퍼에 접근을 끝내면 brelse() 또는 bforget()을 호출하여 대응하는 사용카운터를 감소

28 버퍼 할당 버퍼 페이지 버퍼를 저장하는 전용 페이지 (효율성을 위해 메모리 객체 단위로 할당하지 않는다)
버퍼를 8, 4, 2, 1개 가질 수 있다. 버퍼 헤드의 b_this_page 필드는 버퍼 페이지에 있는 모든 버퍼를 원형 리스트로 연결

29 버퍼 할당 페이지 디스크립터 페이지 버퍼 버퍼 헤드 버퍼 버퍼 헤드 버퍼 버퍼 헤드 버퍼 버퍼 헤드
b_data buffers 페이지 디스크립터 b_this_page 페이지 버퍼 버퍼 헤드 버퍼 버퍼 헤드 버퍼 버퍼 헤드 버퍼 버퍼 헤드 버퍼 4개를 포함한 페이지와 버퍼 헤드

30 버퍼 할당 getblk() 함수 refill_freelist() 함수
프리 버퍼가 필요하면, 적당한 크기의 프리버퍼를 가리키고 있는 리스트에서 항목을 얻으려고 시도 refill_freelist() 함수 해당 리스트가 비어 있으면, 커널은 추가적인 페이지 프레임을 할당하고, 요청한 블록 크기의 새로운 버퍼를 생성 Call grow_buffers()만 호출, 새로운 버퍼를 할당 매개변수 : 할당할 버퍼의 블록 크기 반환값 : 성공=1, 실패=0

31 버퍼 할당 grow_buffers() 함수 call __ get_free_page() B 페이지 프레임이 없는 경우
사용가능한 페이지 프레임 유 or 무 페이지 프레임이 있는 경우 call create_buffers() Return o call get_unused_buffer_head() 반복 호출하여 페이지의 모든 버퍼에 대한 버퍼 헤드를 할당 시도 A

32 버퍼 페이지의 총 바이트수를 포함한 buffermem 변수를 변경
버퍼 할당 버퍼 헤드를 얻은 경우 버퍼 헤드를 얻지 못한 경우 A 버퍼 헤드 초기화 b_dev 필드 = B_FREE b_data 필드 = 페이지의 버퍼 시작 주소 B_this_page 필드로 버퍼 헤드 연결 페이지의 첫째 버퍼 헤드주소 반환 put_unused_buffer_head를 반복호출 Return NULL grow_buffers() = 0 비동기 버퍼 헤드가 생길때 까지 Buffer_wait 대기큐에서 대기 nr_buffers + 새로 생성한 버퍼 개수 B buffers 필드를 페이지의 첫째 버퍼 헤드 주소로 설정 버퍼 페이지의 총 바이트수를 포함한 buffermem 변수를 변경 Return 1

33 디스크에 더티 버퍼 기록하기 쓰기 지연(deferred writing)
쓰기 연산은 읽기 연산만큼 영향을 주지 않는다 (쓰기 지연으로 인해 프로세스가 보류되지는 않는다) 쓰기 지연을 극단적으로 수행할 경우 하드웨어 또는 전원이상이 발생하면, 시스템을 시동한 시점부터 많은 파일 갱신 내용을 잃어버린다. 버퍼 캐시의 크기(램)이 매우 커야 한다.

34 디스크에 더티 버퍼 기록하기 더티 버퍼를 디스크에 몰아 쓰는 경우
버퍼 캐시가 꽉 차고, 새로운 버퍼가 필요하거나, 더티 버퍼의 수가 너무 커졌을 때 버퍼가 더티 상태로 너무 오랫동안 있는 경우 프로세스가 블록 장치의 모든 버퍼 또는 특정 파일의 모든 버퍼를 몰아쓰도록 요청하는 경우

35 디스크에 더티 버퍼 기록하기 bdflush 커널 쓰레드 시스템 초기화 과정에서 만들어진다
더티 버퍼의 일부를 선택하여 대응하는 블록의 변경 내용을 물리적 블록장치에 기록한다. bdflush 동작 시스템 매개변수로 제어 bdf_prm 테이블의 b_un 필드에 저장 /proc/sys/vm/bdflush 파일 또는 bdflush() 시스템 콜을 통해서 접근 bdflush를 한 번 활성화 할 때 모든 더티 버퍼를 디스크에 기록하지 않는다. (쓰기 지연을 효율적으로 구현하기 위해 한 번에 많은 데이터를 기록하는 것은 비효율적이다) bdf_prm의 ndirty 매개변수에 한 번 활성화 할 때 몰아 쓰는 더티 버퍼의 최대수를 저장한다.

36 디스크에 더티 버퍼 기록하기 Kupdate 커널 스레드
타임아웃 매개변수 age_buffer : kupdate가 일반 버퍼를 디스크에 기록할 때까지 시간 age_super : 수퍼 블록에 대한 시간

37 디스크에 더티 버퍼 기록하기 Kupdate()함수 for(;;) { if (bdf_prm.b_un.interval) {
0이 아니면 스레드는 지정 틱만큼 작업을 보류 한다. for(;;) { if (bdf_prm.b_un.interval) { tsk->state = TASK_INTERRUPTIBLE; schedule_timeout(bdf_prm.b_un.interval); } else { tsk->state = TASK_STOPPED; schedule(); /*SIGCONT를 기다린다. */ } sync_old_buffers(); 그렇지 않으면 스레드는 SIGCONT 시그널을 받을 때까지 자신을 정지 시킨다. 더티 버퍼를 디스케에 기록하는 일을 한다.

38 디스크에 더티 버퍼 기록하기 Sync_old_buffers() Call syn_sypers()
현재 마운트한 모든 파일시스템의 수퍼블록을 탐색하기 위헤 super_blocks 배열에 접근 Call sync_inodes() 현재 마운트된 모든 파일시스템의 수퍼블록을 탐색하고, 각 수퍼블록에 대해 수퍼블록 객체의 s_diryt필드가 가지키는 더티 아니노드 리스트를 탐색한다. BUF_DIRTY와 buf_LOCKED 리스트 탐색 오래된 모든 더티 버퍼를 디스크에 기록한다. tq_disk 태스크 큐 함수 실행 블록을 디스크에 기록하는 데 필요한 저수준 블록 장치 드라이버를 시작한다.

39 디스크에 더티 버퍼 기록하기 sync(), fsync(), fdatasync() 시스템 콜
더티 버퍼를 디스크에 몰아쓰기 위해 사용자 애플리케이션에서 사용할 수 있는 시스템 콜 sync() Shutdown 전에 호출. 모든 더티 버퍼를 디스크에 기록 fsync() 프로세스가 현재 열려 있는 특정 파일에 속한 모든 블록을 디스크에 기록 fdatasync() Fsync()와 유사하지만, 파일의 아이노드 블록을 몰아 쓰지 않는다.

40 디스크에 더티 버퍼 기록하기 sync() 시스템 콜의 fsync_dev() 함수 Call sync_buffers()
리스트에 있는 버퍼 중에서 바뀌지 않거나, 락이 걸리지 않는 모든 버퍼에 대해 write 요청을 보낸다. Call sync_supers() write_super 메소드를 사용하여 더티 수퍼블록을 디스크에 기록한다. Call sync_inodes() write_inode 메소드를 사용하여 더티 아이노드를 디스크에 기록한다. Sync_supers()와 sync_inodes()가 추가된 버퍼를 더티로 표시했을 경우가 있기 때문에 제 호출한다.

41 Part I : 페이지 캐시

42 페이지 캐시 페이지 캐시 페이지 입출력 연산을 통해 접근하는 데이터에 대한 디스크 캐시
read(), write(), mmap() 시스템 콜을 통해 생성된 모든 정규 파일에 대한 접근은 페이지 캐시로 할 수 있다.(15장 참조) 주요작업 캐시에 없는 파일 부분에 접근할 때 페이지 추가하기 캐시가 너무 커졌을 때 페이지 제거 하기 주어진 파일 오프셋을 포함하는 페이지 찾기

43 페이지 캐시 자료구조 페이지 헤시 테이블 아이노트 큐
커널은 특정 아이노드와 파일 오프셋에 대응하는 페이지에 대한 페이지 디스크립터 주소를 빨리 얻을 수 있게 한다. 아이노트 큐 특정 파일의 데이터 페이지에 대응하는 페이지 디스크립터 리스트

44 페이지 캐시 자료 구조 페이지 해시 테이블 프로세스가 커다란 파일을 읽을 때, 이 파일과 관련한 페이지로 페이지 캐시를 채울 수 있다. Page_hash_table 요청한 파일 부분에 대응하는 페이지를 찾기위해 사용하는 헤시테이블 페이지 디스크립터 포인터 크기(64MB 경우) : 16(페이지프레임 수)×4096(프레임당 바이트 수)/4(포인터 크기) = 16,384 page_hash() 함수 아이노드 객체의 주소와 오프셋 값에서 해시 테이블 내 대응하는 항목주소를 얻어온다. add_page_to_hash_queue() 함수, remove_page_from_hash_queur() 함수 항목을 해시 테이블에 추가하고, 테이블에서 항목을 삭제하기 위해 사용

45 페이지 캐시 자료 구조 아이노드 큐 i_page 필드 : 아이노드 큐의 첫째 페이지 디스크립터 주소를 포함
i_nrpages 필드 : 리스트의 길이를 포함 add_page_to_inode_queue()함수, remove_page_from_inode_queur() 함수 페이지 디스크립터를 아이노드 큐에 삽입하고, 큐에서 디스크립터를 제거하기위해 사용

46 페이지 캐시 자료 구조 페이지 캐시와 관련한 페이지 디스크립터 필드 Inode offset next prev next_hash
페이지 안에 포함된 데이터에 대한 파일의 아이노드 객체주소를 포함 페이지가 페이지 캐시에 속하지 않으면, 이 필드는 NULL. offset 파일 안에서 데이터의 상대 주소를 나타낸다. next 아이노드 큐에서 다음 항목을 가리킨다. prev 아이노크 큐에서 이전 항목을 가리킨다. next_hash 페이지 해시 리스트에서 다음 충돌 페이지 디스크립터를 가리킨다. pprev_hash 페이지 해시 리스트에서 이전 충돌 페이지 디스크립터를 가리킨다.

47 페이지 캐시 처리 함수 find_page() 함수 add_to_page_cache() 함수
아이노드 객체의 주소와 오프셋 값을 매개변수로 받는다. Page_hasg() 함수를 사용하여 요청한 페이지를 찾는다. add_to_page_cache() 함수 새로운 페이지 디스크립터를 페이지 캐시에 삽입 Remove_indoe_page() 함수 페이지 디스크립터를 페이지 캐시에서 제거한다.

48 페이지 캐시 튜닝 페이지 캐시 튜닝 빠르게 증가하는 페이지 캐시를 위해, 프리 메모리가 모자라면 커널은 오래되어 쓰지 않는 페이지를 해제하여 페이지 캐시를 줄인다. 페이지크기는 미리 정의 한 크기 이하로 떨어질 수 없다. Page_cache 테이블의 min_percent 매개변수 페이지 캐시의 최소 크기 제한을 조정한다. 이값은 페이지 캐시에 속해야 하는 페이지의 최소 비율을 나타낸다 기본값 : 2% 매개변수 값 : sysctl() 시스템 콜 또는 /proc/sys/vm/pagecache 파일에 접근하여 읽거나 변경가능


Download ppt "리눅스 커널의 이해 중에서 14장. 디스크 캐시 남재준 네트워크 실험실."

Similar presentations


Ads by Google