📄 内存.txt
字号:
进程地址空间管理
创建,销毁。
mm_struct, vm_area_struct, mmap/mprotect/munmap
page fault处理,demand page, copy on write
相关文件:
include/linux/mm.h:struct page结构的定义,page的标志位定义以及存取操作宏定义。struct
vm_area_struct定义。mm子系统的函数原型说明。
include/linux/mman.h:和vm_area_struct的操作mmap/mprotect/munmap相关的常量宏定义。
memory.c:page fault处理,包括COW和demand page等。
对一个区域的页表相关操作:
zeromap_page_range: 把一个范围内的页全部映射到zero_page
remap_page_range:给定范围的页重新映射到另一块地址空间。
zap_page_range:把给定范围内的用户页释放掉,页表清零。
mlock.c: mlock/munlock系统调用。mlock把页面锁定在物理内存中。
mmap.c::mmap/munmap/brk系统调用。
mprotect.c: mprotect系统调用。
前面三个文件都大量涉及vm_area_struct的操作,有很多相似的xxx_fixup的代码,它们的任务是修补受到影响的区域,保证vm_area_struct
链表正确。
交换
目的:
使得进程可以使用更大的地址空间。同时容纳更多的进程。
任务:
选择要换出的页
决定怎样在交换区中存储页面
决定什么时候换出
kswapd内核线程:每10秒激活一次
任务:当空闲页面低于一定值时,从进程的地址空间、各类cache回收页面
为什么不能等到内存分配失败再用try_to_free_pages回收页面?原因:
有些内存分配时在中断或异常处理调用,他们不能阻塞
有时候分配发生在某个关键路径已经获得了一些关键资源的时候,因此它不能启动IO。如果不巧这时所有的路径上的内存分配都是这样,内存就无法释放。
kreclaimd 从inactive_clean_list回收页面,由__alloc_pages唤醒。
相关文件:
mm/swap.c kswapd使用的各种参数以及操作页面年龄的函数。
mm/swap_file.c 交换分区/文件的操作。
mm/page_io.c 读或写一个交换页。
mm/swap_state.c swap cache相关操作,加入/删除/查找一个swap cache等。
mm/vmscan.c 扫描进程的vm_area,试图换出一些页面(kswapd)。
reclaim_page:从inactive_clean_list回收一个页面,放到free_list
kclaimd被唤醒后重复调用reclaim_page直到每个区的
zone->free_pages>= zone->pages_low
page_lauder:由__alloc_pages和try_to_free_pages等调用。通常是由于freepages +
inactive_clean_list的页太少了。功能:把inactive_dirty_list的页面转移到inactive_clean_list,首先把已经被写回文件或者交换区的页面(by
bdflush)放到inactive_clean_list,如果freepages确实短缺,唤醒bdflush,再循环一遍把一定数量的dirty页写回。
关于这几个队列(active_list,inactive_dirty_list,inactive_clean_list)的逻辑,请参照:文档:RFC:
design for new VM,可以从lisoleg的文档精华获得。
page cache、buffer cache和swap cache
page cache:读写文件时文件内容的cache,大小为一个页。不一定在磁盘上连续。
buffer cache:读写磁盘块的时候磁盘块内容的cache,buffer cache的内容对应磁盘上一个连续的区域,一个buffer
cache大小可能从512(扇区大小)到一个页。
swap cache: 是page cache的子集。用于多个进程共享的页面被换出到交换区的情况。
page cache 和 buffer cache的关系
本质上是很不同的,buffer cache缓冲磁盘块内容,page cache缓冲文件的一页内容。page cache写回时会使用临时的buffer
cache来写磁盘。
bdflush: 把dirty的buffer
cache写回磁盘。通常只当dirty的buffer太多或者需要更多的buffer而内存开始不足时运行。page_lauder也可能唤醒它。
kupdate: 定时运行,把写回期限已经到了的dirty buffer写回磁盘。
2.4的改进:page cache和buffer cache耦合得更好了。在2.2里,磁盘文件的读使用page cache,而写绕过page
cache,直接使用buffer cache,因此带来了同步的问题:写完之后必须使用update_vm_cache()更新可能有的page
cache。2.4中page cache做了比较大的改进,文件可以通过page cache直接写了,page cache优先使用high
memory。而且,2.4引入了新的对象:file address space,它包含用来读写一整页数据的方法。这些方法考虑到了inode的更新、page
cache处理和临时buffer的使用。page cache和buffer cache的同步问题就消除了。原来使用inode+offset查找page
cache变成通过file address space+offset;原来struct page
中的inode成员被address_space类型的mapping成员取代。这个改进还使得匿名内存的共享成为可能(这个在2.2很难实现,许多讨论过)。
虚存系统则从freeBSD借鉴了很多经验,针对2.2的问题作了巨大的调整。
文档:RFC: design for new VM不可不读。
由于时间仓促,新vm的很多细微之处我也还没来得及搞清楚。先大致罗列一下,以后我将进一步完善本文,争取把问题说清楚。另外,等这学期考试过后,我希望能为大家提供一些详细注释过的源代码。
[目录]
用户态
用户空间存取内核空间,具体的实现方法要从两个方面考虑,先是用户进程,需要调用mmapp来将自己的一段虚拟空间映射到内核态分配的物理内存;然后内核空间需要重新设置用户进程的这段虚拟内存的页表,使它的物理地址指向对应的物理内存。针对linux内核的几种不同的内存分配方式(kmalloc、vmalloc和ioremap),需要进行不同的处理。
一、Linux内存管理概述
这里说一下我的理解,主要从数据结构说。
1、物理内存都是按顺序分成一页一页的,每页用一个page结构来描述。系统所有的物理页 面的page结
构描述就组成了一个数组mem_map。
2、进程的虚拟地址空间用task_struct的域mm来描述,它是一个mm_struct结构,这个结构包包含了指向?
程页目录的指针(pgd_t * pgd)和指向进程虚拟内存区域的指针(struct vm_area_structt * mmap)
3、进程虚拟内存区域具有相同属性的段用结构vm_area_struct描述(简称为VMA)。进程所所有的VMA?
树组织。
4、每个VMA就是一个对象,定义了一组操作,可以通过这组操作来对不同类型的VMA进行不屯 的处理。
例如对vmalloc分配的内存的映射就是通过其中的nopage操作实现的。
二、mmap处理过程
当用户调用mmap的时候,内核进行如下的处理:
1、先在进程的虚拟空间查找一块VMA;
2、将这块VMA去映射
3、如果设备驱动程序或者文件系统的file_operations定义了mmap操作,则调用它
4、将这个VMA插入到进程的VMA链中
file_operations的中定义的mmap方法原型如下:
int (*mmap) (struct file *, struct vm_area_struct *);
其中file是虚拟空间映射到的文件结构,vm_area_struct就是步骤1中找到的VMA。
三、缺页故障处理过程
当访问一个无效的虚拟地址(可能是保护故障,也可能缺页故障等)的时候,就会产生一个个页故障,?
统的处理过程如下:
1、找到这个虚拟地址所在的VMA;
2、如果必要,分配中间页目录表和页表
3、如果页表项对应的物理页面不存在,则调用这个VMA的nopage方法,它返回物理页面的paage描述结构
(当然这只是其中的一种情况)
4、针对上面的情况,将物理页面的地址填充到页表中
当页故障处理完后,系统将重新启动引起故障的指令,然后就可以正常访问了
下面是VMA的方法:
struct vm_operations_struct {
void (*open)(struct vm_area_struct * area);
void (*close)(struct vm_area_struct * area);
struct page * (*nopage)(struct vm_area_struct * area, unsigned long address,
innt
write_access);
};
其中缺页函数nopage的address是引起缺页故障的虚拟地址,area是它所在的VMA,write_acccess是存取
属性。
三、具体实现
3.1、对kmalloc分配的内存的映射
对kmalloc分配的内存,因为是一段连续的物理内存,所以它可以简单的在mmap例程中设置汉 页表的物
理地址,方法是使用函数remap_page_range。它的原型如下:
int remap_page_range(unsigned long from, unsigned long phys_addr, unsigned long
size,
pgprot_t prot)
其中from是映射开始的虚拟地址。这个函数为虚拟地址空间from和from+size之间的范围构栽 页表;
phys_addr是虚拟地址应该映射到的物理地址;size是被映射区域的大小;prot是保护标志?
remap_page_range的处理过程是对from到form+size之间的每一个页面,查找它所在的页目侣己 页表(
必要时建立页表),清除页表项旧的内容,重新填写它的物理地址与保护域。
remap_page_range可以对多个连续的物理页面进行处理。<<Linux设备驱动程序>>指出,
remap_page_range只能给予对保留的页和物理内存之上的物理地址的访问,当对非保留的页页使?
remap_page_range时,缺省的nopage处理控制映射被访问的虚地址处的零页。所以在分配内内存后,就?
对所分配的内存置保留位,它是通过函数mem_map_reserve实现的,它就是对相应物理页面?
PG_reserved标志位。(关于这一点,参见前面的主题为“关于remap_page_range的疑问”档奶致郏?
因为remap_page_range有上面的限制,所以可以用另外一种方式,就是采用和vmalloc分配档哪 存同样
的方法,对缺页故障进行处理。
3.2、对vmalloc分配的内存的映射
3.2.1、vmalloc分配内存的过程
(1)、进行预处理和合法性检查,例如将分配长度进行页面对齐,检查分配长度是否过大?
(2)、以GFP_KERNEL为优先级调用kmalloc分配(GFP_KERNEL用在进程上下文中,所以这里里就限制了?
中断处理程序中调用vmalloc)描述vmalloc分配的内存的vm_struct结构。
(3)、将size加一个页面的长度,使中间形成4K的隔离带,然后在VMALLOC_START和VMALLOOC_END之间
编历vmlist链表,寻找一段自由内存区间,将其地址填入vm_struct结构中
(4)、返回这个地址
vmalloc分配的物理内存并不连续
3.2.2、页目录与页表的定义
typedef struct { unsigned long pte_low; } pte_t;
typedef struct { unsigned long pmd; } pmd_t;
typedef struct { unsigned long pgd; } pgd_t;
#define pte_val(x) ((x).pte_low)
3.2.3、常见例程:
(1)、virt_to_phys():内核虚拟地址转化为物理地址
#define __pa(x) ((unsigned long)(x)-PAGE_OFFSET)
extern inline unsigned long virt_to_phys(volatile void * address)
{
return __pa(address);
}
上面转换过程是将虚拟地址减去3G(PAGE_OFFSET=0XC000000),因为内核空间从3G到3G+实实际内存一?
映射到物理地址的0到实际内存
(2)、phys_to_virt():内核物理地址转化为虚拟地址
#define __va(x) ((void *)((unsigned long)(x)+PAGE_OFFSET))
extern inline void * phys_to_virt(unsigned long address)
{
return __va(address);
}
virt_to_phys()和phys_to_virt()都定义在include\asm-i386\io.h中
(3)、#define virt_to_page(kaddr) (mem_map + (__pa(kaddr) >> PAGE_SHIFT))(内核核2.4?
#define VALID_PAGE(page) ((page - mem_map) < max_mapnr)(内核2.4)
第一个宏根据虚拟地址,将其转换为相应的物理页面的page描述结构,第二个宏判断页面是是不是在有?
的物理页面内。(这两个宏处理的虚拟地址必须是内核虚拟地址,例如kmalloc返回的地址#
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -