linux源代码阅读笔记 get_free_page()代码分析
33 /* 34 * Get physical address of first (actually last :-) free page, and mark it 35 * used. If no free pages left, return 0. 36 */ 37 unsigned long get_free_page(void) 38 { 39 register unsigned long __res asm("ax"); 40 41 __asm__("std ; repne ; scasw\n\t" 42 "jne 1f\n\t" 43 "movw $1,2(%%edi)\n\t" //将对应页面的内存映像置1(使用) 44 "sall $12,%%ecx\n\t" //页面数*4kb=相对LOW_MEMORY的页面起始地址
45 "movl %%ecx,%%edx\n\t" //加上LOW_MEMORY形成物理起始地址 46 "addl %2,%%edx\n\t" //页面实际地址给edx 47 "movl $1024,%%ecx\n\t" 48 "leal 4092(%%edx),%%edi\n\t" //将该页面的末端地址给edi 49 "rep ; stosl\n\t" //将edi所指内存请0 50 "movl %%edx,%%eax\n" 51 "1:" 52 :"=a" (__res) 53 :"0" (0),"i" (LOW_MEM),"c" (PAGING_PAGES), 54 "D" (mem_map+PAGING_PAGES-1) 55 :"di","cx","dx"); 56 return __res; 57 }
这段代码的作用是获取一个空闲的内存页,并且返回它的地址。
首先分析,std ; repne ;scasw 这三句的作用。首先,我们参考一下下面这这篇文章:http://blog.163.com/b____d/blog/static/7491166020099115207391/
那么,我们来分析一下这个函数里含义。首先,在这三句代码执行以前,asm已经将参数装入寄存器。即:
%1(ax=0) %2(LOW_MEM) %3(cx=PAGING_PAGES) %4(edi=mem_map+PAGING_PAGES-1) 。这里注意的是,参数%1 "0" (0),”0“表示使用和输出相同的寄存器。即eax。
这样,这三句话的含义就很清楚了,它的作用是从edi指代的地址搜索,如果找到0位,则返回。直到检索了PAGING_PAGES长度。
也就是说,这三句指令在mem_map[ ]数组中寻找0位,并且该0位的index存储在edx中。
疑问:系统页面长度为4096bit,为何将页面内容清0时,赋予计数器ecx的值是1024而不是4096?
答:关注这里的清零指令,rep;stosl
STOSL指令相当于将EAX中的值保存到ES:EDI指向的地址中,若设置了EFLAGS中的方向位置位(即在STOSL指令前使用STD指令)则EDI自减4,否则(使用CLD指令)EDI自增4。
由此可见,stosl每次将4个bit清零。因此计数器赋值1024。同理,这样是为什么edi的初始值被赋予4092而不是4095