Linux-0.11核心記憶體管理get_free_page()函數分析

來源:互聯網
上載者:User

標籤:linux   asm   x86   c   核心   

/*
 *Author  : DavidLin
 *Date    : 2014-11-11pm
 *Email   : [email protected] or [email protected]
 *world   : the city of SZ, in China
 *Ver     : 000.000.001
 *history :     editor      time            do
            1)LinPeng       2014-11-11      created this file!
            2)
 */
Linux-0.11記憶體管理模組是原始碼中比較難以理解的部分,現在把筆者個人的理解發表
先發Linux-0.11核心記憶體管理get_free_page()函數分析

有時間再寫其他函數或者檔案的:)


/*
 * Get physical address of first (actually last :-) free page, and mark it
 * used. If no free pages left, return 0.

 */

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" (0),"i" (LOW_MEM),"c" (PAGING_PAGES),    "D" (mem_map+PAGING_PAGES-1)    :"di","cx","dx");return __res;}

1.函數目的:
    尋找mem_map[0..(PAGING_PAGES-1)]中的空閑項,即mem_map[i]==0的項,如果找到,
    就返回物理地址,找不到返回0
2.技巧:
    這段代碼為何不用C實現呢?
    筆者個人認為C函數會開闢棧幀,在可能會汙染任務堆棧,
    同時該函數需要經常頻繁的調用,彙編中,寄存器層級的彙編指令操作的效率比C更高:)
3.程式碼分析:
(1)register unsigned long __res asm("ax");
    __res是寄存器級變數,值儲存在ax寄存器中,就是說對__res的操作等於ax寄存器的操作,為效率考慮
(2)__asm__("std ; repne ; scasb\n\t"
    迴圈比較,找出mem_map[i]==0的頁;
    std設定DF=1,所以scasb執行遞減操作,涉及寄存器al, ecx, es:(e)di三個寄存器,在函數尾部的定義中
    :"0" (0),"i" (LOW_MEM),"c" (PAGING_PAGES),
    "D" (mem_map+PAGING_PAGES-1)
    :"di","cx","dx");
    即有

    al       = 0;                                                            //如果mem_map[i] == 0,表示為空白閑頁,否則為已指派佔用,al儲存0值,用於比較

    ecx    = PAGING_PAGES;                                 //主記憶體葉表個數

    es:di =  (mem_map+PAGING_PAGES-1);   //記憶體管理數組最後一項
    這句指令的意思是從數組mem_map[0..(PAGING_PAGES-1)]的最後一項
    mem_map[PAGING_PAGES-1]開始,比較mem_map[i]是否等於0(0值儲存在al寄存器中);
    每比較一次,es:di值減1,如果不相等,es:di值減1,即mem_map[i--],繼續比較,直到ecx == 0;
    如果相等,則跳出迴圈

    C語言實現如下:

    if(i = PAGING_PAGES-1; i != 0; i--)    {        if(0 != mem_map[i]) {            continue;   //繼續迴圈        }        else {            break;      //跳出迴圈        }    }

(3)"jne 1f\n\t"
    如果mem_map[0..(PAGING_PAGES-1)]均不等於0,
    跳轉到標籤1f處執行,Nf表示向前標籤,Nb表示向後標籤,N是取值1-10的十進位數字
(4)"movb $1,1(%%edi)\n\t"
    mem_map[i]==0是mem_map[0..(PAGING_PAGES-1)]中逆序第一個找到的等於0的目標,
    將edi的最低位置1,即mem_map[i]=1,標誌為該頁已被佔用,不是空閑位
(5)"sall $12,%%ecx\n\t"
    此時ecx儲存的是mem_map[i]的下標i,即相對頁面數,
    舉例:
        假設mem_map[0..(PAGING_PAGES-1)]最後一個參數
    mem_map[PAGING_PAGES-1] == 0,即i == (PAGING_PAGES-1),
    所以此時*ecx == PAGING_PAGES-1;
    此時相對頁面地址是4k*(PAGING_PAGES-1),
    每一頁1024個4位元組物理頁,左移12位等於4096(2的12次方),
(6)    "addl %2,%%ecx\n\t"
    加上低端記憶體位址,得到實際物理地址
    %2等於LOW_MEM,在如下語句中定義
    "0" (0),"i" (LOW_MEM),"c" (PAGING_PAGES),
    提問:
        為什麼4k*(PAGING_PAGES-1)不是實際物理地址呢?
        答案是初始化的時候如下:
        mem_map[0..(PAGING_PAGES)]是主記憶體管理數組
        管理的只是1-16M的空間,即PAGING_MEMORY = ((16-1)*1024*1024)

        不包括0-1M(0-1M,其實是0-640K已經被核心佔用)

        #define LOW_MEM 0x100000        #define PAGING_MEMORY (15*1024*1024)        #define PAGING_PAGES (PAGING_MEMORY>>12)        #define MAP_NR(addr) (((addr)-LOW_MEM)>>12)        void mem_init(long start_mem, long end_mem)        {            int i;            HIGH_MEMORY = end_mem;            for (i=0 ; i<PAGING_PAGES ; i++){                mem_map[i] = USED;            }//所有主記憶體區初始化為被佔用            i = MAP_NR(start_mem);            end_mem -= start_mem;            end_mem >>= 12;            while (end_mem-->0)            mem_map[i++]=0;        }

(7)"movl %%ecx,%%edx\n\t"
    將ecx寄存器的值儲存到edx寄存器中,即將實際物理地址儲存到edx寄存器中。
(8)"movl $1024,%%ecx\n\t"
    將1024儲存到ecx寄存器中,因為每一頁佔用4096位元組(4K),
    實際實體記憶體,每項佔用4位元組,有1024項。
(9)"leal 4092(%%edx),%%edi\n\t"
    因為按照4位元組對齊,所以每項佔用4位元組,
    取當前物理頁最後一項4096 = 4096-4 = 1023*4 = (1024-1)*4 。
    將該物理頁面的末端儲存在edi寄存器中,
    即ecx+4092處的地址儲存在edi寄存器中。
(10)"rep ; stosl\n\t"
    從ecx+4092處開始,反方向,步進4,重複1024次,
    將該物理頁1024項全部填入eax寄存器的值,
    在如下代碼定義中,eax初始化為0(al=0,eax =0,ax =0)
    :"0" (0),"i" (LOW_MEM),"c" (PAGING_PAGES),
    所以該物理頁1024項全部清零。
(11)"movl %%edx,%%eax\n"
    將該物理頁面起始地址放入eax寄存器中,
    Intel的EABI規則中,
    eax寄存器用於儲存函數返回值
(12)"1:"
    標籤1,用於"jne 1f\n\t"語句跳轉返回0值,
    注意:
        eax寄存器只在"movl %%edx,%%eax\n"中被賦值,
        eax寄存器初始值是‘0‘,如果跳轉到標籤"1:"處,
        返回值是0,表示沒有空閑物理頁。
(13):"=a" (__res)
    輸出寄存器列表,這裡只有一個,其中a表示eax寄存器
(14):"0" (0),"i" (LOW_MEM),"c" (PAGING_PAGES),
    "0"表示與上面同個位置的輸出相同的寄存器,即"0"等於輸出寄存器eax,
    即eax既是輸出寄存器,同時也是輸入寄存器,
    當然,在時間顆粒度最小的情況小,eax不能同時作為輸入或者輸出寄存器,
    只能作為輸入或者輸出寄存器;

    "i" (LOW_MEM)是%2,從輸出寄存器到輸入寄存器依次編號%0,%1,%2.....%N,
    其中"i"表示立即數,不是edi的代號,edi的代號是"D";

    "c" (PAGING_PAGES)表示將ecx寄存器存入PAGING_PAGES,
    ecx寄存器代號"c"。

(15)"D" (mem_map+PAGING_PAGES-1)
    "D"使用edi寄存器,即edi寄存器儲存的值是(mem_map+PAGING_PAGES-1)
    即%%edi = &mem_map[PAGING_PAGES-1]。
(16):"di","cx","dx");
    保留寄存器,告訴編譯器"di","cx","dx"三個寄存器已經被分配,
    在編譯器編譯中,不會將這三個寄存器分配為輸入或者輸出寄存器。
(17)return __res;
    返回__res儲存的值,
    相當於彙編的ret,隱含將eax寄存器返回,
    C語言中是顯式返回。

4.彙編指令及文法規則解析,參照Intel官方文檔《Volume 2A Instruction Set Reference (A-M)》
《Volume 2B Instruction Set Reference (N-Z)》,GNU彙編規則
(1)std:
    主要將ESI and/or EDI方向設定為遞減,對應cld(用於方向設定為遞增)
    1)Operation
    Sets the DF flag in the EFLAGS register. When the DF flag is set to 1, string operations
    decrement the index registers (ESI and/or EDI).
    This instruction’s operation is the same in non-64-bit modes and 64-bit mode.
    2)Operation
    DF -> 1;
(2)repne:
    1)Description
    Repeats a string instruction the number of times specified in the count register or
    until the indicated condition of the ZF flag is no longer met. The REP (repeat), REPE
    (repeat while equal), REPNE (repeat while not equal), REPZ (repeat while zero), and
    REPNZ (repeat while not zero) mnemonics are prefixes that can be added to one of
    the string instructions. The REP prefix can be added to the INS, OUTS, MOVS, LODS,
    and STOS instructions, and the REPE, REPNE, REPZ, and REPNZ prefixes can be
    added to the CMPS and SCAS instructions. (The REPZ and REPNZ prefixes are synonymous
    forms of the REPE and REPNE prefixes, respectively.) The behavior of the REP
    prefix is undefined when used with non-string instructions.
    The REP prefixes apply only to one string instruction at a time. To repeat a block of
    instructions, use the LOOP instruction or another looping construct. All of these
    repeat prefixes cause the associated instruction to be repeated until the count in
    register is decremented to 0. See Table 4-13.

    2)Operation

    IF AddressSize = 16        THEN            Use CX for CountReg;        ELSE IF AddressSize = 64 and REX.W used            THEN Use RCX for CountReg; FI;        ELSE            Use ECX for CountReg;    FI;    WHILE CountReg = 0        DO            Service pending interrupts (if any);            Execute associated string instruction;            CountReg <- (CountReg – 1);            IF CountReg = 0            THEN exit WHILE loop; FI;            IF (Repeat prefix is REPZ or REPE) and (ZF = 0)            or (Repeat prefix is REPNZ or REPNE) and (ZF = 1)            THEN exit WHILE loop; FI;        OD;

(3)scasb:
    GNU彙編
    在組合語言中SCASB是一條字串操作指令,源自“SCAN String Byte”的縮寫。該指令的具體操作是 :
    -------------------------------------------------------------------------------
    Code |    Mnemonic |    Description
    -------------------------------------------------------------------------------
    AE      |   SCAS m8   |    Compare AL with byte at ES:(E)DI and set status flags
    -------------------------------------------------------------------------------
    AF      |   SCAS m16 |     Compare AX with word at ES:(E)DI and set status flags
    -------------------------------------------------------------------------------
    AF      |   SCAS m32 |     Compare EAX with doubleword at ES(E)DI and set status flags
    -------------------------------------------------------------------------------
    AE      |   SCASB       |  Compare AL with byte at ES:(E)DI and set status flags
    -------------------------------------------------------------------------------
    AF      |   SCASW      |  Compare AX with word at ES:(E)DI and set status flags
    -------------------------------------------------------------------------------
    AF      |   SCASD       |  Compare EAX with doubleword at ES:(E)DI and set status flags
    -------------------------------------------------------------------------------
    計算 AL - byte of [ES:EDI] , 設定相應的標誌寄存器的值;
    修改寄存器EDI的值:如果標誌DF為0,則 inc EDI;如果DF為1,則 dec EDI。
    SCASB指令常與迴圈指令REPZ/REPNZ合用。例如,REPNZ SCASB 語句表示當 寄存器ECX>0 且 標誌寄存器ZF=0,則再執行一次SCASB指令。
    比較寄存器AL的值不相等則重複尋找的字
(4)sall
    如sall $12, %ecx.
    這個指令是演算法左移,相當於c語言中的左移操作符<<.
    intel彙編指令中的SAL,(Shit Arithmetic left).
    根據AT&T的文法規則,
    因為是一個長型的操作(ecx),
    所以在intel彙編指令sal上加一個"l",
    即轉換成sall。
(5)stosl
    STOSL指令相當於將EAX中的值儲存到ES:EDI指向的地址中,
    若設定了EFLAGS中的方向位置位(即在STOSL指令前使用STD指令)
    則EDI自減4,否則(使用CLD指令)EDI自增4。
(6)eax,ax,ah,al
        00000000 00000000 00000000 00000000
        |===============EAX===============|--32個0,4個位元組,2個字,1個雙字
                                                  |======AX=======|--16個0,2個位元組,1個字
                                                  |==AH===|-----------8個0,1個位元組
                                                                     |===AL==|---8個0,1個位元組

        EAX是32位的寄器,只是在原有的8086CPU的寄存器AX上增加了一倍的資料位元數。
        故而EAX與AX根本不可能獨立,二者是整體與部分的關係。
        對EAX直接賦值,若更改了低16位自然會改變了AX值,
        而AX又可以影響EAX整體。而AH,AL寄存器和AX之間的關係也是如此。

轉載請註明出處,謝謝:-)
林鵬!加油,向李雲和陳浩看齊!
MyBlog   : http://blog.csdn.net/linpeng12358
MyMail   : [email protected] or [email protected]
MyGithub : DavilLin1577
    welcome everybody!
    :),請指正,:)

Linux-0.11核心記憶體管理get_free_page()函數分析

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.