Step by Step 커널 프로그래밍 강좌③
작성자 정보
- 웹관리자 작성
- 작성일
컨텐츠 정보
- 16,575 조회
- 0 추천
- 목록
본문
Step by Step 커널 프로그래밍 강좌③
리눅스커널의메모리관리
지난 호에 우리는 커널 모듈 프로그래밍에 관한 기본적인 것들을 살펴보았다. 이번 호
에서는 지난 번 모듈 프로그래밍에 이어 커널에서 가장 흔히 사용하는 함수 중의 하나
인 kmalloc을 통해 커널에서 시스템의 메모리를 어떻게 관리하는지 알아보기로 한다.
글 _ 김민찬 KLDP 멤버, 전문 프로그래머
연재 순서
① 커널 프로그래밍 환경 구축하기와 특징
② 모듈 구현하기
③ 리눅스 커널의 메모리 관리
④ 커널의 동기화에 관하여
⑤ 커널의 시간관리 및 지연 함수에 대하여
⑥ 파일시스템과 proc file system 사용하기
메모리 관련 코드는 파일 시스템 코드와도 연관
리눅스 커널의 메모리 서브시스템을 이해하기는 쉬운 일이 아니다. 여러 서브시스템 중에서도 메모리는 더욱 그러하다. 그런 이유는 리눅스 커널이 MMU를 사용해 물리 메모리와 가상
메모리를 구분하여 쓰고 있으며 demand paging을 사용하기때문이다. demand paging이란 아래 그림과 같이 페이지를 요청했을 경우 바로 할당해주는 것이 아니라 할당받은 페이지를 실제로 사용하게 될 때(그림 상에 진한색의 코드가 실행되었을 경우) 하드웨어(Memory Management Unit)에서 발생시키는 예외를 처리하여 그 때 메모리를 프로세스의 페이지 테이블에 매핑시켜주는 기술이다.
메모리를 더욱 이해하기 복잡하게 만드는 이유는 리눅스는 파일을 메모리에 매핑시켜 일반 파일이나 디바이스 파일을 마치 메모리에 접근하는 것처럼 다룰 수 있기 때문이다. 이 때문에
메모리 관련 코드는 파일 시스템의 코드와도 상당 부분 연관이 되어있다. 그렇기 때문에 짧은 지면에 모든 메모리 관련 기능들을 설명을 마친다는 것은 무리가 있다. 그러므로 이번 강
좌에서는 리눅스 커널의 메모리 관리에 있어 핵심적인 부분들만 살펴보기로 한다.
1. Kernel Memory Allocator
리눅스 커널에서 가장 흔히 볼 수 있는 함수로써 시스템의 연속된 메모리를 동적으로 할당받는 함수는 kmalloc 함수이다. 우리가 일반적으로 유저 모드 프로세스에서 dynamic memory를 할당받기 위해 사용하는 malloc함수의 커널 버전이라고 생각하면 된다. 하지만 메모리를 할당하기 위해 동작하는 방식은 너무나 틀리다. 어디까지나 kmalloc은 커널의 메모리를 할당하는 함수이며 이것을 사용하기 위해서 여러분들이 알고 있어야 할 것들이 꽤 있다. 이 함수는 malloc과 같이 아무곳에서나 사용할 수 있는 것은 아니다.
우선, kmalloc의 인터페이스는 다음과 같다.
static inline void *kmalloc(size_t size, gfp_t flags)
여러분의 예상대로 첫번째 인자는 할당받으려는 메모리의 크기이다. kmalloc으로부터 할당받은 메모리는 물리적으로 연속된 공간에 존재한다. 물리적으로 연속되었다는 사실에 대하여 다소 생소하게 받아들일 독자가 있을 것 같아 부연하면 다음과 같다. 일반적으로 유저 모드에서 다뤄지는 모든 주소는가상 주소이다. 가상 주소는 MMU를 통하여 물리 메모리에연결되게 되어 있다. 이것은 소프트웨어의 노력도 많이 들어가지만 근본적인 구조는 MMU라는 하드웨어에서 제공하는 기능이다. 이 하드웨어를 소프트웨어가 잘 support 하여 그렇게 동작하는 것이니 궁금한 독자는 x86 데이터시트를 천천히 살펴보라. 이 MMU의 기능과 Page Fault의 기능을 사용하면 실행 중에 사용자가 요청한 메모리를 시스템의 메모리 공간중 연속되어 있지 않은 공간에 메모리 영역들로 모아서 짜깁기하여 할당할 수 있게 된다. 이러한 기능은 메모리의 외부 단편화 때문에 사용하게 된다. 이 문제에 대해서는 뒤에서 다시 설명하기로 한다. 아래 그림과 같이 이렇게 시스템 메모리가 물리적으로는 연속되어 있지 않지만 MMU 테이블에 연속된
가상 주소의 공간에 매핑시켜 놓는다면 유저 모드 프로세스는 연속된 공간으로만 바라보게 될 것이다.
아래 그림은 시스템의 페이징 모델을 간단하게 나타낸 그림이다. 실제 페이징의 구현은 3단계나 4단계 레벨로 더욱 복잡하게 되어 있다.
사실 유저 모드 프로그램을 개발하는 사람들은 가상 주소나 물리 주소를 신경 쓸 필요도 없게 되어 있다. 하지만 커널은 다르다. 커널 또한 가상주소로 동작하긴 하지만 커널은 시스템
의 코어이기 때문에 연속되지 않은 물리메모리를 마치 연속된 것처럼 사용하게 된다면 그 후처리를 위한 비용이 크기 때문에 그렇게 다루는 경우는 드물다.(단, vmalloc을 사용할 경우
를 제외하고)
또한 kmalloc의 또 다른 특징은 여러분이 요청한 크기보다 더 큰 메모리를 할당하게 된다.(사실 이 부분은 여러분이 malloc을 호출했을 때도 마찬가지이긴 하다. malloc의 경우는 상황에 따라 brk나 mmap을 통하여 요청한 크기보다 큰 메모리를 확보하게 된다. 이는 glibc에서 일어나므로 응용 개발자가 신경쓸 일은 아니다). 그런 이유는 커널의 메모리 관리자는 기본적으로 페이지 단위로 메모리를 관리하기 때문이다. 그렇다면 4byte를 요청했는데 무식하게4K(일반적인 시스템의 페이지크기)를 반환하게 될까? 그렇지는 않다. 그 이유는 다음 장의 Slab Allocator 장에서 얘기하기로 한다.
두번째 인자는 할당받으려는 메모리의 속성을 나타낸다. 이 플래그는 아래와 같이 3개의 카테고리로 나누어 진다. 중요한 플래그들 몇 가지만을 살펴보기로 하자.
이 할당플래그들은 다음과 같이 or연산으로 묶어 사용할 수도 있다.
ptr = kmalloc(size, _GFP_WAIT | _GFP_IO | _GFP_FS);
위의 인자는 커널의 페이지 할당자에게 이 할당연산은 block 할 수 있으며, I/O 및 filesystem operation을 수행할 수 있다는 것을 의미한다. 이렇게 하는 것은 시스템의 사용 가능한 물
리 메모리가 모자랄 때 커널로 하여금 메모리를 확보할 수 있는 최대의 자유도를 주는 것이다. 커널은 물리 메모리가 모자랄 경우 익명 페이지들(anonymous page)을 스왑아웃시키거나 페이지 캐시를 해지하고 슬랩 캐시에 사용되는 슬랩 오브젝트들을 줄이는 페이지 회수 알고리즘을 동작시켜 최대한 메모리를 확보하려고 한다. 이런 과정에서 커널은 블록될 수 도있고 I/O나 파일시스템 관련 operation들을 호출할 수 있게되는 것이다.
이 플래그를 이용해서 커널은 메모리를 획득하기 위한 zone을 결정한다. zone에 대해서는 Buddy Allocator 장에서 자세히 다루도록 하며 여기서는 그냥 그런 것이 있다는 것만 알고 넘
어가자. _GFP_DMA 플래그를 명시하는 것은 커널로 하여금 DMA 장치가 인식할 수 있는 물리 메모리 를 확보하기 위함이다. 이것이 필요한 이유는 예전 ISA버스에 부착된 DMA 장치들은 RAM의 첫 16M만을 어드레싱 할 수 있었기 때문에 메모리 관리 측면에 있어서 따로 관리를 해주어야 했다. 마찬가지로 _GFP_HIGHMEM은 시스템이 어드레싱 할수 없는 물리메모리를 따로 관리하기 위한 zone이다. 일반적인 32bit machine에서 물리 메모리 896M까지만 linear address에 속하게 되며 그 이상부터는 high memory로 관리되게 된다. 이는 32bit machine이 표현할 수 있는 주소는 전체 4G이지만 4G 중 3G이상의 영역부터 시작해서 1G
관련자료
-
이전
-
다음