Memory Management
2014년 2학기 시스템 프로그래밍 시험 공부

시스템 프로그래밍 시험 공부하면서 정리한 내용이다. 내용 갱신은 없을 예정이다.

Linux Memory Management

  • 가상 메모리 요구 페이지 (Demand Paged Virtual Memory) 모델
    • 물리 페이지의 매핑, 할당, 관리
    • 2차 메모리의 관리 : swapping
  • 아키텍쳐 독립적인 모델
    • 다양한 아키텍쳐에서 다양한 메모리 매핑을 지원하는 인터페이스
    • include/linux/mm.h, mm/*
  • 아키텍쳐 매핑이 필요하다.
    • 메모리 모델은 물리 메모리에 매핑되어야한다.

Process (Virtual) Address Space

  • 프로세스의 선형 주소공간은 2개의 구역으로 구성된다
    • 유저 주소 공간
    • 커널 주소 공간

유저 주소 공간

  • 0x00000000 ~ PAGE_OFFSET (IA32의 경우 일반적으로 0xC0000000, 3GB)
  • 유저모드, 커널 모드에서 접근 가능

커널 주소 공간

  • PAGE_OFFSET (3G) ~ 0xffffffff
  • 커널 모드에서만 접근 가능
  • 커널은 모든 프로세스 주소 공간의 높은 메모리에 매핑되지만, 낮은 주소의 물리 메모리에 저장된다.
    • 선형 주소에서 커널의 시작은 3G이지만 물리 주소에서는 0G
  • 커널 주소 공간은 모든 프로세스에 대해 동일한 매핑을 갖는다.
  • 커널 매핑을 공유하기 때문에 프로세스를 생성해서 새로운 페이지 디렉토리가 생성될때 Kernel Page Directory(PGD) 부분만 복사하면 된다.

가상 주소 공간을 유저/커널로 나누는 이유?

  • 주소공간을 나누지 않고 4GB를 통째로 사용하면…
    • 커널모드와 유저모드 스위칭 할때 비용이 크다.
    • TLB(Translation Lookaside Buffer) 플러시 발생
  • why?
    • 유저 어플리케이션이 커널 모드로 넘어가면 커널은 안정적인 환경에 포함되어야한다. 즉, 커널의 주소공간을 분리해야 된다는 것이다.
    • 물리 페이지와 커널의 시작은 직접 매핑되어있다. 따라서 커널은 페이지 테이블을 조회없이 물리 메모리에 접근할수 있다.

Split ratio

  • 일반적으로 3:1 (user:kernel)을 사용
  • 1:3이나 2:2로 나누는 것도 가능하다.
    • 예를 들어 네트워크 라우터같이 커널코드가 주로 작동하는 경우 커널에 메모리 더 할당할 수 있다
    • 커널의 사용 가능 메모리가 늘어나는 만큼 유저 프로세스의 가용 메모리는 줄어든다.

Physical Memory Mapping in Linux/x86

물리 메모리 매핑

  • 물리 메모리는 커널 주소공간으로 직접 매핑된다.
  • PAGE_OFFSET=3G일때 최대 1GB
  • 따라서 직접 매핑될수 있는 물리메모리는 1GB보다 작야아한다. (최대 896MB)
  • 커널영역의 메모리 할당은 물리 메모리에서 수행된다.
  • 선형주소 - 0xc000000 = 물리주소
    • 가상:0xc0000000 -> 물리:0x00000000
    • 가상:0xffffffff -> 물리:0x3fffffff
  • arch/i386/mm/init.c:paging_unit() -> pagetable_init()
    • 모든 물리 페이지 프레임을 커널 주소 공간으로 직접 매핑하는 초기화
  • 커널 영역에서는 페이징 신경 쓰지 않아도 된다.
  • 용어
    • 커널 주소공간 메모리는 직접 메모리에 매핑되는 896M 이하의 normal (lowmem)과 직접 매핑할수 없는 896MB 위의 highmem 으로 구분한다.

highmem

  • 왜 커널은 1GB를 전부 매핑하지 않는가?
    • 커널은 다른 목적으로 마지막 128MB의 주소 공간을 예약해야 한다.
  • 커널이 128MB를 사용하는 곳
    • vmalloc 공간
      • 연속적인 커널 가상 주소를 불연속적인 물리 주소로 할당
      • 커널은 불연속적인 물리 주소를 될수있는한 피하려고 한다. 하지만 시스템이 오래 작동하면 커널이 물리메모리를 필요로 할때 연속적인 공간이 없을수도 있게된다. (파편화)
    • Persistent Mapping (high memory mapping)
      • Highmem 영역의 비영구적 페이지를 커널로 매핑할때 사용
    • Fixmaps
      • 물리 주소 공간에서 자유롭게 선택하지만 고정된 페이지와 연관되는 가상 주소 공간 항목

High memory mapping

  • persistent mapping, temporary mapping
  • high memory는 커널 주소 공간으로 영구적으로 매핑 불가능
  • high memory가 할당되면 그것은 직접 주소를 붙일수 없다. 주소를 붙이려면 kmap()를 호출해서 메모리 페이지를 커널 페이지 테이블 안으로 입력해야한다. 그래야 kunmap()을 호출하기전 까지 주소가 유효하다. 이 페이지에 접근하려며 kmap(), kunmap()를 써야한다.
  • 대용량 저장 장치의 I/O 버퍼로 주로 사용된다.
    • 그것들은 커널 공간을 많이 차지한다 1GB 이상을 차지할수도 있다.

Physical Memory Layout for Kernel Code

  • 커널은 고정
    • 스왑 불가능, 영구적으로 매핑
    • 커널 코드와 자료구조는 예약된 페이지 프레임에 저장된다
    • 해당 프레임은 동적으로 할당되거나 스왑되는 것이 불가능
    • 리눅스 커널 : 물리 메모리의 0x00100000 (2번째 MB)부터 2MB 이하의 공간에 올라간다
  • 첫번째 MB?
    • 페이지 프레임 0바이오스가 Power-On-Self-Test(POST) 하는 동안 감지한 시스템 하드웨어 설정을 저장할때 사용
    • 0x000a0000 (640KB) ~ 0x000fffff (1KB)
      • 바이오스 루틴과 ISA 그래픽 카드 내부 메모리로 예약
    • 불연속적인 페이지 프레임에 커널을 올리는것을 피하려고 메모리상의 첫번째 MB를 생략
  • 커널 고정 이후 남아있는 물리 메모리
    • 커널 페이지 할당자 용으로 할당
    • 버디 시스템
  • 메모리 구조
    • 0x00100000 : initialization. 커널의 시작
    • 0x00101000 : Kernel page director (Master kernel PGD), swapper_pg_dir, 4KB
    • 0x00102000 : pg0, 4KB
    • 0x00103000 : pg1, 4KB
      • pg0, pg1 : 0-8MB에 대한 임시 커널 페이지 테이블

Constructing Page Table

  • 커널 페이지 테이블 생성
  • 유저 페이지 테이블 생성
  • 프로세스의 주소공간 : 4GB, 총 페이지 테이블의 갯수 : 1 + 1024
    • 커널 영역 : 1GB
      • 커널 영역의 페이지 테이블 : 256개. 모든 프로세스에서 동일
    • 유저 영역 : 3GB
      • 유저 영역의 페이지 테이블 : 1 + 768개. 페이지 디렉토리는 프로세스마다 특화니까 여기 포함

Constructing Kernel Page Table

  • 커널은 자신이 사용하기 위한 페이지 테이블 세트를 유지
    • Master kernel Page Global Directory 라고 부른다
    • 시스템 초기화 후에, 이 테이블은 다른 프로세스나 커널 쓰레드에서 직접 사용되지 않는다.
    • Master kernel Page Global Directory의 769~1024 항목은 시스템의 모든 프로세스의 Page Global Directory에서의 참조모델이다.
      • 커널 주소 공간이 가리키는 물리 메모리는 모든 프로세스에서 동일
      • 3GB~4GB = 769~1024
  • 커널 페이지 테이블은 2단계로 초기화
    • 커널 이미지가 메모리(0x00010000)에 올라간 직후에는 CPU는 real mode이다.
    • 1단계 : 커널은 제한된 8MB의 주소공간의 생성한다. 8MB는 커널을 메모리에 설치하고 기초 자료구조를 초기화하는데 충분
    • 2단계 : 커널은 페이지 테이블을 초기화하고 페이징 활성화

1단계. Provisional Kernel Page Tables

  • Master Kernel PGD
    • 컴파일시 정적으로 초기화
    • swapper_pg_dir 변수에 포함되어있음
      • pgd_t swapper_pg_dir[1024]
  • 임시 페이지 테이블
    • arch/i386/kernel/head.S:startup_32()로 초기화됨
    • 첫 번째 8MB 램에 걸쳐 두 페이지 테이블은 PG0과 PG1에 포함되어 있다
      • 커널 세그먼트, 임시 페이지 테이블, 일부 동적 자료 구조를 128KB의 메모리 영역이 처음 8MB에 들어간다고 가정
      • 목적 : 8MB의 램을 real/protected 모드에서 쉽게 addressing하기
      • 다시 말하면, 8MB의 물리주소(0x00000000부터 시작)와 8MB의 가상주소(0xc000000에서 시작)을 동시에 취급

상세

  • 8MB = 페이지 테이블 2개 필요. 페이지 테이블 1개로 4MB 표현 가능
  • 0x00000000부터 시작하는 페이지 테이블을 PG0, PG1에 대응. PGx의 주소값을 Master Kernel PGD에 설정
    • swapper_pg_dir[0] = PG0
    • swapper_pg_dir[1] = PG1
  • 0xc0000000부터 시작하는 페이지 테이블을 PG0, PG1에 대응. PGx의 주소값을 MAster Kernel PGD에 설정
    • swapper_pg_dir[768] = PG0
    • swapper_pg_dir[769] = PG1
  • 0x00000000기반으로 접근하건 0xc0000000기반으로 접근하건 같은 물리 주소에 대응됨
    • 물리주소라고 별도로 주소 계산할 필요 없음

2단계. Final Kernel Page Table

  • Page Global Directory 설정 끝내기
    • 0xc000000로 시작하는 가상주소를 0x00000000로 시작하는 물리주소로 매핑
  • 3가지 경우가 가능함
    • RAM < 896MB
    • 896MB <= RAM <= 4096MB
    • RAM > 4096MB

RAM < 896MB ?

  • 목표 : 0xc0000000로 시작하는 선형주소를 0x0으로 시작하는 물리주소에 직접 매핑하기

    • swapper_pg_dir에 저정되어있는 master kernel PGD는 paging_init()에 의해 다시 초기화
      • pagetable_init() 호출해서 페이지 테이블 엔트리 적절히 설정
      • swapper_pd_dir의 물리 주소를 cr3 레지스터에 쓰기
      • flush_tlb_all()을 초루해서 모든 TLB 항목 무효화
  • arch/i386/mm/init.c:paging_init() -> pagetable_init()

    • 커널 페이지 테이블 초기화하고 페이징 활성화
    • low-memory의 모든 물리 페이지 프레임을 PAGE_OFFSET 위의 커널 주소 공간으로 직접 매핑 초기화
    • 커널이 페이지 테이블 거치지 않고 물리 주소 접근 가능하도록 만든다

Constructing Process Page Table

  • 모든 프로세스 페이지 테이블 마다…
    • Page Global Directory의 첫번째 항목은 3GB 아래의 선형주소에 매핑 (PAE가 꺼져있으면 첫번째 768 항목)
    • PGD에 남아있는 마지막 256개의 항목은 master kernel PGD의 대응되는 항목과 동일해야한다
    • 페이지 테이블은 커널 데이터 세그먼트에 저장됨

Handling Physical Memory in Kernel

  • 커널 영역에서 코드를 작성할때는 조심할것
    • 너무 많은 메모리 사용하지 말것
    • 재귀 사용 금지. (커널 스택의 크기가 제한되어있다)
  • 물리 메모리의 구성
    • 0 ~ 1MB : 바이오스 통신 영역. 일부 장치에서 사용
    • 1 ~ 8MB : 커널 코드 이미지. 커널이 올라간다.
    • 8 ~ 16MB : DMA 지역. DMA용으로 예약
    • 16MB ~ ? : mem_map, 물리 메모리 맵
    • ? ~ 1GB : 동적으로 할당
    • 1GB ~ : 동적으로 할당되고 가상 메모리 공간에 매핑
  • 실제 프로세서의 하드웨어 제약 (예, x86)
    1. 일부 DMA 하드웨어는 특정 영역의 메모리 주소만 접근 가능
    2. 일부 아키텍쳐는 선형주소보다 큰 물리 메모리를 지원
  • 다양한 Memory Zone
    • 커널은 페이지 프레임을 다양한 zone으로 나눈다.
    • 커널은 zone을 사용해서 유사한 특성의 페이지 프레임 영역을 묶는다.
      • ZONE_DMA
      • ZONE_NORMAL
      • ZONE_HIGHMEM
    • 유저 페이지를 위한 요청은 다음의 순서로 처리
      1. NORMAL
      2. HIGHMEM
      3. DMA

ZONE_DMA

  • 첫번째 16MB 물리메모리 (0~16MB, i386)
  • DMA가 접근 가능한 메모리
  • DMA는 하드웨어에 의해서 접근가능한 일부 메모리 공간이 필요하다.
    • 이것은 캐시되지 않으며 물리적으로 연속이어야 한다.

ZONE_NORMAL

  • 16~896MB 구간의 물리 메모리 (i386 기준)
  • 일반적으로 어드레싱되는 페이지
    • 일반적, 정규적으로 매핑되는 페이지 프레임을 포함한다.

ZONE_HIGHMEM

  • 896MB 이상의 물리 메모리
  • 동적으로 매핑되는 페이지
    • 지속적으로 커널 주소영역에 매핑되지 않는 페이지 프레임을 포함
  • 896MB 위쪽의 물리 메모리는 커널이 해당 메모리에 접근할 필요가 있을때만 일시적으로 커널 가상 메모리로 매핑된다.
  • ‘highmem’ 다루기
    • high memory가 처음 할당되면 그것은 직접 어드레싱 불가능하다. kmap(), kunmap(), kmap_atomic(), kunmap_atomic()을 이용해서 highmem을 커널 가상 메모리로 일시적으로 매핑할수 있다.
    • 해당 메모리에 어드레싱 하려면…
      1. kmap()를 호출해서 메모리 페이지를 커널 페이지 테이블에 넣는다
      2. kunmap()를 호출하기 전까지 주소는 유요하다
      3. 이 페이지에 접근하기전에 kmap()~kunmap()를 호출하는 과정이 필요하다.

Zone data struct

  • include/linux/mmzone.h
  • zonelist[] : zone을 가리키는 포인터
    • idx 0: ZONE_DMA
    • idx 1: ZONE_NORMAL
    • idx 2: ZONE_HIGHMEM
    • idx 3: NULL
  • struct zone
    • free_pages : zone안에 있는 free 페이지 프레임 갯수
    • pages_min : pages_min에 도달하면 kswapd가 깨어난다
    • pages_low : 여기에 도달하면 할당자는 동기 방식으로 kswapd 작업을 한다
    • pages_high : kswaped가 깨어나면 pages_high개의 페이지 프레임이 free될때까지 잠들지 않는다
    • 세부 내용은 kswapd에서 다시 다룬다

간단한 메모리 구조

  • 가상주소 : 0xC0000000 ~ 0xF8000000
    • 물리 메모리의 0~896MB에 직접 매핑된다
  • 가상주소 : 0xF8000000 ~ 0xFFFFFFFF
    • 896MB 이후의 물리 메모리에 커널 페이지 테이블을 거쳐서 간접 매핑
    • vmalloc area, kmap area가 여기에 포함

Data Structures for Handling Physical Memory

  • NUMA (Non-Uniform Memory Access)
    • 접근 시간이 다른 메모리가 존재하는 경우
      • 예: 일부 아키텍쳐는 2-level bus(primary bus, secondary bod), 어떤 버스에 연결된 메모리에 접근하냐에 따라 access time이 달라진다.
    • 메모리를 Node로 나눈다
    • 각각의 노드는 같은 접근 시간
    • include/linux/mmzone.h : struct pg_data_t is node
    • 일반 PC의 경우 1 node == 1 memory unit
  • Memory Zones
    • 각각의 노드는 몇개의 Zone을 가진다
    • 메모리 사용 용도에 따라 다른 존
    • 이전에 나온 Zone(dma, normal, highmem)
    • include/linux/mmzone.h : struct zone
  • Page Frames
    • 물리 페이지 프레임
    • include/linux/mm.h : struct page

Page Frame Management

  • 페이지 프레임의 자료구조 : struct page 디스크립터
    • 커널은 각각의 물리 페이지 프레임의 현재 상태, 정보를 추적해야한다.
    • 자료구조의 목표 : 물리 메모리 표현 (해당 메모리 안의 데이터는 중요하지 않다)
    • 커널은 페이지 프레임이 free인지 알아야한다. 이를 위해서는 커널은 모든 페이지 프레임을 추적한다. 만약 물리 페이지를 free가 아니라면 커널은 페이지 프레임의 소유자를 알아야한다. (유저 프로세스, 동적 할당된 커널 데이터, 커널 코드, 페이지 캐시…)
  • 모든 페이지 디스크립터는 mem_map 배열에 저장된다
    • 리눅스는 전역변수 mem_map에 관리한다. 한개의 항목은 각각의 현재 상태를 추적하고 있는 물리 페이지를 의미한다.
  • 커널 가상 주소 변환하기
    • ZONE_DMA와 ZONE_NORMAL은 직접 매핑된다.
    • 모든 페이지 프레임은 mem_map 배열로 표현된다
    • 커널 가상 메모리 -> 물리 주소
    • 물리 주소 -> struct page
      • 물리 주소를 mem_map 배열 에서의 인덱스로 사용

Kernel Memory Allocations (KMA) in Linux

  • 연속된 페이지 프레임 할당 : alloc_pages()

    • 요청한 갯수의 연속된 자유 페이지를 할당
      • 2**n개. 2, 4, 8, 16, …
    • zoned page frame allocator
    • zone 할당자 + 버디 시스템
  • 불연속적인 페이지 프레임 할당 : vmalloc()

    • 임의프레임을 할당하고 커널 선형주소에 매핑
  • 메모리 객체 할당 : slab 할당자

    • 자주 할당되고 해제되는 커널 객체용 할당자
      • inode, task_struct, …
    • 작은 크기의 할당 (byte단위)
    • 커널 객체용 연속적인 할당 : kmem_cache_alloc()
    • 128KB 이하의 임의의 연속적인 할당 : kmalloc()

Zoned Page Frame Allocator

  • 연속된 물리 페이지 프레임을 할당
    • 특별한 경우에 연속된 가상 주소가 아닌 연속도니 물리 페이지 프레임이 필요하다
    • 예: DMA 프로세서에게 할당된 메모리 버퍼. DMA는 페이지 회로를 무시한다
  • Zone 할당자
    • 커널 페이지 프레임 할당자의 front-end
    • 메모리 요청을 만족시키는 충분히 큰 자유 페이지 프레임을 가지고 있는 메모리 영역을 찾는다.
  • Zone안에서의 페이지 프레임 할당
    • 버디 시스템
      • Zone안에서 페이지 프레임을 다룬다
      • 물리메모리를 2**n 페이지 경계로 정렬된 2**n 페이지 크기 블럭의 집합으로 취급한다.
      • alloc_pages(), free_pages()
    • CPU별 페이지 프레임 캐시
      • 낱개 페이지 요청에 사용하는 미리 할당된 페이지 프레임을 갖고있다
      • 시스템 성능 향상
  • 페이지 프레임 할당 함수
    • alloc_pages() : 2**n개의 연속된 페이지 프레임을 할당
    • alloc_page() : 1개의 페이지 프레임 할당
    • get_zeroed_page() : 0으로 채워진 1개의 페이지 할당
    • free_pages() : addr부터 시작하는 2**n개 페이지 프레임 해제
    • free_page() : 페이지 프레임 1개 해제

Buddy System

  • 주어진 크기의 블럭을 할당
    • 원하는 크기의 블럭을 찾으면 즉시 할당
    • 원하는 크기보다 큰 블럭을 사용해야하면 큰 블럭을 2개의 작은 블럭을 쪼갠다. 쪼개진 2개중에서 upper half를 자유 목록에 넣고 lower half에서 메모리 할당을 한다. 이 작업을 재귀적으로 수행
  • 블럭을 해제할때
    • 블럭이 자유 자유 버디 블럭을 갖고 있으면 2개를 붙여서 큰 블럭 하나로 만든다. 필요하다면 이 과정을 재귀적으로 수행
  • 모든 자유 페이지는 11개의 목록에 보관된다. 1, 2, 4, 8, 16, 32, …. 1024. 각각의 갯수는 할당 가능한 연속 페이지의 수이다.

Slab Allocator

  • 커널 메모리 객체 관리
    • 커널 객체는 자주 할당, 해제된다.
      • ex : file descriptor, socket, inode, …
    • 작은 크기의 메모리 요청에 어떻게 대응하는가? (수십~수백 byte)
  • 리눅스 Slab 할당자 이용
    • 커널 객체 타입용 캐시
    • 각각의 타입마다 이전에 할당/해제된 객체를 위한 메모리 캐시를 관리
    • 페이지 프레임 할당자와 연결됨
    • mm/slab.c
  • Slab 상세
    • 개발 : 솔라리스 2.4, 1994, 썬
    • 커널 함수는 같은 타입의 메모리를 자주 요청하는 경향이 있다.
    • slab할당자는 이전에 할당된뒤 해제된 객체를 버리지 않고 메모리에 저장해놓는다
    • 메모리를 특정 객체용 캐시로 사용
    • 리눅스는 버디 시스템 위에서 슬랩 할당자가 돌아간다
  • 슬랩 할당자는 객체를 캐시로 그룹짓는다
    • 각각의 캐시에는 같은 타입의 할당된/해제된 객체가 저장된다.
    • 2종류의 캐시 : 특정한 캐시, 범용 캐시
  • 캐시는 여러개의 슬랩으로 나뉘어진다.
    • 각각의 슬랩은 1개 이상의 연속된 페이지 프레임으로 구성됨. 안에는 할당된, 해제된 객체가 존재
    • 슬랩은 3가지 상태중 하나
      • full : 빈 객체가 없다
      • partial : 할당된 객체와 할당되지 않은 객체가 슬랩에 있음
      • empty : 슬랩안에 할당된 객체가 없다
    • 버디 시스템에서 할당받음

Specific Cache

  • mm_struct와 같은 커널 자료구조용으로만 사용가능 캐시
  • 1개의 캐시 == 1개의 객체 타입

General Cache

  • 2**n 크기의 범용 캐시
  • 32, 64, ….65536, 131072 byte
  • 할당 : kmalloc()
    • 커널에서 메모리 할당받는 일반적인 방법
  • 해제 : kfree()
  • /proc/slabinfo

Allocating a Slab to a Cache

  • 새로 생성된 캐시는 슬랩을 가지고 있지 않다. 따라서 빈 객체가 없다.
  • 두 조건을 만족하면 새로운 슬랩을 할당한다.
    • 새로운 객체 요청이 발생
    • 캐시안에 빈 객체가 없다
  • cache_grow() 호출해서 새로운 슬랩 할당

Releasing a Slab from a Cache

  • 슬랩 할당자는 빈 슬랩의 페이지 프레임을 해제하지 않는다.
  • 두 조건이 만족될때만 슬랩을 해제
    • 버디 시스템이 새로운 페이지 프레임 요청을 처리할수 없을때
      • 버디시스템이 요청을 처리하려고 슬랩의 페이지 프레임 회수
    • 슬랩이 비어있다 == 슬랩안의 객체를 쓰고있지 않다
  • slab_destroy() 호출

More on kmalloc()

  • kmalloc은 물리적 이유 때문에 사실상 가상적으로 연속인 메모리를 반환
    • 유저 영역 함수인 malloc()의 경우 가상적으로 연속이지만 물리적으로는 연속이 아닐수 있는 메모리를 반환한다.
  • 물리적으로 연속인 메모리의 장점 (2)
    1. 많은 하드웨어 장치는 가상 메물모리를 어드레싱 하지 못한다. 그러므로 장치가 메모리 블럭에 접근하려면 블럭은 물리적으로 연속인 메모리 덩어리여야한다.
    2. 물리적으로 연속인 메모리 블럭은 거대한 단일 페이지 매핑에 사용될 수 있다. 하나의 TLB(translation lookup buffer) 엔트리만 사용해서 TLB가 메모리 어드레싱하는 오버헤드를 줄일수 있다. 페이지 참조 오버헤드 감소
  • 물리적으로 연속인 메모리의 단점 (1)
    1. 대용량 할당시 물리적으로 연속인 블럭을 찾기 어려울수도 있다. 물리적으로 연속이 필요가 없으면 vmalloc()로 대신할 수 있다.

Noncontiguous Frame Allocator : vmalloc()

배경

  • 물리적으로 연속인 매핑은 커널한테 좋지만 항상 성공적으로 사용될수 없다.
  • 커널은 불연속적인 물리 메모리 주소를 될수있는한 피하려고 한다.
  • 거대한 덩어리의 메모리가 할당되면 커널이 물리 메모리를 필요로 하는데 연속적인 공간이 없는 상황이 발생할수 있다. (예를 들어 시스템이 오래 작동한 경우, 파편화때문에)
  • 그러므로 커널은 연속적으로 매핑할수 있는 가상주소공간 덩어리를 예약한다.
  • 이는 해당 영역에 대한 커널 페이지 테이블의 책임을 수정해서 달성

내용

  • 연속적인 선형 주소에 대한 불연속적인 페이지 프레임

    • 선형 커널 주소 공간에 매핑되는 임의의(불연속적인) 프레임을 할당
  • 리눅스는 다음같은 상황에서 불연속 메모리를 사용한다

    • 활성화 스왑 area용 자료구조를 할당
    • 모듈을 위한 공간을 할당
    • 일부 IO 드라이버를 위한 버퍼 할당
    • 급하게 필요한 메모리가 아닌것, 우선순위 낮은 것
  • 연속적인 할당 시도하지만 실패시 불연속적인 메모리 사용

  • 불연속적인 메모리 할당

    • mm/vmallo.c : vmalloc()
    • 4096으로 정렬된 메모리 할당
    • get_vm_area() : 새로운 vm_struct 디스크립터 할당
    • map_vm_area() : 페이지 프레임 덩어리를 선형 주소에 매핑
  • 불연속적인 메모리 해제

    • vfree()
      • remove_vm_area() : 메모리 해제
      • kfree() : vm_struct 디스크립터 해제

Allocating Kernel Memory Summary

  • vmalloc address space

    • 불연속적인 물리 메모리 할당
  • kmap address space

    • ZONE_HIGHMEM 에서 메모리 할당
  • Fixed mapping

    • 컴파일-타임 가상 메모리 할당
  • alloc_pages()

    • Zone 페이지 프레임 할당자의 기본 인터페이스
  • kmalloc()

    • 슬랩 할당자(페이지 프레임 할당자를 써서 커널의 물리적으로 매핑된 가상 페이지 덩어리를 얻음). 작은 메모리 할당
  • kmem_cache_alloc()

    • 특정한 객체 할당하려고 슬랩 할당자 사용
  • vmalloc()

    • 임의의 물리 페이지를 할당하고 연속된 커널 가상 메모리로 매핑

comments powered by Disqus