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. OO作业第三单元总结

    目录 一.JML语言理论基础及应用工具链 二.部署JMLUnitNG,自动生成测试用例 三.架构设计 第一次作业 第二次作业 第三次作业 四.Bug分析 五.心得体会 一.JML语言理论基础及应用工具 ...

  2. ES6 -- 模板字符串(反单引号)

    1)直接使用变量 // before var str = 'test'; console.log(str + "123"); // now var str = 'test'; co ...

  3. 【卡常 bitset 分块】loj#6499. 「雅礼集训 2018 Day2」颜色

    好不容易算着块大小,裸的分块才能过随机极限数据:然而这题在线的数据都竟然是构造的…… 题目描述 有 $n$ 个数字,第 $i$ 个数字为 $a_i$. 有 $m$ 次询问,每次给出 $k_i$ 个区间 ...

  4. Docker系列一:Docker的介绍和安装

    Docker介绍 Docker是指容器化技术,用于支持创建和实验Linux Container.借助Docker,你可以将容器当做重量轻.模块化的虚拟机来使用,同时,你还将获得高度的灵活性,从而实现对 ...

  5. mac Parallels Desttop 13 win7/win8 无法连接网络

    把 “源” 从共享网络改为"Wi-Fi" 在mac 这边点击菜单栏windows图标,选配置(如果没有配置,点控制中心,在点控制中心的齿轮,)选 硬件 找到网络 解锁,上边第一行就 ...

  6. 使用Spring MVC后实现一个BaseController

    使用Spring MVC技术后,可以实现一个基类的Controller类来分装一些MVC常用的方法,其他的Controller都继承自这个BaseController,这样在使用常用的方法时将会变得非 ...

  7. Linux异常体系之vector_stub宏解析

    ARM-Linux汇编的宏定义语法说明如下: 使用注意: 1.宏定义以.macro开始,以.endm结束 2.可带参数,参数可有默认值 3.直接使用参数的名字\arg vector_stub宏的功能: ...

  8. PAT Basic 1074

    1074 宇宙无敌加法器 地球人习惯使用十进制数,并且默认一个数字的每一位都是十进制的.而在 PAT 星人开挂的世界里,每个数字的每一位都是不同进制的,这种神奇的数字称为“PAT数”.每个 PAT 星 ...

  9. python面试题解析(前端、框架和其他)

    答: HTTP是一个属于应用层的面向对象的协议,由于其简捷.快速的方式,适用于分布式超媒体信息系统.它于1990年提出,经过几年的使用与发展,得到不断地完善和扩展.目前在WWW中使用的是HTTP/1. ...

  10. 反射的妙用-类名方法名做参数进行方法调用实例demo

    首先声明一点,大家都会说反射的效率低下,但是大多数的框架能少了反射吗?当反射能为我们带来代码上的方便就可以用,如有不当之处还望大家指出 1,项目结构图如下所示:一个ClassLb类库项目,一个为测试用 ...