38
MEMORY MANAGEMENT WALK-THROUGH Shiguang Wang March 3rd, 2014

MEMORY MANAGEMENT WALK-THROUGH

  • Upload
    others

  • View
    1

  • Download
    0

Embed Size (px)

Citation preview

Page 1: MEMORY MANAGEMENT WALK-THROUGH

MEMORY MANAGEMENT WALK-THROUGH

Shiguang WangMarch 3rd, 2014

Page 2: MEMORY MANAGEMENT WALK-THROUGH

LAST TIME

Process Management

Process Scheduling

Page 3: MEMORY MANAGEMENT WALK-THROUGH

TODAY’S SESSION

Memory Management

Page 4: MEMORY MANAGEMENT WALK-THROUGH

KERNEL SIZE AS AN INDICATOR OF COMPLEXITY

CHAPTER 1

Introduction

Linux is a relatively new operating system that has begun to enjoy a lot of attentionfrom the business, academic and free software worlds. As the operating systemmatures, its feature set, capabilities and performance grow, but so, out of necessitydoes its size and complexity. Table 1.1 shows the size of the kernel source code inbytes and lines of code of the mm/ part of the kernel tree. This size does not includethe machine-dependent code or any of the buffer management code and does noteven pretend to be an accurate metric for complexity, but it still serves as a smallindicator.

Version Release Date Total Size Size of mm/ Line Count1.0 March 13, 1992 5.9MiB 96KiB 3,1091.2.13 February 8, 1995 11MiB 136KiB 4,5312.0.39 January 9, 2001 35MiB 204KiB 6,7922.2.22 September 16, 2002 93MiB 292KiB 9,5542.4.22 August 25, 2003 181MiB 436KiB 15,7242.6.0-test4 August 22, 2003 261MiB 604KiB 21,714

Table 1.1. Kernel Size as an Indicator of Complexity

Out of habit, open source developers tell new developers with questions to referdirectly to the source with the “polite” acronym RTFS1, or refer them to the kernelnewbies mailing list (http://www.kernelnewbies.org). With the Linux VM manager,this used to be a suitable response because the time required to understand the VMcould be measured in weeks. Moreover, the books available devoted enough timeto the memory management chapters to make the relatively small amount of codeeasy to navigate.

The books that describe the operating system such as Understanding the LinuxKernel [BC00] [BC03] tend to cover the entire kernel rather than one topic with thenotable exception of device drivers [RC01]. These books, particularly Understandingthe Linux Kernel, provide invaluable insight into kernel internals, but they miss thedetails that are specific to the VM and not of general interest. But the book you areholding details why ZONE NORMAL is exactly 896MiB and exactly how per-cpu caches

1Read The Flaming Source. It doesn’t really stand for Flaming, but children could be reading.

1

Page 5: MEMORY MANAGEMENT WALK-THROUGH

PAGES

Physical pages as basic unit of memory management

Processor’s smallest addressable unit is a word

MMU typically deals in pages

The hardware that manages memory and performs virtual to physical address translations

32-bit arch 4KB pages, 64-bit arch 8KB pages

1GB physical memory on a 32-bit machine, 262,114 distinct pages

Page 6: MEMORY MANAGEMENT WALK-THROUGH

PAGE STRUCTURE

struct page, the kernel representation of a physical page

Defined in <linux/mm.h>

struct page { page_flags_t flags; atomic_t _count; atomic_t _mapcount; unsigned long private; struct address_space *mapping; pgoff_t index; struct list_head lru; void *virtual;};

Page 7: MEMORY MANAGEMENT WALK-THROUGH

PAGE STRUCTURE

flags: status of the page (e.g. dirty or locked)

bit flag, at least 32 simultaneous flags

flag values are defined in <linux/page-flags.h>

struct page { page_flags_t flags; atomic_t _count; atomic_t _mapcount; unsigned long private; struct address_space *mapping; pgoff_t index; struct list_head lru; void *virtual;};

Page 8: MEMORY MANAGEMENT WALK-THROUGH

PAGE STRUCTURE

_count: usage count of the page

“How many references to this page”

Check this field by page_count()

Zero means the page is free, positive integer means it is in use.

struct page { page_flags_t flags; atomic_t _count; atomic_t _mapcount; unsigned long private; struct address_space *mapping; pgoff_t index; struct list_head lru; void *virtual;};

Page 9: MEMORY MANAGEMENT WALK-THROUGH

PAGE STRUCTURE

virtual: the page’s virtual address

page struct is associated with physical pages, not virtual pages

Transient, because of swapping

Its goal is to describe physical memory, not the data contained therein.

struct page { page_flags_t flags; atomic_t _count; atomic_t _mapcount; unsigned long private; struct address_space *mapping; pgoff_t index; struct list_head lru; void *virtual;};

Page 10: MEMORY MANAGEMENT WALK-THROUGH

PAGE STRUCTURE COST ANALYSIS

“What a lot of memory used!”

Assume struct page consumes 40 bytes of memory

the system has 4KB physical pages,

the system has 128MB physical memory

All the page structures in the system consume slightly more than 1MB of memory

Not too high a cost for managing all the physical pages

Page 11: MEMORY MANAGEMENT WALK-THROUGH

ZONES

Due to hardware limitations, the kernel cannot treat all pages as identical

Two shortcomings of hardware w.r.t. memory addressing:

Some hardware are capable to performing DMA to only certain memory addresses.

Some architectures are capable of physically addressing larger amounts of memory than they can virtually address.

Some memory is not permanently mapped into the kernel address space

Page 12: MEMORY MANAGEMENT WALK-THROUGH

THREE MEMORY ZONESZONE_DMA: This zone contains pages that are capable of undergoing DMA.

ZONE_NORMAL: This zone contains normal, regular mapped, pages.

ZONE_HIGHMEM: This zone contains “high memory”, which are pages not permanently mapped into the kernel’s address space

These zones are defined in <linux/mmzone.h>

Page 13: MEMORY MANAGEMENT WALK-THROUGH

THREE MEMORY ZONES

The actual use and layout of the memory zones is architecture independent.

Zones on x86

Zone Description Physical Memory

ZONE_DMA DMA-able pages <16MB

ZONE_NORMALNormally

addressable pages 16~896MB

ZONE_HIGHDynamically mapped

pages >896MB

Page 14: MEMORY MANAGEMENT WALK-THROUGH

ZONE STRUCTURE

Each zone is represented by struct zone defined in <linux/mmzone.h>struct zone { spinlock_t lock; unsigned long free_pages; unsigned long pages_min; unsigned long pages_low; unsigned long pages_high; unsigned long protection[MAX_NR_ZONES]; spinlock_t lru_lock; struct list_head active_list; struct list_head inactive_list; unsigned long nr_scan_active; unsigned long nr_scan_inactive; unsigned long nr_active; unsigned long nr_inactive; int all_unreclaimable; unsigned long pages_scanned; int temp_priority; int prev_priority; struct free_area free_area[MAX_ORDER]; wait_queue_head_t *wait_table; unsigned long wait_table_size; unsigned long wait_table_bits; struct per_cpu_pageset pageset[NR_CPUS]; struct pglist_data *zone_pgdat; struct page *zone_mem_map; unsigned long zone_start_pfn; char *name; unsigned long spanned_pages; unsigned long present_pages;};

Page 15: MEMORY MANAGEMENT WALK-THROUGH

ZONE STRUCTURE

lock: protects only the structure not all the pages that reside in the zone

free_pages: the number of free pages in this zone. The kernel tries to keep at least page_min pages free (through swapping), if possible.

name: a NULL-terminated string. Three zones are given the names “DMA”, “Normal”, and “HighMem”.

struct zone { spinlock_t lock; unsigned long free_pages; unsigned long pages_min;

/*...*/

char *name;

/*...*/};

Page 16: MEMORY MANAGEMENT WALK-THROUGH

GETTING PAGES

All interfaces allocate memory with page-sized granularity are declared in <linux/gfp.h>

struct page * alloc_pages(unsigned int gfp_mask, unsigned int order)

Allocates 2^order contiguous physical pages and returns a pointer to the first page’s page structure; on error it returns NULL.

unsigned long __get_free_pages(unsigned int gfp_mask, unsigned int order)

struct page * alloc_page(unsigned int gfp_mask)

Same as alloc_pages with order 0

unsigned long __get_free_page(unsigned int gfp_mask)

unsigned long get_zeroed_page(unsigned int gfp_mask)

Page 17: MEMORY MANAGEMENT WALK-THROUGH

FREEING PAGES

void __free_pages(struct page *page, unsigned int order)

void free_pages(unsigned long addr, unsigned int order)

void free_page(unsigned long addr)

You must be careful to free only pages you allocate.

Passing the wrong struct page or address, or the incorrest order, can result in corruption.

Page 18: MEMORY MANAGEMENT WALK-THROUGH

EXAMPLE

unsigned long page;

page = __get_free_pages(GFP_KERNEL, 3);if (!page) { /* insufficient memory: you must handle this error! */ return ENOMEM;}

/* 'page' is now the address of the first of eight contiguous pages ... */

free_pages(page, 3);

/* * our pages are now freed and we should no * longer access the address stored in 'page' */

Page 19: MEMORY MANAGEMENT WALK-THROUGH

“Allocate your memory at the start of the routine, to make

handling the error easier.”

Page 20: MEMORY MANAGEMENT WALK-THROUGH

kmalloc()

Very similar to user-space’s malloc() routine

Interface for obtaining kernel memory in byte-sized chunks

If you need whole pages, use the previously discussed interfaces

For most kernel allocations, kmalloc() is the preferred interface.

Declared in <linux/slab.h>

void * kmalloc(size_t size, int flags)

Returns the pointer to physically contiguous memory of length size bytes; NULL on error.

You must check NULL after all calls to kmalloc(), and handle the error appropriately.

Page 21: MEMORY MANAGEMENT WALK-THROUGH

kfree()

The other end of kmalloc() is kfree(), declared in <linux/slab.h>

void kfree(const void *ptr)

Page 22: MEMORY MANAGEMENT WALK-THROUGH

EXAMPLE

struct dog *ptr;

ptr = kmalloc(sizeof(struct dog), GFP_KERNEL);if (!ptr) /* handle error ... */

/* ... */

/* Later, when you no longer need the memory*/kfree(ptr);

Page 23: MEMORY MANAGEMENT WALK-THROUGH

GFP_MASK FLAGS

The flags are broken up into three categories:

Action modifier

Specify how the kernel is supposed to allocate the requested memory.

For example, interrupt handlers must instruct the kernel not to sleep in the course of allocating memory

Zone modifier

Specify from where to allocate memory.

Type

Specify a combination of action and zone modifiers as needed by a certain type of memory allocation.

The GFP_KERNEL is a type flag, which is used for code in process context inside the kernel.

Page 24: MEMORY MANAGEMENT WALK-THROUGH

ACTION MODIFIERSFlag Description__GFP_WAIT The allocator can sleep.__GFP_HIGH The allocator can access emergency pools.__GFP_IO The allocator can start disk I/O.__GFP_FS The allocator can start filesystem I/O.__GFP_COLD The allocator should use cache cold pages.__GFP_NOWARN The allocator will not print failure warnings.

__GFP_REPEAT The allocator will repeat the allocation if it fails, but the allocation can potentially fail.

__GFP_NOFAIL The allocator will indefinitely repeat the allocation. The allocation cannot fail.

__GFP_NORETRY The allocator will never retry if the allocation fails.__GFP_NO_GROW Used internally by the slab layer.

__GFP_COMP Add compound page metadata. Used internally by the hugetlb code.

Page 25: MEMORY MANAGEMENT WALK-THROUGH

ZONE MODIFIERS

Flag Description

__GFP_DMA Allocate only from ZONE_DMA

__GFP_HIGHMEMAllocate from ZONE_HIGHMEM or

ZONE_NORMAL

Page 26: MEMORY MANAGEMENT WALK-THROUGH

TYPE FLAGSFlag Description

GFP_ATOMICThe allocation is high priority and must not sleep. This is the flag to use in interrupt handlers, in bottom halves, while holding a spinlock, and in other situations where you cannot sleep.

GFP_NOIOThis allocation can block, but must not initiate disk I/O. This is the flag to use in block I/O code when you cannot cause more disk I/O, which might lead to some unpleasant recursion.

GFP_NOFSThis allocation can block and can initiate disk I/O, if it must, but will not initiate a filesystem operation. This is the flag to use in filesystem code when you cannot start another filesystem operation.

GFP_KERNEL

This is a normal allocation and might block. This is the flag to use in process context code when it is safe to sleep. The kernel will do whatever it has to in order to obtain the memory requested by the caller. This flag should be your first choice.

GFP_USER This is a normal allocation and might block. This flag is used to allocate memory for user-space processes.

GFP_HIGHUSER This is an allocation from ZONE_HIGHMEM and might block. This flag is used to allocate memory for user-space processes.

GFP_DMA This is an allocation from ZOME_DMA. Device drivers that need DMA-able memory use this flag, usually in combination with one of the above.

Page 27: MEMORY MANAGEMENT WALK-THROUGH

vmalloc()

Similar as kmalloc(), except it allocates memory that is only virtually contiguous

This is how malloc() works; The pages returned are contiguous within the virtual address space of the processor, but there is no guarantee that they are actually contiguous in physical RAM.

vmalloc() is declared in <linux/vmalloc.h>

void * vmalloc(unsigned long size)

void vfree(void *addr)

Page 28: MEMORY MANAGEMENT WALK-THROUGH

“Allocating and freeing data structures is one of the most

common operations inside any kernel.”

Page 29: MEMORY MANAGEMENT WALK-THROUGH

SLAB LAYER

“free lists” -- no global control!

Slab layer acts as a generic data structure-caching layer.

Divides different objects into groups called caches.

One cache per object type

For example, for process descriptors, a free list of task_struct structures

Caches are divided into slabs, composed of one or more physically contiguous pages.

Page 30: MEMORY MANAGEMENT WALK-THROUGH

SLAB

Each slab contains some number of objects, cached data structures.

Each slab is in one of three states: full, partial, or empty.

When the kernel requests a new object,

the request is satisfied from a partial slab, if one exists

otherwise, it is satisfied from an empty slab, if one exists

otherwise, one empty slab is created

The above strategy reduces fragmentation

Page 31: MEMORY MANAGEMENT WALK-THROUGH

RELATIONSHIP BETWEEN CACHES, SLABS, AND OBJECTS

Page 32: MEMORY MANAGEMENT WALK-THROUGH

SLAB DESCRIPTOR

struct slab { struct list_head list; /* full, partial, or empty list */ unsigned long colouroff; /* offset for the slab coloring */ void *s_mem; /* first object in the slab */ unsigned int inuse; /* allocated objects in the slab */ kmem_bufctl_t free; /* first free object, if any */};

Page 33: MEMORY MANAGEMENT WALK-THROUGH

SLAB ALLOCATOR

Create a new cache:

Destroy a cache:

Obtain an object from a cache:

Free an object and return it to its originating slab:

kmem_cache_t * kmem_cache_create(const char *name, size_t size, size_t align, unsigned long flags, void (*ctor)(void*, kmem_cache_t *, unsigned long), void (*dtor)(void*, kmem_cache_t *, unsigned long))

int kmem_cache_destroy(kmem_cache_t *cachep)

void * kmem_cache_alloc(kmem_cache_t *cachep, int flags)

void kmem_cache_free(kmem_cache_t *cachep, void *objp)

Page 34: MEMORY MANAGEMENT WALK-THROUGH

EXAMPLE

Real-life example uses the task_struct structure, adapted from kernel/fork.c

First, the kernel has a global variable storing a pointer to the task_struct cache:

During kernel initialization, in fork_init(), the cache is created:

kmem_cache_t *task_struct_cachep;

task_struct_cachep = kmem_cache_create("task_struct", sizeof(struct task_struct), ARCH_MIN_TASKALIGN, SLAB_PANIC, NULL, NULL);

Page 35: MEMORY MANAGEMENT WALK-THROUGH

EXAMPLE

Each time a process calls fork(), a new process descriptor must be created (recall our last walk-through lecture)

This is done in dup_task_struct(), which is called from do_fork():

struct task_struct *tsk;

tsk = kmem_cache_alloc(task_struct_cachep, GFP_KERNEL);if (!tsk) return NULL;

Page 36: MEMORY MANAGEMENT WALK-THROUGH

EXAMPLE

After a task dies, if it has no children waiting on it, its process descriptor is freed and returned to the task_struct_cachep slab cache.

This is done in free_task_struct()

kmem_cache_free(task_struct_cachep, tsk);

Page 37: MEMORY MANAGEMENT WALK-THROUGH

EXAMPLE

The task_struct_cachep cache is never destroyed!

If it were, you would destroy the cache via:

int err;

err = kmem_cache_destroy(task_struct_cachep);if (err) /* error destroying cache */