'리눅스'에 해당되는 글 2건

  1. 2017.12.10 페이지 캐시의 크기 제한걸기 (1/2)
  2. 2012.09.30 VFS : Virtual File System - Introduction
Writings/Linux kernel2017. 12. 10. 00:10

리눅스 커널은 I/O 성능을 높이기 위해 페이지 캐시를 제공한다. 


페이지 캐시는 파일 시스템의 각 오퍼레이션들에 삽입되어 (엄밀히 말하자면 VFS 인터페이스겠지만,) 블록 디바이스의 데이터 페이지를 메모리에 캐싱해 둔다. 

이후 요청이 들어왔을 때 해당 블록 어드레스에 대한 페이지가 메모리 내에 있을 경우, 캐시 히트로 간주하여 디바이스에 대한 I/O를 발생시키지 않고 메모리에 캐싱되어 있는 페이지를 제공한다. 

Write가 발생했을 때도 이를 바로 블록 디바이스로 보내는 것이 아니라  메모리에 캐싱해두며, 특정 시점 혹은 fsync() 호출이 발생할 때까지 동일한 블록 어드레스에 대한 모든 write를 흡수한다. 


이런 캐시는 I/O가 빈번이 발생하는 경우에는 도움이 되겠지만, 커널의 메모리 사용량을 늘리는 결과를 야기시키기도 한다.


커널은 이를 제한할 수 있는 인터페이스를 sysfs를 통해 제공하게 되는데, 그 안을 보면 꽤 많은 항목들이 있다. 일단 경로는 다음과 같다.


/proc/sys/vm


이 디렉토리 중, 다음의 두 항목을 살펴보자.


/proc/sys/vm/drop_caches

/proc/sys/vm/vfs_cache_pressure


drop_caches는 입력에 따라 서로 다른 수준으로 시스템의 캐시를 비워주게 된다. 


1) echo 1 > /proc/sys/vm/drop_caches // 페이지 캐시만을 비운다.

2) echo 2 > /proc/sys/vm/drop_caches // dentry 캐시와 inode 캐시를 비운다.

3) echo 3 > /proc/sys/vm/drop_caches // inode, dentry, 페이지 캐시 모두를 비운다.


페이지 캐시는 실제 파일에 저장되는 데이터 페이지를 캐싱하며, dentry 및 inode 캐시는 파일시스템에서 사용하는 메타데이터를 캐싱한다. 

즉, 1번 명령은 데이터 캐시를 비우는 명령이며, 2번 명령은 메타데이터 캐시를 비우는 명령인 것이다.

참고로, 위의 명령들은 캐시를 디스크에 flush 시키는 명령이 아니기 때문에 먼저 sync를 실행시킨 뒤에 실행 시킬 것을 권장하고 있다. 


두번째는 vfs_cache_pressure 이다.

해당 항목에 대한 커널 도큐먼트의 설명을 살펴보면, 메모리 회수를 위해 inode 및 dentry 캐시에 압박을 준다고 되어 있다. 

100을 기준으로 그 위의 값은 일반적인 것보다 더 큰 압박을 주는 것이고, 그 아래의 값은 압박을 덜 줌으로써 좀더 메타데이터를 캐싱할 수 있도록 하는 것이다.

특별히, 0은 절대 주지 말라고 한다. 해당 메모리가 회수가 안되서 OOM 킬러에 의한 오작동이 나타날 수 있다고 한다.

'Writings > Linux kernel' 카테고리의 다른 글

페이지 캐시의 크기 제한걸기 (2/2)  (0) 2017.12.11
Enabling Cgroup v2  (0) 2017.12.09
Posted by 곰푼
Tips/Kernel development2012. 9. 30. 01:21

Virtual File System


리눅스를 비롯한 대부분의 유닉스 계열 운영체제는 VFS라는 레이어를 제공한다. 이것은 표준의 SCI(System Call Interface)와 ext2, ext3 를 비롯한 실제 파일시스템 구현 사이에 존재하는 일종의 인터페이스이며, 커널은 이 인터페이스를 통해서 파일시스템 수준에서 동작해야 하는 작업을 처리하기 때문에 가상 파일 시스템(VFS)라는 이름이 붙게 되었다.


그렇다면 왜 VFS가 존재하게 되었는가?


컴퓨터 기술의 발전과 동시에 이를 관리하기 위한 시스템 기술들도 발전하게 되었다. 커널은 비선점형 커널에서 선점형 커널로 발전하였고, 멀티 코어를 지원하기 위한 개념들도 등장하게 되었다. 이는 저장장치와 이 저장장치를 다루는 드라이버 레벨의 소프트웨어, 그리고 보다 추상적인 관점에서 운영체제와 저장장치간의 교두보 역할을 하게 되는 파일 시스템도 마찬가지이다. 기존의 UFS에서 도스 기반의 FAT, 리눅스의 등장과 함께 그 발전을 함께한 ext 계열의 파일 시스템들이 바로 그 맥락에서 발전해온 파일 시스템들이다. 


파일 시스템은 사용자 어플리케이션을 대신하여 파일 레벨의 작업을 수행한다. 사용자 어플리케이션이 표준 라이브러리의 함수를 호출하여 특정 파일에 '쓰기'작업을 한다고 치면, 라이브러리는 커널의 write() 시스템 콜을 호출할 것이다. 커널은 해당 시스템 콜에서 타겟으로 하는 파일이 존재하는 파일시스템에게 write() 시스템 콜에서 정의되어 있는 루틴대로 작업을 시킬 것이고, 파일 시스템은 작업을 처리한 후 커널에게 다시 적절한 값을 리턴할 것이다. 최종적으로 사용자는 커널과 표준 라이브러리로부터 리턴값을 돌려받아 작업이 제대로 처리되었는지를 확인하게 될 것이다.


이는 파일 시스템이 하나만 존재하는 경우에만 성립한다 할 수 있다. 파일 시스템이 둘 이상 존재하게 될 경우, 모든 파일 시스템이 동일한 함수를 제공하지 않는 이상 시스템 콜도 그에 맞추어 서로 다른 루틴으로 작성되어야 할 것이다. 이렇게 되면 새로운 파일 시스템이 생길 때마다 새로운 시스템 콜이 작성되어야 할 것이고, 운영체제도 다시 컴파일 되어야 할 것이다.


이런 문제점을 해결하기 위해서 커널은 모든 파일 시스템이 지켜야할 일종의 표준 모델을 제시하였다. 앞으로 살펴볼 VFS상의 컴포넌트들이 바로 그것인데, 리눅스 커널은 이 모델을 정의하며 객체 지향 모델의 특징을 가져왔다. 하나의 자료구조에 관리를 위한 변수 뿐 아니라 함수의 포인터들의 선언으로 이루어진 테이블을 정의한 것이다. 다음은 표준 모델 중 하나인 struct file의 간략한 예시이다. 


struct file {

struct list_head f_list;

struct dentry * f_dentry;

struct vfsmount * f_vfsmnt;

struct file_operations * f_op;

...

};


struct file_operations {

int (*llseek) (struct file*, int, int);

int (*func2) (int, void *);

...

};


file_operations는 해당 함수의 원형을 정해놓고, 포인터의 형태로 선언한 것이다. 파일 시스템은 이러한 구조체로 제시된 함수의 형식에 맞추어 그 세부사항을 구현하면 된다. 그렇게 되면 커널은 VFS 수준에서 해당 파일시스템의 함수 포인터를 이용한 호출로 작업을 완료할 수 있게 된다. 마치 객체 지향 모델의 인터페이스(혹은 추상클래스)와 같은 모습이라 할 수 있겠다. 


이러한 표준 모형을 제시하기 위해서 VFS는 4개의 객체를 정의한다. 각각은 다음과 같다.

  1. super_block
  2. inode
  3. dentry
  4. file
이 다음부터는 각각의 자료구조와 역할에 대해서 알아보기로 한다.


'Tips > Kernel development' 카테고리의 다른 글

PID Namespace  (0) 2012.03.15
Linux Kernel : 커널이란  (0) 2012.01.31
Posted by 곰푼