1. 进程地址空间

Linux中,进程并不是直接操作物理内存地址,而是每个进程关联一个虚拟地址空间
内存页是memory management unit (MMU) 可以管理的最小地址单元
机器的体系结构决定了内存页大小,32位系统通常是 4KB, 64位系统通常是 8KB
内存页分为 valid or invalid:
A valid page is associated with an actual page of data,例如RAM或者磁盘上的文件
An invalid page is not associated with anything,表现为 未使用未分配的地址空间,存取无效内存页将会导致 段错误
 
如果有效内存页与外存数据相关联,进程就不能存取内存页直到数据交换至物理内存
当进程直接存取关联到磁盘文件的内存页时,会产生 缺页中断,然后内核透明地将数据从外存磁盘置换到 物理内存
there is considerably more virtual memory than physical memory
Paging out is the process of moving data from physical memory to secondary storage
 

2. 动态内存

/* obtaining dynamic memory */
#include <stdlib.h>
void * malloc (size_t size);
The contents of the memory are undefined
切记:每次动态分配内存都必须严格严查返回值是否为NULL,表示分配失败
 
/* returns a pointer to a block of memory suitable for holding an array of nr elements, each of size bytes */
#include <stdlib.h>
void * calloc (size_t nr, size_t size);
注意:calloc() zeros all bytes in the returned chunk of memory,默认0值初始化
程序员必须优先使用 calloc() 确保分配的内存已0值初始化
calloc() 进行0值初始化 比 memset 速度要快
/* resizing (making larger or smaller) existing allocations */
#include <stdlib.h>
void * realloc (void *ptr, size_t size);
It returns a pointer to the newly sized memory. 
参数size为0时,此时realloc相当于 free
参数ptr为NULL时,此时realloc相当于 malloc
 

3. 数据对齐

编写跨平台代码时,必须小心数据对齐要求
大多数情况下,编译器和C标准库透明的处理数据对齐情况,Linux系统中,malloc系列函数分配的内存 在32位系统是 8-byte 对齐,在64位系统是16-byte 对齐
posix_memalign() 是改变系统默认数据对齐规则的函数
因为数据对齐要求,所以动态内存分配得到的实际大小 一定是 大于或等于 请求大小
/* actual allocation size of the chunk of memory pointed to by ptr */
#include <malloc.h>
size_t malloc_usable_size (void *ptr);
dereferencing a cast of a pointer from one type of variable to a different type is usually a violation of the strict aliasing rule
 

4. 管理数据段

#include <unistd.h>
int brk (void *end);
void * sbrk (intptr_t increment);
brk() sets the break point (the end of the data segment) to the address specified by end.
sbrk() increments the end of the data segment by increment bytes, which may be a positive or negative delta.
 

5. 匿名内存映射

glibc的内存分配综合使用了 brk、sbrk数据段管理 和 mmap 内存映射
伙伴内存分配(buddy memory allocation scheme):
将数据段划分为一系列大小为2的指数幂的内存块,返回一个与请求大小最适应的内存块
释放内存时,如果相邻的划分块也被标记位free,那么将合并内存块
优点:速度快,简单
缺点:导致大量 内部碎片
内部碎片:实际分配到的内存块比请求大小要多,导致 分配的内存块使用率低下
外部碎片:系统中空闲内存块总和要比请求大小多,但是没有 单一内存块满足请求大小,导致 系统内存块使用率低下
 
the heap is not shrunk after each free. Instead, the malloc() implementation keeps freed memory around for a subsequent allocation.
但是当申请较大内存时,如果释放后,仍然将此内存保留以便后续分配,这将影响到系统内存使用
 
For large allocations, glibc does not use the heap. Instead, glibc creates an anonymous memory mapping to satisfy the allocation request
普通内存映射将内存映射到磁盘文件,匿名内存映射将内存映射到 a large, zero-filled block of memory
匿名内存映射发生在 堆内存之外,所以不会发生数据段内存碎片
 
匿名内存映射的好处:
a. 不用担心碎片问题,the mapping is unmapped,则内存立即归还系统
b. 可以调整块大小,且有可以调整的权限
c. 每次分配都存在单一内存映射中,不用担心全局的堆内存管理
d. 分配到的内存已经0值初始化,因为 kernel maps the application's anonymous pages to a zero-filled page via copy-on-write
 
匿名内存映射的坏处:
a. 每个内存映射大小必须是系统内存页面大小的整数倍,这将导致内存空间浪费,利用率低
b. 创建内存映射的开销 大于 堆内存分配的开销 
 
glibc's malloc() uses the data segment to satisfy small allocations and anonymous memory mappings to satisfy large allocations
默认临界值是128KB,小于或等于128KB则使用堆内存分配,大于128KB则使用匿名内存映射
 
创建匿名内存映射 只需要在mmap函数中将 fd参数设置为-1 (因为并不是映射到文件)
 
void *p;
p = mmap (NULL, /* do not care where */
* , /* 512 KB */
PROT_READ | PROT_WRITE, /* read/write */
MAP_ANONYMOUS | MAP_PRIVATE, /* anonymous, private */
−, /* fd (ignored) */
); /* offset (ignored) */
if (p == MAP_FAILED)
perror ("mmap");
else
/* 'p' points at 512 KB of anonymous memory... */

6. 基于栈的动态内存分配

/* make a dynamic memory allocation from the stack */
#include <alloca.h>
void * alloca (size_t size);
Usage is identical to malloc(), but you do not need to (indeed, must not) free the allocated memory
注意 alloca是在栈上进行动态内存分配,并且 不需要使用free释放内存
This means you cannot use this memory once the function that calls alloca() returns! However, because you don't have to do any  cleanup by calling free()
 
POSIX未定义alloca函数,所以不适合编写跨平台程序
 

7. 变长数组 Variable-Length Arrays

C99引进 变长数组,数组的长度在运行时动态确定,而不是在编译时静态确定
for (i = ; i < n; ++i) {
char foo[i + ];
/* use 'foo'... */
}
On each iteration of the loop, foo is dynamically created and automatically cleaned up when it falls out of scope
 

8. 选择内存分配机制

 
 

9. 内存操作

/* memset() sets the n bytes starting at s to the byte c and returns s */
#include <string.h>
void * memset (void *s, int c, size_t n);
/* compares two chunks of memory for equivalence */
#include <string.h>
int memcmp (const void *s1, const void *s2, size_t n);

因为结构体通常涉及到数据对齐,所以使用memcmp来比较两个结构体是不安全的

/* are two dinghies identical? (BROKEN) */
int compare_dinghies (struct dinghy *a, struct dinghy *b)
{
return memcmp (a, b, sizeof (struct dinghy));
}

上述代码不安全,应该分别比较每个结构体成员:

/* are two dinghies identical? */
int compare_dinghies (struct dinghy *a, struct dinghy *b)
{
int ret;
if (a->nr_oars < b->nr_oars)
return −;
if (a->nr_oars > b->nr_oars)
return ;
ret = strcmp (a->boat_name, b->boat_name);
if (ret)
return ret;
/* and so on, for each member... */
}
/* copies the first n bytes of src to dst, returning dst */
#include <string.h>
void * memmove (void *dst, const void *src, size_t n);

memmove可以正确处理内存区重叠的情况(部分dst位于src之内)

#include <string.h>
void * memcpy (void *dst, const void *src, size_t n)

memcpy在内存区出现重叠时 属于未定义行为

/* scans the n bytes of memory pointed at by s for the character c */
#include <string.h>
void * memchr (const void *s, int c, size_t n);

10. 锁定内存

Linux实现的 内存页置换, which means that pages are paged in from disk as needed and paged out to disk when no longer needed
/* “locking”one or more pages into physical memory, ensuring that they are never paged out to disk */
#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

/*  mlockall() locks all of the pages in the current process's address space into physical memory. */
#include <sys/mman.h>
int mlockall (int flags);

Linux System Programming 学习笔记(九) 内存管理的更多相关文章

  1. Linux System Programming 学习笔记(五) 进程管理

    1. 进程是unix系统中两个最重要的基础抽象之一(另一个是文件) A process is a running program A thread is the unit of activity in ...

  2. Linux System Programming 学习笔记(八) 文件和目录管理

    1. 文件和元数据 每个文件都是通过inode引用,每个inode索引节点都具有文件系统中唯一的inode number 一个inode索引节点是存储在Linux文件系统的磁盘介质上的物理对象,也是L ...

  3. Linux System Programming 学习笔记(一) 介绍

    1. Linux系统编程的三大基石:系统调用.C语言库.C编译器 系统调用:内核向用户级程序提供服务的唯一接口.在i386中,用户级程序执行软件中断指令 INT n 之后切换至内核空间 用户程序通过寄 ...

  4. Linux System Programming 学习笔记(七) 线程

    1. Threading is the creation and management of multiple units of execution within a single process 二 ...

  5. Linux System Programming 学习笔记(四) 高级I/O

    1. Scatter/Gather I/O a single system call  to  read or write data between single data stream and mu ...

  6. Linux System Programming 学习笔记(十一) 时间

    1. 内核提供三种不同的方式来记录时间 Wall time (or real time):actual time and date in the real world Process time:the ...

  7. Linux System Programming 学习笔记(十) 信号

    1. 信号是软中断,提供处理异步事件的机制 异步事件可以是来源于系统外部(例如用户输入Ctrl-C)也可以来源于系统内(例如除0)   内核使用以下三种方法之一来处理信号: (1) 忽略该信号.SIG ...

  8. Linux System Programming 学习笔记(六) 进程调度

    1. 进程调度 the process scheduler is the component of a kernel that selects which process to run next. 进 ...

  9. Linux System Programming 学习笔记(二) 文件I/O

    1.每个Linux进程都有一个最大打开文件数,默认情况下,最大值是1024 文件描述符不仅可以引用普通文件,也可以引用套接字socket,目录,管道(everything is a file) 默认情 ...

随机推荐

  1. 解决cocos游戏安卓release版本闪退问题

    在cocos中偶尔会遇到闪退的问题,特别是android和ios系统下的闪退就特别难处理了, 虽然说能使用xcode和eclipse显示log,但是也会出现一些特别的情况,直接闪退而且 没有任何预兆. ...

  2. iOS动画之iOS UIBezierPath类 介绍

    感谢:http://blog.csdn.net/crayondeng/article/details/11093689 使用UIBezierPath类可以创建基于矢量的路径,这个类在UIKit中.此类 ...

  3. vue组件:canvas实现图片涂鸦功能

    方案背景 需求 需要对图片进行标注,导出图片. 需要标注N多图片最后同时保存. 需要根据多边形区域数据(区域.颜色.名称)标注. 对应方案 用canvas实现涂鸦.圆形.矩形的绘制,最终生成图片bas ...

  4. pandas按索引插入对应值的处理方法 - join

    在工作中遇到一个问题即,实时的车辆数据中,需要将车辆的vid(一个Series)对应上其通用名称,以及车辆用途等信息进行统计. 正常的小规模操作是利用一个循环,查找vid 在另一张vid对应车辆名称用 ...

  5. Python 使用multiprocessing 特别耗内存

    采用multiprocessing多进程进行数据计算的时候内存飚升,这总体可以说是multiprocessing的一个「bug」导致: 大致原因如下: multiprocessing.Process ...

  6. exp分析

    1 from pwn import* 2 3 local =1 4 debug = 1 5 6 if local: 7 p = process('./pwn1') 8 else: 9 p = remo ...

  7. JS中如何操作数组

    背景:随笔中所应用到的代码来自于上一篇随笔,MVC&JQuery如何根据List动态生成表格,部分代码不再重复. 代码如下: $("#btnTan").click(func ...

  8. 将系统从.Net Core2.0升级到.Net Core2.1

    最近将手头的一个.Net Core2.0开发的小系统升级到最新的Core2.1.升级期间遇到了一些问题,现将问题以及解决方法整理汇总一下. 一是作为笔记,二也为跟各位分享一下.如过能帮到看到这帖子的人 ...

  9. BZOJ 3326: [Scoi2013]数数

    数位DP,然而式子真的复杂 #include<cstdio> #include<algorithm> #include<cstring> using namespa ...

  10. 解决获取View的width和Height为0的4种方法

    很经常当我们动态创建某些View时,需要通过获取他们的width和height来确定别的view的布局,但是在onCreate()获取view的width和height会得到0.view.getWid ...