《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中文站对于中国读者来说太重要了,希望赵博能够一直维护下去。力挺赵博

《linux 内核完全剖析》get_free_page(void),布布扣,bubuko.com

时间: 2024-08-03 17:15:19

《linux 内核完全剖析》get_free_page(void)的相关文章

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

最近在做项目开发时用到了MySql数据库,在看了一些有关MySql的文章后,很快就上手使用了.在使用的过程中还是出现了一些问题,因为使用的是绿色免安装版的MySql所以在配置的时候出现了一些问题,该篇文章就主要针对MySql绿色版的配置及其使用进行讨论. 一.MySql概述 MySql数据库是有瑞典MySql AB公司开发,现在该公司被Oracle收购属于Oracle所有.同SQL Server类似,它也是基于关系型数据库的数据库管理系统,在Web应用方面MySQL是最好的RDBMS之一,因为它

《linux 内核完全剖析》chapter 13 内存管理 (不含swap.c)

内存管理(memory.c 和swap.s 部分) "倒着看" 先看memory management,很明显,前面各种阻力,都是因为涉及内存管理.不先看这个,我估计前面看了也是白看 我估算着理论打基础砸了差不多一个星期的时间在memory management上面了...感觉很有收获,是时候用实践(code)印证理论了! <modern operating system>讲内存管理那一章 http://blog.csdn.net/cinmyheart/article/de

《linux 内核完全剖析》 fork.c 代码分析笔记

fork.c 代码分析笔记 verifiy_area long last_pid=0; //全局变量,用来记录目前最大的pid数值 void verify_area(void * addr,int size) // addr 是虚拟地址 ,size是需要写入的字节大小 { unsigned long start; start = (unsigned long) addr; //把地址强制类型转换之后,赋值给start size += start & 0xfff; //取addr在当前虚拟地址中4

《linux 内核完全剖析》 exit.c 代码分析笔记

exit.c 代码分析笔记 release 释放进程的函数release() 主要根据指定进程的任务数据结构指针,在任务数组中删除指定的进程指针,释放相关内存页,并立刻让内核重新调度进程的运行. void release(struct task_struct * p) //释放p指向的进程 { int i; if (!p) //常规检测p是否为0 return; if (p == current) { //不能把自己给释放了 printk("task releasing itself\n\r&q

《linux 内核完全剖析》sched.c sched.h 代码分析笔记

sched.c sched.h 代码分析笔记 首先上header file sched.h #ifndef _SCHED_H #define _SCHED_H #define HZ 100 #define NR_TASKS 64 #define TASK_SIZE 0x04000000 #define LIBRARY_SIZE 0x00400000 #if (TASK_SIZE & 0x3fffff) #error "TASK_SIZE must be multiple of 4M&qu

《linux 内核完全剖析》 signal.c 代码分析笔记

signal.c 代码分析笔记 sgetmask int sys_sgetmask()// 获取当前进程阻塞的信号 { returncurrent->blocked; } sys_ssetmask int sys_ssetmask(int newmask) //设置当前进程阻塞信号,确保SIGKILL 和SIGSTOP不被阻塞 { int old=current->blocked; current->blocked= newmask & ~(1<<(SIGKILL-1

《linux 内核完全剖析》 sys.c 代码分析

sys.c 代码分析 setregid /* * This is done BSD-style, with no consideration of the saved gid, except * that if you set the effective gid, it sets the saved gid too. This * makes it possible for a setgid program to completely drop its privileges, * which i

《linux 内核完全剖析》 keyboard.S 部分代码分析(key_map)

keyboard.S 部分代码分析(key_map) keyboard中间有这么一段,我一开始没看明白,究竟啥意思 key_map: .byte 0,27 .ascii "1234567890-=" .byte 127,9 .ascii "qwertyuiop[]" .byte 13,0 .ascii "asdfghjkl;'" .byte '`,0 .ascii "\\zxcvbnm,./" .byte 0,'*,0,32

《linux 内核完全剖析》 笔记 由逻辑地址转换成线性地址代码分析

一开始由这段代码引发的纠结 get_base(current->ldt[1]) 下面是各个相关的代码,摘自不同的header files... current是指向当前task的指针 struct desc_struct ldt[3]; struct desc_struct { unsigned long a,b; } ; #define _get_base(addr) ({unsigned long __base; __asm__("movb %3,%%dh\n\t" &quo