歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux綜合 >> Linux內核 >> Linux內核代碼之初始化內核臨時頁表

Linux內核代碼之初始化內核臨時頁表

日期:2017/2/25 10:37:34   编辑:Linux內核

漫長而黑暗的史前時代終於到了setup。在setup匯編函數中,linux通過設置cr0寄存器的PE位(從實模式切換到保護模式)完成了史前文明到現代文明的轉變。在setup時期,linux已經了解到世上可用的內存資源遠遠不止1MB。此時linux的欲望開始膨脹,最為滿足它欲望的第一步,它開始搶占內存資源的前8MB。

  初始化臨時內核頁表是在startup_32匯編語言函數中完成的。在ULK所述中,假設內核能容納於RAM的前8MB空間,然後對RAM的前8MB進行恆等映射(例如用戶地址0x00003000映射物理地址0x00003000,0xc0003000映射到物理地址0x00003000),來初始化臨時頁全局目錄swapper_pg_dir和相應的頁表。映射8MB只需要填充swapper_pg_dir中第0項,1項,768項和769項。前兩項是給用戶線性地址映射,後兩項給內核線性地址映射。用頁全局目錄裡的兩項就能對8MB映射的理由是2×1024(頁表有1024項)×4K(一頁的大小)=8M。實際上初始化內核頁表來對RAM的前8MB映射不是個硬性的規定。這取決於你的內核的配置(我認為大多數情況下是對8MB映射)。在startup_32中可以看到,對多少內存進行映射是通過pg0動態判斷的。

  linux/arch/i386/kernel/head.S

  page_pde_offset = (__PAGE_OFFSET >>20);

  /*__PAGE_OFFSET是0xc0000000,內核線性空間的起始地址。

  page_pde_offset=0xc00(十進制為3072)*/

  movl $(pg0 - __PAGE_OFFSET), %edi

  /*pg0的線性地址可以在/boot/System.map文件中找到。我的Ubuntu8.04機器上是0xc04f4000。

  減去0xc0000000就是pg0的物理地址(004f4000),放入edi中。*/

  movl $(swapper_pg_dir - __PAGE_OFFSET), %edx

  /*swapper_pg_dir的線性地址也可以在/boot/System.map文件中找到。我機器上是0xc047d000。

  減去0xc0000000就是swapper_pg_dir的物理地址(0047d000),放入edx中。*/

  movl $0x007, %eax /* 0x007 = PRESENT+RW+USER */

  /*頁目錄項和頁表項的低12位是標志位,把標志位0x007放入eax中。*/

  10:

  leal 0x007(%edi),%ecx /* Create PDE entry */

  /*第一循環時把edi指向的pg0的物理地址加上0x007放入ecx中。

  第二次循環時把edi指向的物理地址0x4f5000加上0x007放入ecx中。*/

  movl %ecx,(%edx) /* Store identity PDE entry */

  /*第一次循環時把ecx中的內容放入swapper_pg_dir的第0項裡。

  第二次循環時把ecx中的內容放入swapper_pg_dir的第1項裡。*/

  movl %ecx,page_pde_offset(%edx) /* Store kernel PDE entry */

  /*第一次循環時把ecx中的內容放入swapper_pg_dir的第768項裡。因為前面算出page_pde_offset的值為3072,而swapper_pg_dir中每項是4個字節,所以3072/4=768。

  第二次循環時把ecx中的內容放入swapper_pg_dir的第769項裡。*/

  addl $4,%edx

  /*第一次循環時,此時edx指向swapper_pg_dir的第1項。

  第二次循環時,此時edx指向swapper_pg_dir的第2項。*/

  movl $1024, %ecx

  /*為初始化1024個頁表項設置計數*/

  11:

  stosl

  /*把eax中的內容放入edi指向的物理地址中,然後edi+4。*/

  addl $0x1000,%eax

  loop 11b

  /*跳到上面的11處循環。

  第一次執行1024次後,從pg0物理地址(0x4f4000)開始存放的是0x007,0x1007,0x2007,...,0x3ff007,也就是當前能夠映射到物理地址從0x000到0x3fffff處。此時edi中的值為0x4f5000。

  第二次執行1024次後,從物理地址(0x4f5000)開始存放的是0x400007,0x401007,0x402007,...,7ff007,也就是當前能夠映射到物理地址0x000到7fffff處,正好8MB。此時edi中的值為0x4f6000。*/

  leal (INIT_MAP_BEYOND_END+0x007)(%edi),%ebp

  /*INIT_MAP_BEYOND_END的值為128k,在此文件中的一個宏定義。把edi指向的物理地址加上128k加上0x007放入edp中。*/

  cmpl %ebp,%eax

  /*在第一次循環中ebp中的值為0x515007,eax中的值為0x400007小於0x515007。當前所映射到的最大物理地址為0x3fffff沒有包含0x515007,所以沒有映射完。

  在第二次循環中ebp中的值為0x516007,eax中的值為0x800007大於0x516007。當前所映射到的最大物理地址為0x7fffff包含了0x516007,所以8MB物理地址映射完畢。*/

  jb 10b

  /*第一次循環做完時跳到上面的10處繼續循環

  第二次循環做完時跳出循環。*/

  movl %edi,(init_pg_tables_end - __PAGE_OFFSET)

  /*最後把0x4f6000放入init_pg_tables_end 所表示的物理地址中。也在/boot/System.map中。*/

  此時的linux胃口越來越大,8MB的資源已不能滿足它的胃口了。它的黑手開始慢慢伸向896MB以下的內存了。

Copyright © Linux教程網 All Rights Reserved