You are on page 1of 59

Memory Management

Version 1.0

Memory Zones in Linux

The Virtual Address Space


Linux, like any modern operating system, virtualizes its physical resource of memory The kernel associates each process with a unique virtual address space which is linear, starting with zero The virtual address space is composed of pages Pages are either valid or invalid (unused) Accessing an invalid page causes a segmentation violation
3

Address Translation Logic

Paging in Linux

The Page Fault


The job of translating process virtual address to physical address is done by a hardware unit called MMU When a process tries to refer a page which is not there in the RAM, the MMU generates a page fault The kernel then intervenes, transparently paging in the desired page from secondary storage to physical memory

Sharing and Copy-On-Write


Multiple pages of virtual memory, even in different virtual address spaces owned by different processes, may map to a single physical page (Ex. Mapped memory, Hard links) If the page is marked as non-sharable, the MMU may intercept the write operation, and raise an exception

In response, will transparently create a new copy of the page for the writing process
copy-on-write occurs on a pageby-page basis
7

The Process Address Space

Allocating Dynamic Memory


#include <stdlib.h> void * malloc (size_t size); C automatically promotes pointers to void to any type on assignment, unlike C++ Thus there is no need to typecast the return value of malloc( ) in C malloc( ) can return NULL. Remember to handle the error conditions
9

Allocating Arrays
#include <stdlib.h> void * calloc (size_t nr, size_t size); Returns a pointer to a block of memory suitable for holding an array of nr elements, each of size bytes calloc( ) zeros all bytes in the returned chunk of memory

10

Resizing Allocations
#include <stdlib.h> void * realloc (void *ptr, size_t size); realloc( ) resizes the region of memory pointed at by ptr to a new size of size bytes It returns a pointer to the newly sized memory, which may or may not be the same as ptr

If size is 0, the effect is the same as an invocation of free() on ptr


If ptr is NULL, the result of the operation is the same as a fresh malloc()
11

Freeing Memory
Dynamic allocations are permanent parts of the process address space until they are manually freed Both static and dynamic allocations, disappear when the entire process exits #include <stdlib.h> void free (void *ptr); You cannot use free( ) to free partial chunks of memory (by passing in a pointer halfway into an allocated block)
12

Pitfalls in Dynamic Allocations


If the program doesnt return the memory to the system or loses the reference to it, it is called a Memory Leak After calling free(), dont access the contents of the freed memory Set the pointer to NULL after freeing it. If not, the pointer is termed as dangling pointer Two common tools to aid you in this quest are Electric Fence and valgrind

13

Alignment of Data
The alignment of data refers to the relation between its address and memory chunks as measured by the hardware A variable located at a memory address that is a multiple of its size is said to be naturally aligned For example, a 32-bit variable is naturally aligned if it is located in memory at an address that is a multiple of 4

14

Alignment After-effects
On some systems, a load of unaligned data results in a processor trap On other systems, accessing unaligned data is safe, but results in a degradation of performance When writing portable code, alignment issues must be avoided, and all types should be naturally aligned

15

Alignment Requirements
Memory returned via malloc( ), calloc( ), and realloc( ) be properly aligned for use with any of the standard C types On Linux, these functions always return memory that is aligned along an 8 byte boundary on 32-bit systems and a 16 byte boundary on 64-bit systems

16

Customized Alignment
Occasionally, programmers require dynamic memory aligned along a larger boundary, such as a page POSIX 1003.1d provides a function named posix_memalign( )
#include <stdlib.h> int posix_memalign (void **memptr, size_t alignment, size_t size);

17

Alignment in Structures
The alignment requirement of a structure is that of its largest constituent type Structures also introduce the need for padding Padding is, filling zeros where ever alignment is necessary The GCC option -Wpadded generates a warning whenever the compiler inserts implicit padding

18

Other Alignment Requirements


The alignment requirement of a union is that of the largest unionized type The alignment requirement of an array is that of the base type

19

Risks on Alignment
Compiler transparently handles most alignment requirements and it may hide certain potential issues Do not enable structure packing Accessing data via a pointer recast from a lesseraligned to a larger-aligned block of data can result in the processor loading data that is not properly aligned for the larger type The reverse is perfectly safe

20

Risks on Alignment
char greeting[] = Hello World"; char *c = greeting[1]; unsigned long badnews = *(unsigned long *) c;

An unsigned long is likely aligned along a four or eight byte boundary. c almost certainly sits one byte off that same boundary The load of c, when typecast, causes an alignment violation

21

Risks on Alignment
On machine architectures that can detect but not properly handle alignment violations, the kernel sends the offending process the SIGBUS signal, which terminates the process

22

The Heap Boundary


#include <unistd.h> int brk (void *end); void * sbrk (intptr_t increment); A call to brk( ) sets the break point (the end of the heap) to the address specified by end A call to sbrk( ) increments the end of the heap by increment, which may be a positive or negative delta Portable programs should not use any of these but rely on malloc() to handle the allocations
23

Implementing Malloc
Memory allocation in glibc uses the heap segment and memory mappings malloc( ) divides the heap segment into a series of power-of-2 partitions, and satisfy allocations by returning the partition that is the closest fit to the requested size Freeing memory is as simple marking the partition as free.

If adjacent partitions are free, they can be coalesced into a single, larger partition
If the top of the heap is entirely free, the system can use brk( ) to lower the break point, returning memory to the kernel
24

Implementing Malloc
This algorithm is called a buddy memory allocation scheme It has the upside of speed and simplicity, but the downside of introducing two types of fragmentation Internal fragmentation occurs when more memory than requested is used to satisfy an allocation External fragmentation occurs when sufficient memory is free to satisfy a request, but it is split into two or more nonadjacent chunks
25

Anonymous Memory Mappings


For large allocations, glibc does not use the heap. Instead, glibc creates an anonymous memory mapping to satisfy the allocation request Anonymous memory mappings are similar to the filebased mappings except that they are not backed by any file Its simply a large, zero-filled block of memory, ready for use Because these mappings are located outside of the heap, they do not contribute to fragmentation
26

Anonymous Memory Mappings


Two downsides to using anonymous memory mappings: Each memory mapping is an integer multiple of the system page size

Creating a new memory mapping incurs more overhead than returning memory from the heap
glibcs malloc( ) uses the heap segment to satisfy small allocations and anonymous memory mappings to satisfy large allocations Currently, the threshold for this is 128 KB

27

Creating Memory Mappings


#include <sys/mman.h> void * mmap (void *start, size_t length, int prot, int flags, int fd, off_t offset); int munmap (void *start, size_t length);

28

Creating Memory Mappings


The first parameter, start, is set to NULL The flags parameter sets the MAP_ANONYMOUS bit, making this mapping anonymous The fd and offset parameters are ignored when MAP_ANONYMOUS is set. Keep it to -1 for portability

For better zero filling performance, open fd as /dev/zero, pass it to mmap, and then close fd

29

Fine-Tuning Memory Allocation


#include <malloc.h> size_t malloc_usable_size (void *ptr); int malloc_trim (size_t padding); malloc_usable_size( ) returns the actual allocation size of the chunk of memory pointed to by ptr (not requested) malloc_trim( ) shrinks the data segment as much as possible, minus padding bytes, which are reserved and returns 1 (and 0 on failure) Dont use them
30

Debugging Memory Allocations


$ MALLOC_CHECK_=1 ./helloworld If MALLOC_CHECK_ is set to 0, the memory subsystem silently ignores any errors If it is set to 1, an informative message is printed to stderr If it is set to 2, the program is immediately terminated via abort( )

31

Debugging Memory Allocations


#include <malloc.h> struct mallinfo mallinfo (void); /* all sizes in bytes */ struct mallinfo { int arena; /* size of data segment used by malloc */ int ordblks; /* number of free chunks */ int smblks; /* number of fast bins */ int hblks; /* number of anonymous mappings */ int hblkhd; /* size of anonymous mappings */ int usmblks; /* maximum total allocated size */ int fsmblks; /* size of available fast bins */ int uordblks; /* size of total allocated space */ int fordblks; /* size of available chunks */ int keepcost; /* size of trimmable space */ };

32

Debugging Memory Allocations


#include <malloc.h> void malloc_stats (void); malloc_stats( ) function, prints memory-related statistics to stderr

33

Stack-Based Allocations
#include <alloca.h> void * alloca (size_t size); alloca() make a dynamic memory allocation from the stack This memory lives on the stack, and is automatically freed when the invoking function returns Dont call free() for memory allocated with alloca(). This results in inefficient stack managment
34

Stack-Based Allocations
Dont use alloca( )-allocated memory in the parameters to a function call, because the allocated memory will then exist in the middle of the stack
/* DO NOT DO THIS! */ ret = foo (x, alloca (10));

using alloca( ) is an easy way to overflow the stack


alloca() is faster than malloc() as there is no real hunt for free memory. But it is less portable
35

Duplicating Strings on the Stack


#include <string.h> char * strdupa (const char *s); char * strndupa (const char *s, size_t n); A call to strdupa( ) returns a duplicate of s. A call to strndupa( ) duplicates up to n characters of s The duplicated string is automatically freed when the invoking function returns POSIX does not define the alloca( ), strdupa( ), or strndupa( )
36

Variable-Length Arrays
C99 introduced variable-length arrays (VLAs), which are arrays whose geometry is set at runtime, and not at compile time for (i = 0; i < n; ++i) { char foo[i + 1]; /* use 'foo'... */ }

If we used alloca( ) instead of a VLA, the memory would not be freed until the function returned
37

Filling Memory
#include <string.h> void * memset (void *s, int c, size_t n); A call to memset( ) sets the n bytes starting at s to the byte c and returns s A frequent use is zeroing a block of memory Do not use memset( ) if you can use calloc( ). The latter is more efficient

38

Comparing Bytes
#include <string.h> int memcmp (const void *s1, const void *s2, size_t n); This compares the first n bytes of s1 to s2, and returns 0 if the blocks of memory are equivalent, a value less than zero if s1 is less than s2, and a value greater than zero if s1 is greater than s2

Because of structure padding comparing two structures for equivalence via memcmp( ) is unreliable

39

Moving Bytes
#include <string.h> void * memmove (void *dst, const void *src, size_t n); void * memcpy (void *dst, const void *src, size_t n); void * mempcpy (void *dst, const void *src, size_t n); memmove( ) can safely handle overlapping memory regions (say, if part of dst is inside of src) memcpy( ) faster than memmove( ), but dst and src may not overlap mempcpy( ) function performs the same as memcpy( ), except that it returns a pointer to the next byte after the last byte copied
40

Searching Bytes
#include <string.h> void * memchr (const void *s, int c, size_t n); void * memrchr (const void *s, int c, size_t n);

memchr( ) function scans the n bytes of memory pointed at by s for the character c
memrchr( ) function is the same as the memchr( ) function, except that it searches backward from the end of the n bytes pointed at by s

41

Searching Bytes
#include <string.h> void * memmem (const void *haystack, size_t haystacklen, const void *needle, size_t needlelen);

memmem( ) function returns a pointer to the first occurrence of the subblock needle, of length needlelen bytes, within the block of memory haystack, of length haystacklen bytes If the function does not find needle in haystack, it returns NULL
42

Jumbling Bytes
#include <string.h> void * memfrob (void *s, size_t n); memfrob( ) scrambles the first n bytes of memory starting at s by exclusive ORing (XORing) each byte with the number 42. The call returns s The effect of a call to memfrob( ) can be reversed by calling memfrob( ) again on the same region of memory This is not a substitute for encryption
43

Locking Memory
Linux implements demand paging, which means that pages are swapped in from disk as needed, and swapped out to disk when no longer needed Swapping hurts two things: Determinism: page faults can overrun the timing needs Security: private secrets swapped on a disk may not be acceptable in a high-security environment

44

Locking Memory
The kernel, if we trust its design, always chooses the optimal page to swap outthat is, the page least likely to be used in the future So when you change its behavior, it has to swap out a suboptimal page

45

Locking Part of Address Space


#include <sys/mman.h> int mlock (const void *addr, size_t len); mlock( ) locks the virtual memory starting at addr, and extending for len bytes into physical memory The POSIX standard dictates that addr should be aligned to a page boundary. Linux adjusts this automatically

46

Locking All of an Address Space


#include <sys/mman.h> int mlockall (int flags); mlockall( ) locks all of the pages in the current process address space into physical memory CAP_IPC_LOCK capability is needed to lock all pages. Resource constrains apply (default - 32KB) The flags parameter is as follows: MCL_CURRENT: lock all currently mapped pages MCL_FUTURE: lock all pages including future ones

47

Unlocking Memory
#include <sys/mman.h> int munlock (const void *addr, size_t len); int munlockall (void);

munlock( ) unlocks the pages starting at addr and extending for len bytes
munlockall( ) undoes the effects of mlockall( ) Memory locks do not nest. Therefore, a single mlock( ) or munlock( ) will unlock a locked page
48

Fork() Swap
When the parent process calls fork() system call, the child process is created If there is short of memory then the child process is sent to the read-to-run state in the swap device The parent process is not swapped out

When the memory will be available the child process will be swapped into the main memory

49

Opportunistic Allocation
Linux employs an opportunistic allocation strategy When a process requests additional memory from the kernel the kernel commits to the memory without actually providing any physical storage Only when the process writes to the newly allocated memory does the kernel satisfy the commitment by converting the commitment for memory to a physical allocation of memory The kernel does this on a page-by-page basis
50

Opportunistic Allocation
The amount of committed memory can far exceed the amount of physical memory and even swap space available This feature is called overcommitment

51

Overcommitting and OOM


Without overcommitment, mapping a 2 GB file copy-onwrite would require the kernel to set aside 2 GB of storage With overcommitment, mapping a 2 GB file requires storage only for each page of data to which the process actually writes

When overcommitment results in insufficient memory to satisfy a committed request, is called an out of memory (OOM) condition

52

Overcommitting and OOM


In response to an OOM condition, the kernel employs the OOM killer to pick a process worthy of termination The kernel tries to find the least important process that is consuming the most memory Indeterministic termination of a process by the OOM killer is often unacceptable Disable overcommitment via the file /proc/sys/vm/overcommit_memory. A value of 2 disables overcommitments altogether, and enables strict accounting
53

Overcommitting and OOM


In strict accounting mode, memory commitments are restricted to the size of the swap area, plus a configurable percentage of physical memory The configuration percentage is set via the file /proc/sys/vm/overcommit_ratio Suppose that you have 256MB of RAM and 256MB of swap and you want to limit overcommit at 384MB. That means 256 + 50 percent * 256MB, so put 50 on /proc/sys/vm/overcommit_ratio

54

Memory Allocation Statistics


The Linux kernel provides /proc/meminfo as a way to find complete information about memory conditions $ cat /proc/meminfo MemTotal: 255944 kB MemFree: 3668 kB Buffers: 13640 kB Cached: 171788 kB SwapCached: 0 kB HighTotal: 0 kB HighFree: 0 kB LowTotal: 255944 kB LowFree: 3668 kB SwapTotal: 909676 kB SwapFree: 909676 kB
55

Debugging with Valgrind


Available for x86 and AMD64 architectures It allows you to run your program in Valgrind's own environment that monitors memory usage such as calls to malloc and free If you use uninitialized memory, write off the end of an array, or forget to free a pointer, Valgrind can detect it

56

Memory Leaks with Valgrind


#include <stdlib.h> int main() { char *x = malloc(100); /* or, in C++, "char *x = new char[100] */ return 0; } % valgrind --tool=memcheck --leak-check=yes example1

==2116== 100 bytes in 1 blocks are definitely lost in loss record 1 of 1 ==2116== at 0x1B900DD0: malloc (vg_replace_malloc.c:131) ==2116== by 0x804840F: main (in /home/cprogram/example1)

57

Invalid Pointer using Valgrind


#include <stdlib.h> int main() { char *x = malloc(10); x[10] = 'a'; return 0; } valgrind --tool=memcheck --leak-check=yes example2
==9814== ==9814== ==9814== ==9814== ==9814== Invalid write of size 1 at 0x804841E: main (example2.c:6) Address 0x1BA3607A is 0 bytes after a block of size 10 alloc'd at 0x1B900DD0: malloc (vg_replace_malloc.c:131) by 0x804840F: main (example2.c:5)

58

Thank you!

59

You might also like