首页 > 代码库 > 《linux 内核完全剖析》get_free_page(void)

《linux 内核完全剖析》get_free_page(void)

get_free_page(void) 分析极其资料整理





实现在swap.c 里面

程序功能概述:

首先在内存映射字节位图中查找值为0的字节项,然后把对应物理内存页面清零,如果得到的页面地址值大于实际物理内存容量则重新寻找。如果没有找到空闲页面则去调用执行交换处理,并重新查找。最后返回空闲物理地址。


我一开始没能比较熟练的掌握嵌入式汇编,所以又把问题的难度拔高了。。。如果熟练的掌握嵌入式汇编的话,不至于被卡这么久

        :"=a" (__res)
        :"0" (0),"i" (LOW_MEM),"c" (PAGING_PAGES),
        "D" (mem_map+PAGING_PAGES-1)
        :"di","cx","dx");


这里把输出寄存器指定为%eax, 并保存到变量__res中。返回是%0,这个是指令操作数,%0 代表 第一个占位符,即上面“=a” ax寄存器

%1 %2 %3 %4 分别代表代表  ax di cx dx 寄存器

输入

%1(ax = 0) -0

%2   (LOW_MEM) 这里并不是指定到di寄存器,“i”是说明这是个直接操作数。 这个定义为0x1000 是pg0 的起始位置。《注释》书上说“内存字节位图管理的起始位置” 这里有点小含糊,我郁闷了好一会儿,各种发帖子问。。。各种纠结其实我觉得这么描述不是很好,为啥不直接了当的很直白的说这里是mem_map第一个元素对应的内存页捏?我还以为mem_map就放在0x1000。mem_map是个全局变量,是不会放在0x1000的

%3 ecx = PAGING_PAGES

%4 edi = mem_map+PAGING_PAGES-1 这其实是mem_map最后一个元素的地址


限定符意义
"m"、"v"、"o"内存单元
"r"任何寄存器
"q"寄存器eax、ebx、ecx、edx之一
"i"、"h"直接操作数
"E"和"F"浮点数
"g"任意
"a"、"b"、"c"、"d"分别表示寄存器eax、ebx、ecx和edx
"S"和"D"寄存器esi、edi
"I"常数(0至31)


对于这些输入输出寄存器一定要理解好


"std ; repne ; scasb\n\t"

这行代码的作用是“置为方向,al(0) 与对应每个页面的di 做比较”

个人觉得,这句话,不要纠结。。。知道它真正做了什么就OK了。

这行代码会查询edi寄存器储存的地址处的值是否为0,每次循环(repeat 一次)

判断是否为0"jne 1f\n\t" 如果不是0,就跳转到嵌入式汇编结束的位置

如果是,执行跳转指令后面的


unsigned long get_free_page(void)
{
register unsigned long __res asm("ax");

repeat:
    __asm__("std ; repne ; scasb\n\t"//执行完scasb之后ecx - 1, edi -1
        "jne 1f\n\t"//如果在mem_map里没有知道值为0的数组元素,即代表free page的位。那么这个时候所有的页都处于占用状态,get free page 第一次失败
        "movb $1,1(%%edi)\n\t"//如果找到了空闲页,那么执行这指令,然后把该页对应页面内存映像比特位置1.表示占用此页。注意edi寄存器的初始值是LOW_MEM 0x1000
        "sall $12,%%ecx\n\t"
        "addl %2,%%ecx\n\t"// ecx 储存了页面数右移12位并且加上LOW_MEM,即可得到对应页面的地址
        "movl %%ecx,%%edx\n\t"//把页面地址保存到edx里面去
        "movl $1024,%%ecx\n\t"//赋值1024给ecx
        "leal 4092(%%edx),%%edi\n\t"//edx+4092得到该页面最后一个字节的地址,然后赋值给edi
        "rep ; stosl\n\t"//edi所指的内存反向清0
        "movl %%edx,%%eax\n"
        "1:"
        :"=a" (__res)
        :"0" (0),"i" (LOW_MEM),"c" (PAGING_PAGES),
        "D" (mem_map+PAGING_PAGES-1)
        :"di","cx","dx");
    if (__res >= HIGH_MEMORY)
        goto repeat;
    if (!__res && swap_out())
        goto repeat;
    return __res;

}



        其实我到现在才知道,linux 0.12的内核管理的内存就16M。也就是说,也就4个memory page,也刚好是内核代码的4个memory page

        纠结了这么久,也算是可以放下了,其实真正还是没有把最细致的地方搞懂,但是我觉得对于x86的嵌入式汇编,目前水平重要的是掌握linux设计的思想是如果实现的,尽自己最大的能力去印证代码和理论设计思想是一致。只要不是妨碍整理的理解,可以暂时先把问题放一放,以后功力好了,问题也就自然不是问题了。

下面是我收集到,关于这个get _free_page讨论的一些写帖子

http://www.oldlinux.org/oldlinux/archiver/?tid-13694.html

http://www.oldlinux.org/oldlinux/archiver/?tid-6763.html

http://www.oldlinux.org/oldlinux/archiver/?tid-6777.html

我觉得oldlinux中文站对于中国读者来说太重要了,希望赵博能够一直维护下去。力挺赵博