⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 3.html

📁 网上一个牛人整理的关于linux内核编译
💻 HTML
📖 第 1 页 / 共 4 页
字号:
mm/swap_file.c 交换分区/文件的操作。<p>
mm/page_io.c 读或写一个交换页。<p>
mm/swap_state.c swap cache相关操作,加入/删除/查找一个swap cache等。<p>
mm/vmscan.c 扫描进程的vm_area,试图换出一些页面(kswapd)。<p>
reclaim_page:从inactive_clean_list回收一个页面,放到free_list<p>
&nbsp;&nbsp;&nbsp; kclaimd被唤醒后重复调用reclaim_page直到每个区的<p>
zone-&gt;free_pages&gt;= zone-&gt;pages_low<p>
&nbsp;&nbsp;&nbsp; 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页写回。<p>
&nbsp;&nbsp;&nbsp; 关于这几个队列(active_list,inactive_dirty_list,inactive_clean_list)的逻辑,请参照:文档:RFC: design for new VM,可以从lisoleg的文档精华获得。<p>
page cache、buffer cache和swap cache<p>
&nbsp;&nbsp;&nbsp; page cache:读写文件时文件内容的cache,大小为一个页。不一定在磁盘上连续。<p>
&nbsp;&nbsp;&nbsp; buffer cache:读写磁盘块的时候磁盘块内容的cache,buffer cache的内容对应磁盘上一个连续的区域,一个buffer cache大小可能从512(扇区大小)到一个页。<p>
&nbsp;&nbsp;&nbsp; swap cache: 是page cache的子集。用于多个进程共享的页面被换出到交换区的情况。<p>
page cache 和 buffer cache的关系<p>
&nbsp;&nbsp;&nbsp; 本质上是很不同的,buffer cache缓冲磁盘块内容,page cache缓冲文件的一页内容。page cache写回时会使用临时的buffer cache来写磁盘。<p>
bdflush: 把dirty的buffer cache写回磁盘。通常只当dirty的buffer太多或者需要更多的buffer而内存开始不足时运行。page_lauder也可能唤醒它。<p>
kupdate: 定时运行,把写回期限已经到了的dirty buffer写回磁盘。<p>
&nbsp;&nbsp;&nbsp; 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很难实现,许多讨论过)。<p>
虚存系统则从freeBSD借鉴了很多经验,针对2.2的问题作了巨大的调整。<p>
&nbsp;&nbsp;&nbsp; 文档:RFC: design for new VM不可不读。<p>
&nbsp;&nbsp;&nbsp; 由于时间仓促,新vm的很多细微之处我也还没来得及搞清楚。先大致罗列一下,以后我将进一步完善本文,争取把问题说清楚。另外,等这学期考试过后,我希望能为大家提供一些详细注释过的源代码。<p>
<p>
<br>



<center><A HREF="#Content">[目录]</A></center>
<hr><br><A NAME="I378" ID="I378"></A><center><b><font size=+2>用户态</font></b></center><br>
&nbsp;&nbsp;&nbsp; 用户空间存取内核空间,具体的实现方法要从两个方面考虑,先是用户进程,需要调用mmapp来将自己的一段虚拟空间映射到内核态分配的物理内存;然后内核空间需要重新设置用户进程的这段虚拟内存的页表,使它的物理地址指向对应的物理内存。针对linux内核的几种不同的内存分配方式(kmalloc、vmalloc和ioremap),需要进行不同的处理。<p>
一、Linux内存管理概述<p>
这里说一下我的理解,主要从数据结构说。<p>
1、物理内存都是按顺序分成一页一页的,每页用一个page结构来描述。系统所有的物理页 面的page结<p>
构描述就组成了一个数组mem_map。<p>
2、进程的虚拟地址空间用task_struct的域mm来描述,它是一个mm_struct结构,这个结构包包含了指向?<p>
程页目录的指针(pgd_t * pgd)和指向进程虚拟内存区域的指针(struct vm_area_structt * mmap)<p>
3、进程虚拟内存区域具有相同属性的段用结构vm_area_struct描述(简称为VMA)。进程所所有的VMA?<p>
<br>
树组织。<p>
4、每个VMA就是一个对象,定义了一组操作,可以通过这组操作来对不同类型的VMA进行不屯 的处理。<p>
例如对vmalloc分配的内存的映射就是通过其中的nopage操作实现的。<p>
二、mmap处理过程<p>
当用户调用mmap的时候,内核进行如下的处理:<p>
1、先在进程的虚拟空间查找一块VMA;<p>
2、将这块VMA去映射<p>
3、如果设备驱动程序或者文件系统的file_operations定义了mmap操作,则调用它<p>
4、将这个VMA插入到进程的VMA链中<p>
file_operations的中定义的mmap方法原型如下:<br>
int (*mmap) (struct file *, struct vm_area_struct *);<p>
其中file是虚拟空间映射到的文件结构,vm_area_struct就是步骤1中找到的VMA。<p>
三、缺页故障处理过程<p>
当访问一个无效的虚拟地址(可能是保护故障,也可能缺页故障等)的时候,就会产生一个个页故障,?<p>
统的处理过程如下:<p>
1、找到这个虚拟地址所在的VMA;<p>
2、如果必要,分配中间页目录表和页表<p>
3、如果页表项对应的物理页面不存在,则调用这个VMA的nopage方法,它返回物理页面的paage描述结构<p>
(当然这只是其中的一种情况)<p>
4、针对上面的情况,将物理页面的地址填充到页表中<p>
当页故障处理完后,系统将重新启动引起故障的指令,然后就可以正常访问了<p>
下面是VMA的方法:<br>
struct vm_operations_struct {<br>
 void (*open)(struct vm_area_struct * area);<br>
 void (*close)(struct vm_area_struct * area);<br>
 struct page * (*nopage)(struct vm_area_struct * area, unsigned long address, innt<p>
write_access);<br>
};<p>
其中缺页函数nopage的address是引起缺页故障的虚拟地址,area是它所在的VMA,write_acccess是存取<p>
属性。<p>
三、具体实现<p>
3.1、对kmalloc分配的内存的映射<p>
对kmalloc分配的内存,因为是一段连续的物理内存,所以它可以简单的在mmap例程中设置汉 页表的物<p>
理地址,方法是使用函数remap_page_range。它的原型如下:<p>
int remap_page_range(unsigned long from, unsigned long phys_addr, unsigned long size,<p>
pgprot_t prot)<p>
其中from是映射开始的虚拟地址。这个函数为虚拟地址空间from和from+size之间的范围构栽 页表;<p>
phys_addr是虚拟地址应该映射到的物理地址;size是被映射区域的大小;prot是保护标志?<p>
remap_page_range的处理过程是对from到form+size之间的每一个页面,查找它所在的页目侣己 页表(<p>
必要时建立页表),清除页表项旧的内容,重新填写它的物理地址与保护域。<p>
remap_page_range可以对多个连续的物理页面进行处理。&lt;&lt;Linux设备驱动程序&gt;&gt;指出,<p>
remap_page_range只能给予对保留的页和物理内存之上的物理地址的访问,当对非保留的页页使?<p>
remap_page_range时,缺省的nopage处理控制映射被访问的虚地址处的零页。所以在分配内内存后,就?<p>
对所分配的内存置保留位,它是通过函数mem_map_reserve实现的,它就是对相应物理页面?<p>
PG_reserved标志位。(关于这一点,参见前面的主题为“关于remap_page_range的疑问”档奶致郏?<p>
因为remap_page_range有上面的限制,所以可以用另外一种方式,就是采用和vmalloc分配档哪 存同样<p>
的方法,对缺页故障进行处理。<p>
3.2、对vmalloc分配的内存的映射<p>
<br>
3.2.1、vmalloc分配内存的过程<p>
(1)、进行预处理和合法性检查,例如将分配长度进行页面对齐,检查分配长度是否过大?<p>
(2)、以GFP_KERNEL为优先级调用kmalloc分配(GFP_KERNEL用在进程上下文中,所以这里里就限制了?<p>
中断处理程序中调用vmalloc)描述vmalloc分配的内存的vm_struct结构。<p>
(3)、将size加一个页面的长度,使中间形成4K的隔离带,然后在VMALLOC_START和VMALLOOC_END之间<p>
编历vmlist链表,寻找一段自由内存区间,将其地址填入vm_struct结构中<p>
(4)、返回这个地址<p>
vmalloc分配的物理内存并不连续<p>
3.2.2、页目录与页表的定义<p>
typedef struct { unsigned long pte_low; } pte_t;<br>
typedef struct { unsigned long pmd; } pmd_t;<br>
typedef struct { unsigned long pgd; } pgd_t;<br>
#define pte_val(x) ((x).pte_low)<p>
3.2.3、常见例程:<p>
(1)、virt_to_phys():内核虚拟地址转化为物理地址<br>
#define __pa(x)&nbsp; ((unsigned long)(x)-PAGE_OFFSET)<br>
extern inline unsigned long virt_to_phys(volatile void * address)<br>
{<br>
 return __pa(address);<br>
}<p>
上面转换过程是将虚拟地址减去3G(PAGE_OFFSET=0XC000000),因为内核空间从3G到3G+实实际内存一?<p>
映射到物理地址的0到实际内存<p>
(2)、phys_to_virt():内核物理地址转化为虚拟地址<br>
#define __va(x)&nbsp; ((void *)((unsigned long)(x)+PAGE_OFFSET))<br>
extern inline void * phys_to_virt(unsigned long address)<br>
{<br>
 return __va(address);<br>
}<br>
virt_to_phys()和phys_to_virt()都定义在include\asm-i386\io.h中<p>
(3)、#define virt_to_page(kaddr) (mem_map + (__pa(kaddr) &gt;&gt; PAGE_SHIFT))(内核核2.4?<br>
&nbsp;&nbsp; #define VALID_PAGE(page) ((page - mem_map) &lt; max_mapnr)(内核2.4)<br>
第一个宏根据虚拟地址,将其转换为相应的物理页面的page描述结构,第二个宏判断页面是是不是在有?<p>
的物理页面内。(这两个宏处理的虚拟地址必须是内核虚拟地址,例如kmalloc返回的地址#

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -