Linux kernel v4.4에서 간단한 블록 장치 드라이버 만들어보기

페이지캐시 실험

페이지캐시가 뭔지 알아보기 위해 약간의 실험을 해보겠습니다. 

우선 mybrd를 실험했던 커널을 다시 부팅합니다. 당연히 mybrd 드라이버도 포함되겠지요. 부팅하자마자 첫번째로 실행할 명령은 free -k 와 cat /proc/zoneinfo | grep file_pages 입니다.

/ # cat /proc/zoneinfo | grep file_pages
    nr_file_pages 0
    nr_file_pages 966
/ # free -k
             total       used       free     shared    buffers     cached
Mem:        114160      21408      92752       3864          0       3864
-/+ buffers/cache:      17544      96616
Swap:            0          0          0

free 명령은

  • total: 총 메모리
  • used: 사용중인 메모리
  • free: 남은 메모리
  • shared: 공유 메모리
  • buffers: 파일시스템 자체를 관리하는 메타데이터
  • cached: 파일의 내용을 메모리에 캐시하는 페이지캐시

free 명령에서 buffers와 cached의 차이가 뭔지 저도 많이 헷갈렸었는데 검색해도 설명들이 애매했었습니다. 그런데 레드햇 사이트에 설명이 잘 되있네요.

우리는 페이지캐시에 관심이있으니 cached 값만 보도록 하겠습니다. 현재 cached 페이지의 크기가 3864k 입니다. -k 옵션은 kilobyte로 출력하라는 옵션입니다. -m을하면 megabyte로 나오겠지요.

/proc/zoneinfo는 각 zone에서 얼마만큼의 메모리에 어디에 사용되고 있는지를 보여주는 것입니다. 여기에서 페이지캐시는 nr_file_pages 항목에 해당됩니다. 나중에 커널 코드를 볼때 nr_file_pages 항목을 업데이터하는 코드가 나올 것입니다.

이상태에서 mybrd에서 파일로 데이터를 옮겨보겠습니다. 드라이버에서 파일로 데이터를 옮기는 것이므로 어플 입장에서는 읽기에 해당합니다.

/ # dd if=/dev/mybrd of=./big
[  120.253239] mybrd: start mybrd_make_request_fn: block_device=ffff880006194340 mybrd=ffff8800065ab240
[  120.253945] CPU: 1 PID: 1053 Comm: dd Not tainted 4.4.0+ #76
[  120.254408] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Ubuntu-1.8.2-1ubuntu1 04/01/2014


------------------- 생략

4194304 bytes (4.0MB) copied, 1.999599 seconds, 2.0MB/s
/ # 
/ # cat /proc/zoneinfo | grep file_pages
    nr_file_pages 140
    nr_file_pages 1850
/ # free -k
             total       used       free     shared    buffers     cached
Mem:        114160      25572      88588       7960          0       7960
-/+ buffers/cache:      17612      96548
Swap:            0          0          0

dd 명령을 실행하면 mybrd 디스크의 크기가 4MB이므로 총 4.0MB의 복사가 발생합니다. free 명령을 실행해보면 cached의 값이 4096K 증가된걸 볼 수 있습니다. 정말 정확하게 4MB의 데이터가 디스크에서 메모리로 복사된 것입니다. 바로 이렇게 장치의 데이터를 메모리로 복사해놓는게 페이지캐시입니다. 사실 파일을 만든다는 것은 그 파일이 저장되어있는 디스크에 공간을 할당하고, 디스크에 데이터를 저장한다는 것입니다. 그런데 파일을 만들때마다 디스크에 IO가 발생한다면 디스크의 성능이 곧 컴퓨터의 성능이 될 수 있습니다. 하지만 이렇게 메모리에 보관해놓는다면 디스크보다 메모리가 더 빠르니 컴퓨터 전체의 성능이 더 빨라지겠지요. 메모리에 있는 데이터는 나중에 컴퓨터가 한가할때 디스크에 쓰면, 사용자는 알아차리지도 못할 것입니다.

zoneinfo 파일에서 nr_file_pages 항목을 볼까요. 2개의 zone이 있으므로 2개의 값이 나타났는데 어쨌든 총 증가된걸 따지면 1024가 됩니다. 이건 페이지의 갯수이므로 곧 4MB라는 의미입니다.

이번에는 파일을 파일로 복사해보겠습니다.

/ # dd if=./big of=./big2

8192+0 records in
8192+0 records out
4194304 bytes (4.0MB) copied, 0.003766 seconds, 1.0GB/s
/ # cat /proc/zoneinfo | grep file_pages
    nr_file_pages 294
    nr_file_pages 2720
/ # free -k
             total       used       free     shared    buffers     cached
Mem:        114160      29512      84648      12056          0      12056
-/+ buffers/cache:      17456      96704
Swap:            0          0          0

새로 4MB의 파일이 생성되었습니다. 마찬가지로 페이지캐시가 4MB 늘어났습니다.

그럼 파일에서 mybrd 장치로 데이터를 써볼까요.

/ # dd if=./big of=/dev/mybrd/ 

/ # free -k
             total       used       free     shared    buffers     cached
Mem:        114160      33788      80372      12056          0      12056
-/+ buffers/cache:      21732      92428
Swap:            0          0          0

파일의 데이터는 이미 메모리에 있는 데이터입니다. 따라서 파일 데이터를 장치에 써봐야 페이지캐시의 크기는 늘어나지 않습니다.

그런데 used의 값이 증가했습니다. 왜냐면 mybrd 드라이버가 데이터를 저장하기위해 페이지 할당을 했기 때문입니다. 4276KB가 증가했네요. 데이터를 보관하기위한 페이지뿐 아니라, 그 페이지를 관리하기위한 메타데이터도 증가했기 때문입니다. radix-tree에 페이지가 추가되면 트리 노드도 만들어져야하니까요.

그럼 장치 드라이버가 쓴 메모리는 왜 used가 되고 파일에 쓴 메모리는 cached가 될까요.

파일에 쓴 메모리는 나중에 시스템에 메모리가 부족해지면 메모리의 데이터를 디스크에 저장하고, free 메모리로 쓸 수 있습니다. 그래서 cached로 구분하는 것입니다. free 명령의 두번째 라인에 -/+ buffers/cache 값을 보면 used와 free에서 버퍼/캐시를 뺀 값을 보여줍니다. 메모리가 부족해지면 페이지캐시를 제거할 수 있으므로 최대 92428KB의 메모리를 확보할 수 있다는걸 보여주는 것입니다.

드라이버가 할당한 메모리는 드라이버가 스스로 반납하지않는한 계속 사용중인 메모리입니다. 드라이버가 시스템 전체의 메모리가 부족한지 알 방법도 없고 알 이유도 없습니다. 드라이버는 자기 동작만 확실하게 하면 되기때문에 드라이버가 할당한 메모리를 회수할 방법이 없습니다. (있긴 있습니다만 예외적인 것이니까요.) 그래서 used로 구분합니다.

참고로 커널이 할당하는 페이지는 페이지 속성중에 unmovable 속성을 가집니다. compaction을 해서 연속된 메모리를 확보할때 페이지 데이터를 옮기는데 함부로 옮길 수 없기 때문입니다. 드라이버가 어떤 데이터의 물리 주소를 사용하고 있는데, 커널이 이 데이터를 옮겨버리면 물리 주소가 바껴버리고, 드라이버는 바뀌기전의 물리 주소를 참조하게 되니 엉뚱한 메모리에 접근하게되니까요.

어플에게 할당된 페이지는 movable입니다. 어플은 가상 주소를 사용하므로 페이지의 데이터를 옮기더라도 어플의 페이지 테이블의 물리 주소만 바꿔주면 어플은 계속 같은 가상 주소로 바뀐 메모리를 접근할 수 있으니까, 페이지가 옮겨질 수 있습니다.

현재 시스템의 페이지들이 얼마나 movable이냐 unmovable이냐 등은 /proc/pagetypeinfo로 확인할 수 있습니다. 위에서 실험한 내용을 다시 해보면서 /proc/zoneinfo뿐 아니라 /proc/pagetypeinfo의 내용도 어떻게 변하는지 확인해보세요.

/ # cat /proc/pagetypeinfo 
Page block order: 9
Pages per block:  512

Free pages count per migrate type at order       0      1      2      3      4      5      6      7      8      9     10 
Node    0, zone      DMA, type    Unmovable     11      4      5      2      1      1      1      1      1      1      0 
Node    0, zone      DMA, type      Movable      0      2      3      2      3      3      3      1      0      0      1 
Node    0, zone      DMA, type  Reclaimable      5      1      7      3      1      0      0      1      1      1      0 
Node    0, zone      DMA, type   HighAtomic      0      0      0      0      0      0      0      0      0      0      0 
Node    0, zone    DMA32, type    Unmovable      1      2     11      6      6      2      2      2      2      1      0 
Node    0, zone    DMA32, type      Movable     12     11      7      5      7      5     10      4      2      2     13 
Node    0, zone    DMA32, type  Reclaimable      8      5     20     15      2      1      0      0      0      1      0 
Node    0, zone    DMA32, type   HighAtomic      0      0      0      0      0      0      0      0      0      0      0 

Number of blocks type     Unmovable      Movable  Reclaimable   HighAtomic 
Node 0, zone      DMA            3            3            2            0 
Node 0, zone    DMA32           10           42            4            0 

 

 

댓글

댓글 본문
작성자
비밀번호
버전 관리
gurugio
현재 버전
선택 버전
graphittie 자세히 보기