歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux基礎 >> Linux教程

Linux物理內存探測

linux在被bootloader加載到內存後, cpu最初執行的linux內核代碼是/header.S文件中的start_of_setup函數,這個函數在做了一些准備工作後會跳轉到boot目下文件main.c的main函數執行,在這個main函數中我們可以第一次看到與內存管理相關的代碼,這段代碼調用detect_memeory()函數檢測系統物理內存

在header.S中執行下面匯編代碼:

  1. start_of_setup:  
  2.        .....  
  3. # Jump to C code (should not return)   
  4.     calll   main  
  5.        .....  
跳到boot目錄下的main.c文件中
  1. void main(void)  
  2. {  
  3.         ......  
  4.     /* Detect memory layout */  
  5.     detect_memory();/*內存探測函數*/  
  6.     ......  
  7. }  

 
  1. int detect_memory(void)  
  2. {  
  3.     int err = -1;  
  4.   
  5.     if (detect_memory_e820() > 0)  
  6.         err = 0;  
  7.   
  8.     if (!detect_memory_e801())  
  9.         err = 0;  
  10.   
  11.     if (!detect_memory_88())  
  12.         err = 0;  
  13.   
  14.     return err;  
  15. }  
由上面的代碼可知,linux內核會分別嘗試調用detect_memory_e820()、detcct_memory_e801()、detect_memory_88()獲得系統物理內存布局,這3個函數內部其實都會以內聯匯編的形式調用bios中斷以取得內存信息,該中斷調用形式為int 0x15,同時調用前分別把AX寄存器設置為0xe820h、0xe801h、0x88h,關於0x15號中斷有興趣的可以去查詢相關手冊。下面分析detect_memory_e820()的代碼,其它代碼基本一樣。
  1. #define SMAP    0x534d4150  /* ASCII "SMAP" */   
  2. /*由於歷史原因,一些i/o設備也會占據一部分內存 
  3. 物理地址空間,因此系統可以使用的物理內存空 
  4. 間是不連續的,系統內存被分成了很多段,每個段 
  5. 的屬性也是不一樣的。int 0x15 查詢物理內存時每次 
  6. 返回一個內存段的信息,因此要想返回系統中所有 
  7. 的物理內存,我們必須以迭代的方式去查詢。 
  8. detect_memory_e820()函數把int 0x15放到一個do-while循環裡, 
  9. 每次得到的一個內存段放到struct e820entry裡,而 
  10. struct e820entry的結構正是e820返回結果的結構!而像 
  11. 其它啟動時獲得的結果一樣,最終都會被放到 
  12. boot_params裡,e820被放到了 boot_params.e820_map。 
  13. */  
  14. static int detect_memory_e820(void)  
  15. {  
  16.     int count = 0;/*用於記錄已檢測到的物理內存數目*/  
  17.     struct biosregs ireg, oreg;  
  18.     struct e820entry *desc = boot_params.e820_map;  
  19.     static struct e820entry buf; /* static so it is zeroed */  
  20.   
  21.     initregs(&ireg);/*初始化ireg中的相關寄存器*/  
  22.     ireg.ax  = 0xe820;  
  23.     ireg.cx  = sizeof buf;/*e820entry數據結構大小*/  
  24.     ireg.edx = SMAP;/*標識*/  
  25.     ireg.di  = (size_t)&buf;/*int15返回值的存放處*/  
  26.   
  27.     /* 
  28.      * Note: at least one BIOS is known which assumes that the 
  29.      * buffer pointed to by one e820 call is the same one as 
  30.      * the previous call, and only changes modified fields.  Therefore, 
  31.      * we use a temporary buffer and copy the results entry by entry. 
  32.      * 
  33.      * This routine deliberately does not try to account for 
  34.      * ACPI 3+ extended attributes.  This is because there are 
  35.      * BIOSes in the field which report zero for the valid bit for 
  36.      * all ranges, and we don't currently make any use of the 
  37.      * other attribute bits.  Revisit this if we see the extended 
  38.      * attribute bits deployed in a meaningful way in the future. 
  39.      */  
  40.   
  41.     do {  
  42.         /*在執行這條內聯匯編語句時輸入的參數有: 
  43.         eax寄存器=0xe820 
  44.         dx寄存器=’SMAP’ 
  45.         edi寄存器=desc 
  46.         ebx寄存器=next 
  47.         ecx寄存器=size 
  48.          
  49.         返回給c語言代碼的參數有: 
  50.         id=eax寄存器 
  51.         rr=edx寄存器 
  52.         ext=ebx寄存器 
  53.         size=ecx寄存器 
  54.         desc指向的內存地址在執行0x15中斷調用時被設置 
  55.         */  
  56.         intcall(0x15, &ireg, &oreg);  
  57.         /*選擇下一個*/  
  58.         ireg.ebx = oreg.ebx; /* for next iteration... */  
  59.   
  60.         /* BIOSes which terminate the chain with CF = 1 as opposed 
  61.            to %ebx = 0 don't always report the SMAP signature on 
  62.            the final, failing, probe. */  
  63.         if (oreg.eflags & X86_EFLAGS_CF)  
  64.             break;  
  65.   
  66.         /* Some BIOSes stop returning SMAP in the middle of 
  67.            the search loop.  We don't know exactly how the BIOS 
  68.            screwed up the map at that point, we might have a 
  69.            partial map, the full map, or complete garbage, so 
  70.            just return failure. */  
  71.         if (oreg.eax != SMAP) {  
  72.             count = 0;  
  73.             break;  
  74.         }  
  75.   
  76.         *desc++ = buf;/*將buf賦值給desc*/  
  77.         count++;/*探測數加一*/  
  78.     }   
  79.     while (ireg.ebx && count < ARRAY_SIZE(boot_params.e820_map));  
  80.     /*將內存塊數保持到變量中*/  
  81.     return boot_params.e820_entries = count;  
  82. }  
Copyright © Linux教程網 All Rights Reserved