요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.
1. 요약·해설
원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.
2. 영어 원문 전체
번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.
원문 전체 펼치기
=================
Concepts overview
=================
The memory management in Linux is a complex system that evolved over the
years and included more and more functionality to support a variety of
systems from MMU-less microcontrollers to supercomputers. The memory
management for systems without an MMU is called ``nommu`` and it
definitely deserves a dedicated document, which hopefully will be
eventually written. Yet, although some of the concepts are the same,
here we assume that an MMU is available and a CPU can translate a virtual
address to a physical address.
.. contents:: :local:
Virtual Memory Primer
=====================
The physical memory in a computer system is a limited resource and
even for systems that support memory hotplug there is a hard limit on
the amount of memory that can be installed. The physical memory is not
necessarily contiguous; it might be accessible as a set of distinct
address ranges. Besides, different CPU architectures, and even
different implementations of the same architecture have different views
of how these address ranges are defined.
All this makes dealing directly with physical memory quite complex and
to avoid this complexity a concept of virtual memory was developed.
The virtual memory abstracts the details of physical memory from the
application software, allows to keep only needed information in the
physical memory (demand paging) and provides a mechanism for the
protection and controlled sharing of data between processes.
With virtual memory, each and every memory access uses a virtual
address. When the CPU decodes an instruction that reads (or
writes) from (or to) the system memory, it translates the `virtual`
address encoded in that instruction to a `physical` address that the
memory controller can understand.
The physical system memory is divided into page frames, or pages. The
size of each page is architecture specific. Some architectures allow
selection of the page size from several supported values; this
selection is performed at the kernel build time by setting an
appropriate kernel configuration option.
Each physical memory page can be mapped as one or more virtual
pages. These mappings are described by page tables that allow
translation from a virtual address used by programs to the physical
memory address. The page tables are organized hierarchically.
The tables at the lowest level of the hierarchy contain physical
addresses of actual pages used by the software. The tables at higher
levels contain physical addresses of the pages belonging to the lower
levels. The pointer to the top level page table resides in a
register. When the CPU performs the address translation, it uses this
register to access the top level page table. The high bits of the
virtual address are used to index an entry in the top level page
table. That entry is then used to access the next level in the
hierarchy with the next bits of the virtual address as the index to
that level page table. The lowest bits in the virtual address define
the offset inside the actual page.
Huge Pages
==========
The address translation requires several memory accesses and memory
accesses are slow relatively to CPU speed. To avoid spending precious
processor cycles on the address translation, CPUs maintain a cache of
such translations called Translation Lookaside Buffer (or
TLB). Usually TLB is pretty scarce resource and applications with
large memory working set will experience performance hit because of
TLB misses.
Many modern CPU architectures allow mapping of the memory pages
directly by the higher levels in the page table. For instance, on x86,
it is possible to map 2M and even 1G pages using entries in the second
and the third level page tables. In Linux such pages are called
`huge`. Usage of huge pages significantly reduces pressure on TLB,
improves TLB hit-rate and thus improves overall system performance.
There are two mechanisms in Linux that enable mapping of the physical
memory with the huge pages. The first one is `HugeTLB filesystem`, or
hugetlbfs. It is a pseudo filesystem that uses RAM as its backing
store. For the files created in this filesystem the data resides in
the memory and mapped using huge pages. The hugetlbfs is described at
Documentation/admin-guide/mm/hugetlbpage.rst.
Another, more recent, mechanism that enables use of the huge pages is
called `Transparent HugePages`, or THP. Unlike the hugetlbfs that
requires users and/or system administrators to configure what parts of
the system memory should and can be mapped by the huge pages, THP
manages such mappings transparently to the user and hence the
name. See Documentation/admin-guide/mm/transhuge.rst for more details
about THP.
Zones
=====
Often hardware poses restrictions on how different physical memory
ranges can be accessed. In some cases, devices cannot perform DMA to
all the addressable memory. In other cases, the size of the physical
memory exceeds the maximal addressable size of virtual memory and
special actions are required to access portions of the memory. Linux
groups memory pages into `zones` according to their possible
usage. For example, ZONE_DMA will contain memory that can be used by
devices for DMA, ZONE_HIGHMEM will contain memory that is not
permanently mapped into kernel's address space and ZONE_NORMAL will
contain normally addressed pages.
The actual layout of the memory zones is hardware dependent as not all
architectures define all zones, and requirements for DMA are different
for different platforms.
Nodes
=====
Many multi-processor machines are NUMA - Non-Uniform Memory Access -
systems. In such systems the memory is arranged into banks that have
different access latency depending on the "distance" from the
processor. Each bank is referred to as a `node` and for each node Linux
constructs an independent memory management subsystem. A node has its
own set of zones, lists of free and used pages and various statistics
counters. You can find more details about NUMA in
Documentation/mm/numa.rst` and in
Documentation/admin-guide/mm/numa_memory_policy.rst.
Page cache
==========
The physical memory is volatile and the common case for getting data
into the memory is to read it from files. Whenever a file is read, the
data is put into the `page cache` to avoid expensive disk access on
the subsequent reads. Similarly, when one writes to a file, the data
is placed in the page cache and eventually gets into the backing
storage device. The written pages are marked as `dirty` and when Linux
decides to reuse them for other purposes, it makes sure to synchronize
the file contents on the device with the updated data.
Anonymous Memory
================
The `anonymous memory` or `anonymous mappings` represent memory that
is not backed by a filesystem. Such mappings are implicitly created
for program's stack and heap or by explicit calls to mmap(2) system
call. Usually, the anonymous mappings only define virtual memory areas
that the program is allowed to access. The read accesses will result
in creation of a page table entry that references a special physical
page filled with zeroes. When the program performs a write, a regular
physical page will be allocated to hold the written data. The page
will be marked dirty and if the kernel decides to repurpose it,
the dirty page will be swapped out.
Reclaim
=======
Throughout the system lifetime, a physical page can be used for storing
different types of data. It can be kernel internal data structures,
DMA'able buffers for device drivers use, data read from a filesystem,
memory allocated by user space processes etc.
Depending on the page usage it is treated differently by the Linux
memory management. The pages that can be freed at any time, either
because they cache the data available elsewhere, for instance, on a
hard disk, or because they can be swapped out, again, to the hard
disk, are called `reclaimable`. The most notable categories of the
reclaimable pages are page cache and anonymous memory.
In most cases, the pages holding internal kernel data and used as DMA
buffers cannot be repurposed, and they remain pinned until freed by
their user. Such pages are called `unreclaimable`. However, in certain
circumstances, even pages occupied with kernel data structures can be
reclaimed. For instance, in-memory caches of filesystem metadata can
be re-read from the storage device and therefore it is possible to
discard them from the main memory when system is under memory
pressure.
The process of freeing the reclaimable physical memory pages and
repurposing them is called (surprise!) `reclaim`. Linux can reclaim
pages either asynchronously or synchronously, depending on the state
of the system. When the system is not loaded, most of the memory is free
and allocation requests will be satisfied immediately from the free
pages supply. As the load increases, the amount of the free pages goes
down and when it reaches a certain threshold (low watermark), an
allocation request will awaken the ``kswapd`` daemon. It will
asynchronously scan memory pages and either just free them if the data
they contain is available elsewhere, or evict to the backing storage
device (remember those dirty pages?). As memory usage increases even
more and reaches another threshold - min watermark - an allocation
will trigger `direct reclaim`. In this case allocation is stalled
until enough memory pages are reclaimed to satisfy the request.
Compaction
==========
As the system runs, tasks allocate and free the memory and it becomes
fragmented. Although with virtual memory it is possible to present
scattered physical pages as virtually contiguous range, sometimes it is
necessary to allocate large physically contiguous memory areas. Such
need may arise, for instance, when a device driver requires a large
buffer for DMA, or when THP allocates a huge page. Memory `compaction`
addresses the fragmentation issue. This mechanism moves occupied pages
from the lower part of a memory zone to free pages in the upper part
of the zone. When a compaction scan is finished free pages are grouped
together at the beginning of the zone and allocations of large
physically contiguous areas become possible.
Like reclaim, the compaction may happen asynchronously in the ``kcompactd``
daemon or synchronously as a result of a memory allocation request.
OOM killer
==========
It is possible that on a loaded machine memory will be exhausted and the
kernel will be unable to reclaim enough memory to continue to operate. In
order to save the rest of the system, it invokes the `OOM killer`.
The `OOM killer` selects a task to sacrifice for the sake of the overall
system health. The selected task is killed in a hope that after it exits
enough memory will be freed to continue normal operation.
3. 한국어 전문 번역
영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.
Linux memory management 개요
1-15Linux memory management는 MMU가 없는 microcontroller부터 supercomputer까지 지원하도록 오랜 시간 확장된 복잡한 system입니다. MMU가 없는 시스템의 `nommu` memory management는 별도 문서가 필요하지만, 여기서는 MMU가 있고 CPU가 virtual address를 physical address로 변환할 수 있다고 가정합니다.
원문의 `contents:: :local:` 지시자는 이 문서 안의 절을 대상으로 로컬 목차를 만듭니다.
Virtual Memory Primer
16-63physical memory는 한정된 자원이며 memory hotplug를 지원해도 설치 가능한 양에는 상한이 있습니다. 반드시 contiguous하지 않고 여러 address range로 나뉠 수 있으며, range 정의 방식도 CPU architecture와 구현마다 다릅니다.
virtual memory는 이런 physical memory 세부 사항을 application에서 숨깁니다. 필요한 정보만 physical memory에 두는 demand paging을 가능하게 하고 process 사이의 데이터 보호와 제어된 공유를 제공합니다.
CPU가 system memory를 읽거나 쓰는 instruction을 해석할 때 instruction의 `virtual` address를 memory controller가 이해하는 `physical` address로 변환합니다.
physical memory는 architecture별 크기의 page frame 또는 page로 나뉩니다. 여러 page size를 지원하는 architecture에서는 kernel build 때 configuration option으로 크기를 선택합니다.
physical page 하나는 하나 이상의 virtual page에 mapping될 수 있습니다. 계층형 page table이 program의 virtual address를 physical address로 변환하는 mapping을 설명합니다.
최하위 table entry는 실제 page의 physical address를 담고 상위 table은 하위 table page의 physical address를 담습니다. CPU는 register의 top-level table pointer에서 시작해 virtual address의 높은 bit부터 각 level index로 사용하고, 가장 낮은 bit를 실제 page 내부 offset으로 사용합니다.
Huge Pages
64-96주소 변환은 여러 번의 느린 memory access가 필요하므로 CPU는 Translation Lookaside Buffer(TLB)에 변환 결과를 cache합니다. TLB는 제한적이어서 큰 working set을 가진 application은 TLB miss로 성능이 떨어질 수 있습니다.
현대 CPU는 상위 page-table level에서 memory page를 직접 mapping할 수 있습니다. x86의 second/third-level entry로 2M 또는 1G page를 mapping하는 것이 예이며 Linux에서는 이를 `huge` page라 부릅니다. huge page는 TLB pressure를 줄이고 hit rate와 전체 성능을 높입니다.
Linux의 첫 mechanism은 RAM을 backing store로 쓰는 pseudo filesystem `HugeTLB filesystem` 또는 hugetlbfs입니다. 여기에 만든 file 데이터는 memory에 있고 huge page로 mapping됩니다. 자세한 내용은 `Documentation/admin-guide/mm/hugetlbpage.rst`에 있습니다.
더 최근의 `Transparent HugePages`(THP)는 관리자가 huge-page 대상 memory를 미리 구성해야 하는 hugetlbfs와 달리 mapping을 투명하게 관리합니다. 자세한 내용은 `Documentation/admin-guide/mm/transhuge.rst`를 참고합니다.
Zones
97-114hardware는 physical memory range 접근에 제약을 둘 수 있습니다. 어떤 device는 모든 addressable memory에 DMA를 할 수 없고, physical memory가 virtual address 가능 범위를 넘어 특별한 접근 절차가 필요하기도 합니다.
Linux는 가능한 용도에 따라 page를 `zone`으로 묶습니다. ZONE_DMA는 device DMA에 쓸 memory, ZONE_HIGHMEM은 kernel address space에 영구 mapping되지 않은 memory, ZONE_NORMAL은 일반적으로 address할 수 있는 page를 담습니다. 실제 zone layout은 architecture와 platform의 DMA 요구에 따라 달라집니다.
Nodes
115-127많은 multiprocessor machine은 NUMA(Non-Uniform Memory Access) system입니다. memory bank마다 processor와의 `distance`에 따른 access latency가 다르며 각 bank를 `node`라고 합니다.
Linux는 node마다 독립적인 memory management subsystem을 만들고 각 node는 고유한 zone 집합, free/used page list, statistics counter를 가집니다. 자세한 내용은 `Documentation/mm/numa.rst`와 `Documentation/admin-guide/mm/numa_memory_policy.rst`에 있습니다.
Page cache
128-139physical memory는 volatile이므로 보통 file에서 데이터를 읽어옵니다. file read 데이터는 다음 접근의 비싼 disk I/O를 피하려고 `page cache`에 둡니다. write 데이터도 먼저 page cache에 들어간 뒤 backing storage로 전달됩니다.
write된 page는 `dirty`로 표시됩니다. Linux가 이를 다른 용도로 재사용하려 할 때 device의 file 내용과 갱신된 데이터를 먼저 동기화합니다.
Anonymous Memory
140-153`anonymous memory` 또는 `anonymous mapping`은 filesystem이 backing하지 않는 memory입니다. program stack과 heap에 암묵적으로 만들어지거나 mmap(2) system call로 명시적으로 생성됩니다.
처음에는 program이 접근할 수 있는 virtual memory area만 정의됩니다. read하면 zero로 채운 특수 physical page를 가리키는 page-table entry가 생기고 write하면 실제 데이터를 보관할 일반 physical page가 할당됩니다. 이 page는 dirty로 표시되며 kernel이 재사용하려 하면 swap out됩니다.
Reclaim
154-192system 수명 동안 physical page는 kernel 내부 구조, device driver의 DMA buffer, filesystem data, userspace allocation 등 여러 용도로 바뀌어 사용됩니다.
다른 곳에서 다시 읽거나 disk로 swap out할 수 있어 언제든 free 가능한 page를 `reclaimable`이라 합니다. 대표적으로 page cache와 anonymous memory가 있습니다.
kernel 내부 데이터나 DMA buffer는 보통 사용자가 해제할 때까지 pinned되어 `unreclaimable`입니다. 다만 storage에서 다시 읽을 수 있는 filesystem metadata cache 같은 일부 kernel 구조는 memory pressure에서 버릴 수 있습니다.
reclaimable physical page를 free해 다른 용도로 바꾸는 과정을 `reclaim`이라 합니다. system이 한가할 때 allocation은 free page에서 즉시 충족됩니다. free page가 low watermark에 이르면 allocation이 `kswapd`를 깨우고, daemon은 비동기로 page를 scan해 즉시 free하거나 dirty 데이터를 backing storage로 evict합니다.
memory 사용량이 더 올라 min watermark에 이르면 allocation이 `direct reclaim`을 시작합니다. 이때 요청은 필요한 page를 충분히 회수할 때까지 멈춥니다.
Compaction
193-210task가 memory를 할당하고 해제하면서 physical memory는 fragmentation됩니다. virtual memory는 흩어진 page를 virtually contiguous하게 보일 수 있지만 DMA의 큰 buffer나 THP huge page에는 큰 physically contiguous 영역이 필요합니다.
`compaction`은 memory zone 아래쪽의 사용 중 page를 위쪽 free page로 옮깁니다. scan이 끝나면 free page가 zone 시작 부분에 모여 큰 contiguous allocation이 가능해집니다.
reclaim과 마찬가지로 `kcompactd` daemon에서 비동기로 실행하거나 memory allocation 요청 때문에 동기로 실행할 수 있습니다.
OOM killer
211-220부하가 큰 machine에서 memory가 고갈되고 kernel이 동작을 계속할 만큼 reclaim하지 못할 수 있습니다. 나머지 system을 살리기 위해 `OOM killer`를 호출합니다.
`OOM killer`는 전체 system health를 위해 희생할 task를 선택해 종료합니다. 그 task가 빠져나간 뒤 정상 동작을 계속할 만큼 memory가 해제되기를 기대합니다.
Memory management 개념 지도
concepts.rst:1-220주소 변환과 page 계층에서 시작해 memory 배치, file·anonymous data의 수명, pressure 대응과 최종 OOM 복구까지 이어지는 관계를 한 문서에서 설명합니다.