只是大概谢谢,笔记类型的。
1. 以页位单位
struct page {
page_flags_t flags; //标记:是否需要写出,是否被锁在内存等,在linux/page-flags.h 中定义
atomic_t _count; //页面引用计数
atomic_t _mapcount; //有多少个页表项映射到了此页面
unsigned long private; //私有数据区,我们在以后将要看到的交换和磁盘高速缓存中会有关于这个成员的使用
struct address_space *mapping; //用于磁盘高速缓存,以后分析
pgoff_t index; //同上
struct list_head lru; //LRU链表,链至管理区的相应链表中
#if defined(WANT_PAGE_VIRTUAL)
void *virtual; //这个页的虚拟地址,如果没有,例如高位的内存(动态映射),这个值是NULL
}
这个结构体是定义物理内存的,不是虚拟内存,因此这个结构体对应用程序来讲是透明的。
内核用这个结构体来记录和保持所有页的状态。
对每一个页都会分配这样的一个结构体,因此会有内存使用方面的问题,但也不大,差不多百分之一,1G会占据10M
2. 内存区域
由于硬件的限制,所有内存不能同等的对待,例如dma只能使用某些内存,为了便于管理,内核将内存划分为三个区域
a. dma 区域
b. 普通区域
c. 高内存区域
具体就不在讨论
3. 内存分配
以页为单位 和 以byte为单位的两种接口
options:
a. GFP_IO: 是否可以进行IO,例如发现内存不够,是否可以将dirty page写回来释放内存
b. GFP_WAIT: 是否可以sleep,例如是否可以被重新调度,在中断处理器里不能设置,不能再中断
c. GFP_FS: 是否可以进行文件系统io操作
4. vmalloc:
分配虚拟地址连续的内存,不需要物理地址连续,user空间使用这个分配器,当物理地址不连续时,在tlb中需要额外的映射,因此会有性能方面的损耗
5. slab 内存分配
一些基本原则:
a. 经常使用到的数据对象会被频繁地分配和释放,所以需要cache他们
b. 频繁地分配和释放会导致内存碎片,为了避免这个问题,缓存的空闲列表应该被组织为连续的
c. 使用空闲列表,分配和释放都变得非常简单,只需要拿走或者返还对象就可以
d. 如果cache能够事先知道对象大小,页大小,以及总的cache大小,就有可能做更多的优化
e. 在smp下,可以每个cpu一个cache,这样就不存在锁的情况,在多线程的程序中,可以每个线程一个cache,这样也不就存在锁的问题
linux的实现:
不同大小的对象叫做caches,例如进程描述符cache,inode cache。 cache被组织为slab,一个slab包含一个或者多个连续的页,每个slab都包含多个对象,每个slab有三种状态,full,partial,empty
6 从栈上分配内存
1. 每个进程的栈大小有限制,ulimit -s, 一般为10M