linux_0.11-memory.c-get_free_page

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

  __asm__ ("std ; repne ; scasb\n\t"
    "jne 1f\n\t"
    "movb $1,1(%%edi)\n\t"
    "sall $12,%%ecx\n\t"
    "addl %2,%%ecx\n\t"
    "movl %%ecx,%%edx\n\t"
    "movl $1024,%%ecx\n\t" 
    "leal 4092(%%edx),%%edi\n\t" 
    "rep ; stosl\n\t" 
    "movl %%edx,%%eax\n"
"1:": "=a" (__res): "" (0), "i" (LOW_MEM), "c" (PAGING_PAGES), "D" (mem_map + PAGING_PAGES - 1):"di", "cx",
    "dx");
  return __res;   
}

    函数作用分配一页(4K)。稍后嵌入式汇编的格式要稍作介绍一下,其实我也不是很熟悉,多练习吧。

    代码分析:

1,:register unsigned long __res asm ("ax");

定义了一个名为__res的变量,使用了register修饰符(register修饰符暗示编译程序相应的变量将被频繁地使用,如果可能的话,应将其保存在CPU的寄存器中,以加快其存储速度)。asm("ax")则表示__res的值保存在寄存器ax中。

2:"std ; repne ; scasb\n\t"

一下子来了三兄弟。std设置方向,先设置方向嘛!立场原则还是要有的(还有一个cld清除方向,指示di和si寄存器的移动方向;cld时fd=0,地址从低到高,std相反)。repne scasb联合使用,repne重复命令(按计数寄存器 ((E)CX) 中指定的次数重复执行字符串指令,或是重复到 ZF 标志不再满足指定的条件。REP(重复)、REPE(相等时重复)、REPNE(不相等时重复)、REPZ(为零时重复)及 REPNZ(不为零时重复)助记符都是可以添加到一些字符串指令中的前缀)。scasb命令比较增加,将al中的值域es:di比较,inc di或dec di(取决于DF)。

此步骤的操作变量位于”输入寄存器“。如果es:di的值不等于al则dec di,否则执行下一步。

3:"jne 1f\n\t"

如果es:di的值相均不和al相等,即均不为零。跳转到标签1f处执行,Nf表示向前标签,Nb表示向后标签,N是取值1-10的十进制数字

4:"movb $1,1(%%edi)\n\t"

找到未使用的页面置1,表示已经被分配。

5:"sall $12,%%ecx\n\t"

算法左移,相当于c中<<,intel汇编中sal命令,长整型的操作。此时ecx保存的是mem_map[i]的下标i,即相对页面数,

6:"addl %2,%%ecx\n\t"

加上低端内存的地址,即是页面的物理地址,%2即是第二个输入参数"i" (LOW_MEM)。

7:"movl %%ecx,%%edx\n\t"

将ecx寄存器的值存在edx中,即将实际物理地址存入edx中。

8:"movl $1024,%%ecx\n\t"

将1024存入ecx中,1k。一页4k,实际物理内存每项占4个字节,一共1024项。

9:"leal 4092(%%edx),%%edi\n\t"

即是edx+4092存入edi。按照4字节对齐,4096即是最后一项,4096 - 4 = 4092 = 1023*4 = (1024-1)*4

leal 8(%ebp),%eax  即把8+(%ebp)的地址送入寄存器%eax中,类似的还有其他方式:

传送a值到eax:movl a, %%eax         

传送a地址到edi: movl $a, %%edi

movl与leal的区别:

lead s,d 将&s->d

movl s, d 将s->d

10:"rep ; stosl\n\t"

重复1024次,将1024项物理地址全部存入eax中,

11:"movl %%edx,%%eax\n"

将物理地址的其实页面存入eax,interl的EABI规则eax用来保存函数的输出值。

12:"1:"
标签1,用于"jne 1f\n\t"语句跳转返回0值。

具有输入和输出参数的嵌入式汇编语句的基本格式为:

             asm("汇编语句"

                      :输出寄存器

                      :输入寄存器

                      :会被修改的寄存器);

                                                                 

                                                                                                                          常用寄存器说明

代码 说明 代码 说明
a 使用寄存器eax m 使用内存地址
b 使用寄存器ebx o 使用内存地址并可以加偏移值
c 使用寄存器ecx I 使用常数0-31
d 使用寄存器edx J 使用常数0-63
S 使用esi K 使用常数0-255
D 使用edi L 使用常数0-65535
q 使用动态分配字节可寻址寄存器(eax,ebx,ecx或edx) M 使用常数0-3
r 使用任意动态分配的寄存器 N 使用1字节常数(0-255)
g 使用通用有效的地址即可(eax,ebx,ecx,edx或内存变量) O 使用常数0-31
A 使用eax与edx联合(64位) = 输出操作数,输出值将替换前值
+ 表示操作数可读可写 &

早期汇编的操作数。表示在使用完操作数之前,内容会被修改

   

猜你喜欢

转载自blog.csdn.net/xq723310/article/details/41728777
今日推荐