記憶體空間和使用者空間

來源:互聯網
上載者:User

A.INTRODUCTION     (32位系統)


            

     Linux 簡化了分段機制,使得虛擬位址(邏輯地址)與線性地址總是一致,因此,Linux的虛擬位址空間也為0~4G(2^32)。

      Linux核心將這4G位元組的空間分為兩部分。將最高的 1G位元組(從虛擬位址0xC0000000到0xFFFFFFFF),供核心使用,稱為“核心空間”。而將較低的3G位元組(從虛擬位址 0x00000000到0xBFFFFFFF),供各個進程使用,稱為“使用者空間“。因為每個進程可以通過系統調用進入核心,因此,Linux核心由系統 內的所有進程共用。於是,從具體進程的角度來看,每個進程可以擁有4G位元組的虛擬空間。


*進程定址空間0~4G  
*進程在使用者態只能訪問0~3G,只有進入核心態才能訪問3G~4G  
*進程通過系統調用進入核心態
*每個進程虛擬空間的3G~4G部分是相同的  
*進程從使用者態進入核心態不會引起CR3的改變但會引起堆棧的改變




  












    關於虛擬核心空間(邏輯地址)到物理空間的映射:

           當核心模組代碼或線程訪問記憶體時,代碼中的記憶體位址都為邏輯地址,而對應到真正的實體記憶體地址,需要地址一對一的映射,如邏輯地址0xc0000003對應的物理地址為0×3,0xc0000004對應的物理地址為0×4,… …,邏輯地址與物理地址對應的關係為

物理地址 = 邏輯地址 – 0xC0000000

邏輯地址 實體記憶體地址
0xc0000000 0×0
0xc0000001 0×1
0xc0000002 0×2
0xc0000003 0×3
… …
0xe0000000 0×20000000
… …
0xffffffff 0×40000000 ??

      對核心空間來說,其地址映射是很簡單 的線性映射,0xC0000000就是物理地址與線性地址之間的位移量,在Linux代碼中就叫做PAGE_OFFSET。

B.outline


一、Linux使用者空間與核心空間

       refer: http://blog.chinaunix.net/uid-15007890-id-3415331.html
二、Linux進程地址空間的一步步探究        refer:http://soft.chinabyte.com/os/51/12324551.shtml




C.contents


一、Linux使用者空間與核心空間

      Linux 作業系統和驅動程式運行在核心空間,應用程式運行在使用者空間,兩者不能簡單地使用指標傳遞資料,因為Linux使用的虛擬記憶體機制,使用者空間的資料可能被換出,當核心空間使用使用者空間指標時,對應的資料可能不在記憶體中。

      我們知道,linux作業系統每個進程的地址空間都是獨立的,其實這裡的獨立說得是物理空間上得獨立。

1. Linux核心地址映射模型

x86 CPU採用了段頁式地址映射模型。進程代碼中的地址為邏輯地址,經過段頁式地址映射後,才真正訪問實體記憶體。

段頁式機制如下圖。

 

2.Linux核心地址空間劃分

通常32位Linux核心地址空間劃分0~3G為使用者空間,3~4G為核心空間。注意這裡是32位核心地址空間劃分,64位核心地址空間劃分是不同的。

 

3.Linux核心高端記憶體的由來

當核心模組代碼或線程訪問記憶體時,代碼中的記憶體位址都為邏輯地址,而對應到真正的實體記憶體地址,需要地址一對一的映射,如邏輯地址0xc0000003對應的物理地址為0×3,0xc0000004對應的物理地址為0×4,… …,邏輯地址與物理地址對應的關係為

物理地址 = 邏輯地址 – 0xC0000000

邏輯地址 實體記憶體地址
0xc0000000 0×0
0xc0000001 0×1
0xc0000002 0×2
0xc0000003 0×3
… …
0xe0000000 0×20000000
… …
0xffffffff 0×40000000 ??

假 設按照上述簡單的地址映射關係,那麼核心邏輯地址空間訪問為0xc0000000 ~ 0xffffffff,那麼對應的實體記憶體範圍就為0×0 ~ 0×40000000,即只能訪問1G實體記憶體。若機器中安裝8G實體記憶體,那麼核心就只能訪問前1G實體記憶體,後面7G實體記憶體將會無法訪問,因為核心 的地址空間已經全部映射到實體記憶體位址範圍0×0 ~ 0×40000000。即使安裝了8G實體記憶體,那麼物理地址為0×40000001的記憶體,核心該怎麼去訪問呢。代碼中必須要有記憶體邏輯地址 的,0xc0000000 ~ 0xffffffff的地址空間已經被用完了,所以無法訪問物理地址0×40000000以後的記憶體。

顯 然不能將核心地址空間0xc0000000 ~ 0xfffffff全部用來簡單的地址映射。因此x86架構中將核心地址空間劃分三部分:ZONE_DMA、ZONE_NORMAL和 ZONE_HIGHMEM。ZONE_HIGHMEM即為高端記憶體,這就是記憶體高端記憶體概念的由來。


在x86結構中,三種類型的地區如下:

ZONE_DMA        記憶體開始的16MB

ZONE_NORMAL       16MB~896MB

ZONE_HIGHMEM       896MB ~ 結束

 

4.Linux核心高端記憶體的理解

前 面我們解釋了高端記憶體的由來。 Linux將核心地址空間劃分為三部分ZONE_DMA、ZONE_NORMAL和ZONE_HIGHMEM,高端記憶體HIGH_MEM地址空間範圍為 0xF8000000 ~ 0xFFFFFFFF(896MB~1024MB)。那麼如核心是如何藉助128MB高端記憶體位址空間是如何?訪問可以所有實體記憶體。

當核心想訪問高於896MB物理地址記憶體時,從0xF8000000 ~ 0xFFFFFFFF地址空間範圍內找一段相應大小閒置邏輯地址空間,借用一會。借用這段邏輯地址空間,建立映射到想訪問的那段實體記憶體(即填充核心PTE頁面表),臨時用一會,用完後歸還。這樣別人也可以借用這段地址空間訪問其他實體記憶體,實現了使用有限的地址空間,訪問所有所有實體記憶體。如下圖。

例 如核心想訪問2G開始的一段大小為1MB的實體記憶體,即物理位址範圍為0×80000000 ~ 0x800FFFFF。訪問之前先找到一段1MB大小的空閑地址空間,假設找到的空閑地址空間為0xF8700000 ~ 0xF87FFFFF,用這1MB的邏輯地址空間映射到物理地址空間0×80000000 ~ 0x800FFFFF的記憶體。映射關係如下:

邏輯地址 實體記憶體地址
0xF8700000 0×80000000
0xF8700001 0×80000001
0xF8700002 0×80000002
… …
0xF87FFFFF 0x800FFFFF

當核心訪問完0×80000000 ~ 0x800FFFFF實體記憶體後,就將0xF8700000 ~ 0xF87FFFFF核心線性空間釋放。這樣其他進程或代碼也可以使用0xF8700000 ~ 0xF87FFFFF這段地址訪問其他實體記憶體。

從上面的描述,我們可以知道高端記憶體的最基本思想:借一段地址空間,建立臨時地址映射,用完後釋放,達到這段地址空間可以迴圈使用,訪問所有實體記憶體。

看到這裡,不禁有人會問:萬一有核心進程或模組一直佔用某段邏輯地址空間不釋放,怎麼辦。若真的出現的這種情況,則核心的高端記憶體位址空間越來越緊張,若都被佔用不釋放,則沒有建立映射到實體記憶體都無法訪問了。

在 香港尖沙咀有些寫字樓,洗手間很少且有門鎖的。客戶要去洗手間的話,可以向前台拿鑰匙,方便完後,把鑰匙歸還到前台。這樣雖然只有一個洗 手間,但可以滿足所有客戶去洗手間的需求。要是某個客戶一直佔用洗手間、鑰匙不歸還,那麼其他客戶都無法上洗手間了。Linux核心高端記憶體管理的思想類 似。


5.Linux核心高端記憶體的劃分
核心將高端記憶體劃分為3部分:VMALLOC_START~VMALLOC_END、KMAP_BASE~FIXADDR_START和FIXADDR_START~4G。


      對 於高端記憶體,可以通過 alloc_page() 或者其它函數獲得對應的 page,但是要想訪問實際實體記憶體,還得把 page 轉為線性地址才行(為什麼。想想 MMU 是如何訪問實體記憶體的),也就是說,我們需要為高端記憶體對應的 page 找一個線性空間,這個過程稱為高端記憶體映射。

對應高端記憶體的3部分,高端記憶體映射有三種方式:
映射到”核心動態映射空間”(noncontiguous memory allocation)
這種方式很簡單,因為通過 vmalloc() ,在”核心動態映射空間”申請記憶體的時候,就可能從高端記憶體獲得頁面(參看 vmalloc 的實現),因此說高端記憶體有可能映射到”核心動態映射空間”中。


持久核心映射(permanent kernel mapping)
如果是通過 alloc_page() 獲得了高端記憶體對應的 page,如何給它找個線性空間。
核心專門為此留出一塊線性空間,從 PKMAP_BASE 到 FIXADDR_START ,用於映射高端記憶體。在 2.6核心上,這個位址範圍是 4G-8M 到 4G-4M 之間。這個空間起叫”核心永久映射空間”或者”永久核心映射空間”。這個空間和其它空間使用同樣的頁目錄表,對於核心來說,就是 swapper_pg_dir,對普通進程來說,通過 CR3 寄存器指向。通常情況下,這個空間是 4M 大小,因此僅僅需要一個頁表即可,核心通過來 pkmap_page_table 尋找這個頁表。通過 kmap(),可以把一個 page 映射到這個空間來。由於這個空間是 4M 大小,最多能同時映射 1024 個 page。因此,對於不使用的的 page,及應該時從這個空間釋放掉(也就是解除映射關係),通過 kunmap() ,可以把一個 page 對應的線性地址從這個空間釋放出來。


臨時映射(temporary kernel mapping)
核心在 FIXADDR_START 到 FIXADDR_TOP 之間保留了一些線性空間用於特殊需求。這個空間稱為”固定映射空間”在這個空間中,有一部分用於高端記憶體的臨時映射。

這塊空間具有如下特點:
(1)每個 CPU 佔用一塊空間
(2)在每個 CPU 佔用的那塊空間中,又分為多個小空間,每個小空間大小是 1 個 page,每個小空間用於一個目的,這些目的定義在 kmap_types.h 中的 km_type 中。

當要進行一次臨時映射的時候,需要指定映射的目的,根據映射目的,可以找到對應的小空間,然後把這個空間的地址作為映射地址。這意味著一次臨時映射會導致以前的映射被覆蓋。通過 kmap_atomic() 可實現臨時映射。


6. 常見問題:

1、使用者空間(進程)是否有高端記憶體概念。

使用者進程沒有高端記憶體概念。只有在核心空間才存在高端記憶體。使用者進程最多隻可以訪問3G實體記憶體,而核心進程可以訪問所有實體記憶體。

 

2、64位核心中有高端記憶體嗎。

目前現實中,64位Linux核心不存在高端記憶體,因為64位核心可以支援超過512GB記憶體。若機器安裝的實體記憶體超過核心地址空間範圍,就會存在高端記憶體。

 

3、使用者進程能訪問多少實體記憶體。核心代碼能訪問多少實體記憶體。

32位系統使用者進程最大可以訪問3GB,核心代碼可以訪問所有實體記憶體。

64位系統使用者進程最大可以訪問超過512GB,核心代碼可以訪問所有實體記憶體。

 

4、高端記憶體和物理地址、邏輯地址、線性地址的關係。

高端記憶體只和邏輯地址有關係,和邏輯地址、物理地址沒有直接關係。

 

5、為什麼不把所有的地址空間都分配給核心。

若把所有地址空間都給記憶體,那麼使用者進程怎麼使用記憶體。怎麼保證核心使用記憶體和使用者進程不起衝突。


(1)讓我們忽略Linux對段式記憶體映射的支援。 在保護模式下,我們知道無論CPU運行於使用者態還是核心態,CPU執行程式所訪問的地址都是虛擬位址,MMU 必須通過讀取控制寄存器CR3中的值作為當前頁面目錄的指標,進而根據分頁記憶體映射機制(參看相關文檔)將該虛擬位址轉換為真正的物理地址才能讓CPU真 正的訪問到物理地址。

(2)對於32位的Linux,其每一個進程都有4G的定址空間,但當一個進程訪問其虛擬記憶體空間中的某個地址時又是怎樣實現不與其它進程的虛擬空間混淆 的呢。每個進程都有其自身的頁面目錄PGD,Linux將該目錄的指標存放在與進程對應的記憶體結構task_struct.(struct mm_struct)mm->pgd中。每當一個進程被調度(schedule())即將進入運行態時,Linux核心都要用該進程的PGD指標設 置CR3(switch_mm())。

(3)當建立一個新的進程時,都要為新進程建立一個新的頁面目錄PGD,並從核心的頁面目錄swapper_pg_dir中複製核心區間頁面目錄項至建立進程頁面目錄PGD的相應位置,具體過程如下:
do_fork() –> copy_mm() –> mm_init() –> pgd_alloc() –> set_pgd_fast() –> get_pgd_slow() –> memcpy(&PGD + USER_PTRS_PER_PGD, swapper_pg_dir + USER_PTRS_PER_PGD, (PTRS_PER_PGD - USER_PTRS_PER_PGD) * sizeof(pgd_t))
這樣一來,每個進程的頁面目錄就分成了兩部分,第一部分為“使用者空間”,用來映射其整個進程空間(0x0000 0000-0xBFFF FFFF)即3G位元組的虛擬位址;第二部分為“系統空間”,用來映射(0xC000 0000-0xFFFF FFFF)1G位元組的虛擬位址。可以看出Linux系統中每個進程的頁面目錄的第二部分是相同的,所以從進程的角度來看,每個進程有4G位元組的虛擬空間, 較低的3G位元組是自己的使用者空間,最高的1G位元組則為與所有進程以及核心共用的系統空間。

(4)現在假設我們有如下一個情景:
在進程A中通過系統調用sethostname(const char *name,seze_t len)設定電腦在網路中的“主機名稱”.
在該情景中我們勢必涉及到從使用者空間向核心空間傳遞資料的問題,name是使用者空間中的地址,它要通過系統調用設定到核心中的某個地址中。讓我們看看這個 過程中的一些細節問題:系統調用的具體實現是將系統調用的參數依次存入寄存器ebx,ecx,edx,esi,edi(最多5個參數,該情景有兩個 name和len),接著將系統調用號存入寄存器eax,然後通過中斷指令“int 80”使進程A進入系統空間。由於進程的CPU運行層級小於等於為系統調用設定的陷阱門的准入層級3,所以可以暢通無阻的進入系統空間去執行為int 80設定的函數指標system_call()。由於system_call()屬於核心空間,其運行層級DPL為0,CPU要將堆棧切換到核心堆棧,即
進程A的系統空間堆棧。我們知道核心為建立進程建立task_struct結構時,共分配了兩個連續的頁面,即8K的大小,並將底部約1k的大小用於 task_struct(如#define alloc_task_struct() ((struct task_struct *) __get_free_pages(GFP_KERNEL,1))),而其餘部分記憶體用於系統空間的堆棧空間,即當從使用者空間轉入系統空間時,堆棧指標 esp變成了(alloc_task_struct()+8192),這也是為什麼系統空間通常用宏定義current(參看其實現)擷取當前進程的
task_struct地址的原因。每次在進程從使用者空間進入系統空間之初,系統堆棧就已經被依次壓入使用者堆棧SS、使用者堆棧指標ESP、EFLAGS、 使用者空間CS、EIP,接著system_call()將eax壓入,再接著調用SAVE_ALL依次壓入ES、DS、EAX、EBP、EDI、ESI、 EDX、ECX、EBX,然後調用sys_call_table+4*%EAX,本情景為sys_sethostname()。

(5)在sys_sethostname()中,經過一些保護考慮後,調用copy_from_user(to,from,n),其中to指向核心空間 system_utsname.nodename,譬如0xE625A000,from指向使用者空間譬如0x8010FE00。現在進程A進入了核心,在 系統空間中運行,MMU根據其PGD將虛擬位址完成到物理地址的映射,最終完成從使用者空間到系統空間資料的複製。準備複製之前核心先要確定使用者空間地址和 長度的合法性,至於從該使用者空間地址開始的某個長度的整個區間是否已經映射並不去檢查,如果區間內某個地址未映射或讀寫權限等問題出現時,則視為壞地址,
就產生一個頁面異常,讓頁面異常服務程式處理。過程如 下:copy_from_user()->generic_copy_from_user()->access_ok()+__copy_user_zeroing().



二、Linux進程地址空間的一步步探究 2012-05-03 00:00中國IT實驗室佚名

關鍵字:Linux

  我們知道,在32位機器上linux作業系統中的進程的地址空間大小是4G,其中0-3G是使用者空間,3G-4G是核心空間。其實,這個4G的地址空間是不存在的,也就是我們所說的虛擬記憶體空間。

   (我們知道,linux作業系統每個進程的地址空間都是獨立的,其實這裡的獨立說得是物理空間上得獨立。)

  那虛擬記憶體空間是什麼呢,它與實際實體記憶體空間又是怎樣對應的呢,為什麼有了虛擬記憶體技術,我們就能運行比實際實體記憶體大的應用程式,它是怎麼做到的呢。()

  呵呵,這一切的一切都是個迷呀,下面我們就一步一步解開心中的謎團吧!

  我們來看看,當我們寫好一個應用程式,編譯後它都有什麼東東。

  例如:

  用命令size a.out會得到:

  其中text是放的是代碼,data放的是初始化過的全域變數或靜態變數,bss放的是未初始化的全域變數或靜態變數

  由於曆史原因,C程式一直由下列幾部分組成:

  A.本文段。這是由cpu執行的機器指令部分。通常,本文段是可共用的,所以即使是經常執行的程式(如文本編輯程式、C編譯器、shell等)在儲存空間中也只需要有一個副本,另外,本文段常常是唯讀,以防止程式由於意外事故而修改器自身的指令。

  B.初始化資料區段。通常將此段稱為資料區段,它包含了程式中需賦初值的變數。例如,C程式中任何函數之外的說明:

  int maxcount = 99;(全域變數)

  C.非初始化資料區段。通常將此段稱為bss段,這一名稱來源於早期組譯工具的一個操作,意思是”block started by symbol”,在程式開始執行之前,核心將此段初始化為0。函數外的說明:

  long sum[1000];

  使此變數存放在非初始化資料區段中。

  D.棧。自動變數以及每次函數調用時所需儲存的資訊都存放在此段中。每次函數調用時,其返回地址、以及調用者的環境資訊(例如某些機器寄存器)都存放在棧中。然後,新被調用的函數在棧上為其自動和臨時變數分配儲存空間。通過以這種方式使用棧,C函數可以遞迴調用。

  E.堆。通常在堆中進行動態儲存裝置分配。由於曆史上形成的慣例,堆位於非初始化資料區段頂和棧底之間。

  從上圖我們看到棧空間是下增長的,堆空間是從下增長的,他們會會碰頭呀。一般不會,因為他們之間間隔很大,如:

  #include

  #include

  int bss_var;

  int data_var0 = 1;

  int main()

  {

  printf(“Test location:\n”);

  printf(“\tAddress of main(Code Segment):%p\n”,main);

  printf(“_____________________________________\n”);

  int stack_var0 = 2;

  printf(“Stack location:\n”);

  printf(“\tInitial end of stack:%p\n”,&stack_var0);

  int stack_var1 = 3;

  printf(“\tNew end of stack:%p\n”,&stack_var1);

  printf(“_____________________________________\n”);

  printf(“Data location:\n”);

  printf(“\tAddress of data_var(Data Segment):%p\n”,&data_var0);

  static int data_var1 = 4;

  printf(“\tNew end of data_var(Data Segment):%p\n”,&data_var1);

  printf(“_____________________________________\n”);

  printf(“BSS location:\n”);

  printf(“\tAddress of bss_var:%p\n”,&bss_var);

  printf(“_____________________________________\n”);

  printf(“Heap location:\n”);

  char *p = (char *)malloc(10);

  printf(“\tAddress of head_var:%p\n”,p);

  return 0;

  }

  運行結果如下:

  呵呵,這裡我們看到地址了,這個地址是虛擬位址,這些地址時怎麼來的呢。其實在我們編譯的時候,

  這些地址就已經確定了,如下圖中紅線。

  也就是說,我們不論我們運行a.out程式多少次這些地址都是一樣的。我們知道,linux作業系統每個進程的地址空間都是獨立的,其實這裡的獨立說得是物理空間上得獨立。那相同的虛擬位址,不同的物理地址,他們之間是怎樣聯絡起來的呢。我們繼續探究…

  在linux作業系統中,每個進程都通過一個task_struct的結構體描敘,每個進程的地址空間都通過一個mm_struct描敘,c語言中的每個段空間都通過vm_area_struct表示,他們關係如下 :

  當運行一個程式時,作業系統需要建立一個進程,這個進程和程式之間都幹了些什麼呢。

  當一個程式被執行時,該程式的內容必須被放到進程的虛擬位址空間,對於可執行程式的共用庫也是如此。可執行程式並非真正讀到實體記憶體中,而只是連結到進程的虛擬記憶體中。

  當一個可執行程式映射到進程虛擬位址空間時,一組vm_area_struct資料結構將被產生。每個vm_area_struct資料結構表示可執行印象的一部分;是可執行代碼,或是初始化的資料,以及未初始化的資料等。

  linux作業系統是通過sys_exec對可執行檔進行映射以及讀取的,有如下幾步:

  1.建立一組vm_area_struct

  2.圈定一個虛擬使用者空間,將其起始結束位址(elf段中已設定好)儲存到vm_start和vm_end中。

  3.將磁碟file控制代碼儲存在vm_file中

  4.將對應段在磁碟file中的位移值(elf段中已設定好)儲存在vm_pgoff中;

  5.將操作該磁碟file的磁碟操作函數儲存在vm_ops中

  注意:這裡沒有對應 的頁目錄表項建立頁表,更不存在設定頁表項了。

  假設現在程式中有一條指令需要讀取上面vm_start–vm_end之間的某內容

  例如:mov [0x08000011],%eax,那麼將會執行如下序列:

  1.cpu依據CR3(current->pgd)找到0x08000011地址對應的pgd[i],由於該pgd[i]內容保持為初始化狀態即為0,導致cpu異常.

  2.do_page_fault被調用,在該函數中,為pgd[i]在記憶體中分配一個頁表,並讓該表項指向它,如下圖所示:

  注意:這裡i為0x08000011高10位,j為其中間10位,此時pt表項全部為0(pte[j]也為0);

  3.為pte[j]分配一個真正的實體記憶體頁面,依據vm_area_struct中的vm_file、vm_pgoff和vm_ops,調用filemap_nopage將磁碟file中vm_pgoff位移處的內容讀入到該物理頁面中,如下圖所示:

  ①。分配實體記憶體頁面;

  ②。從磁碟檔案中將內容讀取到實體記憶體頁面中

  從上面我們可以知道,在進程建立的過程中,程式內容被映射到進程的虛擬記憶體空間,為了讓一個很大的程式在有限的實體記憶體空間運行,我們可以把這個程式的開始部分先載入到實體記憶體空間運行,因為作業系統處理的是進程的虛擬位址,如果在進行虛擬到物理地址的轉換工程中,發現物理地址不存在時,這個時候就會發生缺頁異常(nopage),接著作業系統就會把磁碟上還沒有載入到記憶體中的資料載入到實體記憶體中,對應的進程頁表進行更新。也許你會問,如果此時實體記憶體滿了,作業系統將如何處理。

  下面我們看看linux作業系統是如何處理的:

  如果一個進程想將一個虛擬頁裝入實體記憶體,而又沒有可使用的空閑物理頁,作業系統就必須淘汰實體記憶體中的其他頁來為此頁騰出空間。

  在linux作業系統中,物理頁的描敘如下:

  struct mem_map

  {

  1.本頁使用計數,當該頁被許多進程共用時計數將大於1.

  2.age描敘本頁的年齡,用來判斷該頁是否為淘汰或交換的好候選

  3.map_nr描敘物理頁的頁幀號

  }

  如果從實體記憶體中被淘汰的頁來自於一個映像或資料檔案,並且還沒有被寫過,則該頁不必儲存,它可以丟掉。如果有進程在需要該頁時就可以把它從映像或資料檔案中取回記憶體。

  然而,如果該頁被修改過,作業系統必須保留該頁的內容以便晚些時候在被訪問。這種頁稱為”髒(dirty)頁”,當它被從記憶體中刪除時,將被儲存在一個稱為分頁檔的特殊檔案中。

  相對於處理器和實體記憶體的速度,訪問分頁檔要很長時間,作業系統必須在將頁寫到磁碟以及再次使用時取回記憶體的問題上花費心機。

  如果用來決定哪一頁被淘汰或交換的演算法不夠高效的話,就可能出現稱為”抖動”的情況。在這種情況下,頁面總是被寫到磁碟又讀回來,作業系統忙於此而不能進行真正的工作。

  linux使用”最近最少使用(Least Recently Used ,LRU)”頁面調度技巧來公平地選擇哪個頁可以從系統中刪除。這種設計系統中每個頁都有一個”年齡”,年齡隨頁面被訪問而改變。頁面被訪問越多它越年輕;被訪問越少越老。年老的頁是用於交換的最佳候選頁。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.