linux內存原理
❶ linux的內核是由bootloader裝載到內存中的
linux的內核的確是由bootloader裝載到內存中的。linux的bootloader有2個部分組成:bootstrap和uboot。所以更准確點的說法是:linux的內核是由uboot裝載到內存中的。內核文件本身是存放在硬碟的文件系統中,這句話就是錯的。內核和文件系統是分開存儲的。uboot讀取kernel到內存是從kernel開始存儲的地址開始讀取的,而讀取開始位置和讀取大小,是由環境變數決定的。所以這個時候不需要文件系統的。
給你張圖片,便於理解吧。這張圖片是bootstrap、uboot、環境變數、kernel、文件系統在nandflash裡面的存儲分布。
其中rootfs.jfss2就是文件系統。
❷ linux swap分區原理
swap介紹
Swap,即交換區,除了安裝Linux的時候,有多少人關心過它呢?其實,Swap的調整對Linux伺服器,特別是Web伺服器的性能至關重要。通過調整Swap,有時可以越過系統性能瓶頸,節省系統升級費用。
本文內容包括:
Swap基本原理
突破128M Swap限制
Swap配置對性能的影響
Swap性能監視
有關Swap操作的系統命令
Swap基本原理
Swap的原理是一個較復雜的問題,需要大量的篇幅來說明。在這里只作簡單的介紹,在以後的文章中將和大家詳細討論Swap實現的細節。
眾所周知,現代操作系統都實現了「虛擬內存」這一技術,不但在功能上突破了物理內存的限制,使程序可以操縱大於實際物理內存的空間,更重要的是,「虛擬內存」是隔離每個進程的安全保護網,使每個進程都不受其它程序的干擾。
Swap空間的作用可簡單描述為:當系統的物理內存不夠用的時候,就需要將物理內存中的一部分空間釋放出來,以供當前運行的程序使用。那些被釋放的空間可能來自一些很長時間沒有什麼操作的程序,這些被釋放的空間被臨時保存到Swap空間中,等到那些程序要運行時,再從Swap中恢復保存的數據到內存中。這樣,系統總是在物理內存不夠時,才進行Swap交換。
計算機用戶會經常遇這種現象。例如,在使用Windows系統時,可以同時運行多個程序,當你切換到一個很長時間沒有理會的程序時,會聽到硬碟「嘩嘩」直響。這是因為這個程序的內存被那些頻繁運行的程序給「偷走」了,放到了Swap區中。因此,一旦此程序被放置到前端,它就會從Swap區取回自己的數據,將其放進內存,然後接著運行。
需要說明一點,並不是所有從物理內存中交換出來的數據都會被放到Swap中(如果這樣的話,Swap就會不堪重負),有相當一部分數據被直接交換到文件系統。例如,有的程序會打開一些文件,對文件進行讀寫(其實每個程序都至少要打開一個文件,那就是運行程序本身),當需要將這些程序的內存空間交換出去時,就沒有必要將文件部分的數據放到Swap空間中了,而可以直接將其放到文件里去。如果是讀文件操作,那麼內存數據被直接釋放,不需要交換出來,因為下次需要時,可直接從文件系統恢復;如果是寫文件,只需要將變化的數據保存到文件中,以便恢復。但是那些用malloc和new函數生成的對象的數據則不同,它們需要Swap空間,因為它們在文件系統中沒有相應的「儲備」文件,因此被稱作「匿名」(Anonymous)內存數據。這類數據還包括堆棧中的一些狀態和變數數據等。所以說,Swap空間是「匿名」數據的交換空間。
突破128M Swap限制
經常看到有些Linux(國內漢化版)安裝手冊上有這樣的說明:Swap空間不能超過128M。為什麼會有這種說法?在說明「128M」這個數字的來歷之前,先給問題一個回答:現在根本不存在128M的限制!現在的限制是2G!
Swap空間是分頁的,每一頁的大小和內存頁的大小一樣,方便Swap空間和內存之間的數據交換。舊版本的Linux實現Swap空間時,用Swap空間的第一頁作為所有Swap空間頁的一個「位映射」(Bit map)。這就是說第一頁的每一位,都對應著一頁Swap空間。如果這一位是1,表示此頁Swap可用;如果是0,表示此頁是壞塊,不能使用。這么說來,第一個Swap映射位應該是0,因為,第一頁Swap是映射頁。另外,最後10個映射位也被佔用,用來表示Swap的版本(原來的版本是Swap_space ,現在的版本是swapspace2)。那麼,如果說一頁的大小為s,這種Swap的實現方法共能管理「8 * ( s - 10 ) - 1」個Swap頁。對於i386系統來說s=4096,則空間大小共為133890048,如果認為1 MB=2^20 Byte的話,大小正好為128M。
之所以這樣來實現Swap空間的管理,是要防止Swap空間中有壞塊。如果系統檢查到Swap中有壞塊,則在相應的位映射上標記上0,表示此頁不可用。這樣在使用Swap時,不至於用到壞塊,而使系統產生錯誤。
現在的系統設計者認為:
現在硬碟質量很好,壞塊很少。
就算有,也不多,只需要將壞塊羅列出來,而不需要為每一頁建立映射。
如果有很多壞塊,就不應該將此硬碟作為Swap空間使用。
- vmstat 3
- procs memory swap io system cpu
- r b w swpd free buff cache si so bi bo in cs us sy id 0 0 0 0 93880 3304 19372 0 0 10 2 131 10 0 0 99
- 0 0 0 0 93880 3304 19372 0 0 0 0 109 8 0 0 100
- 0 0 0 0 93880 3304 19372 0 0 0 0 112 6 0 0 100
- …………
- 1234567
procs下的w
它表示當前(三秒鍾之內)需要釋放內存、交換出去的進程數量。memory下的swpd
它表示使用的Swap空間的大小。Swap下的si,so
si表示當前(三秒鍾之內)每秒交換回內存(Swap in)的總量,單位為kbytes;so表示當前(三秒鍾之內)每秒交換出內存(Swap out)的總量,單位為kbytes。- ` swapon -s ` Filename Type Size Used Priority
- /dev/hda9 partition 361420 0 3
- 123
於是,現在的Linux取消了位映射的方法,也就取消了128M的限制。直接用地址訪問,限制為2G。
Swap配置對性能的影響
分配太多的Swap空間會浪費磁碟空間,而Swap空間太少,則系統會發生錯誤。
如果系統的物理內存用光了,系統就會跑得很慢,但仍能運行;如果Swap空間用光了,那麼系統就會發生錯誤。例如,Web伺服器能根據不同的請求數量衍生出多個服務進程(或線程),如果Swap空間用完,則服務進程無法啟動,通常會出現「application is out of memory」的錯誤,嚴重時會造成服務進程的死鎖。因此Swap空間的分配是很重要的。
通常情況下,Swap空間應大於或等於物理內存的大小,最小不應小於64M,通常Swap空間的大小應是物理內存的2-2.5倍。但根據不同的應用,應有不同的配置:如果是小的桌面系統,則只需要較小的Swap空間,而大的伺服器系統則視情況不同需要不同大小的Swap空間。特別是資料庫伺服器和Web伺服器,隨著訪問量的增加,對Swap空間的要求也會增加,具體配置參見各伺服器產品的說明。
另外,Swap分區的數量對性能也有很大的影響。因為Swap交換的操作是磁碟IO的操作,如果有多個Swap交換區,Swap空間的分配會以輪流的方式操作於所有的Swap,這樣會大大均衡IO的負載,加快Swap交換的速度。如果只有一個交換區,所有的交換操作會使交換區變得很忙,使系統大多數時間處於等待狀態,效率很低。用性能監視工具就會發現,此時的CPU並不很忙,而系統卻慢。這說明,瓶頸在IO上,依靠提高CPU的速度是解決不了問題的。
系統性能監視
Swap空間的分配固然很重要,而系統運行時的性能監控卻更加有價值。通過性能監視工具,可以檢查系統的各項性能指標,找到系統性能的瓶頸。本文只介紹一下在Solaris下和Swap相關的一些命令和用途。
最常用的是Vmstat命令(在大多數Unix平台下都有這樣一些命令),此命令可以查看大多數性能指標。
例如:
命令說明:
vmstat 後面的參數指定了性能指標捕獲的時間間隔。3表示每三秒鍾捕獲一次。第一行數據不用看,沒有價值,它僅反映開機以來的平均性能。從第二行開始,反映每三秒鍾之內的系統性能指標。這些性能指標中和Swap有關的包括以下幾項:
以上的指標數量越大,表示系統越忙。這些指標所表現的系統繁忙程度,與系統具體的配置有關。系統管理員應該在平時系統正常運行時,記下這些指標的數值,在系統發生問題的時候,再進行比較,就會很快發現問題,並制定本系統正常運行的標准指標值,以供性能監控使用。
另外,使用Swapon-s也能簡單地查看當前Swap資源的使用情況。例如:
能夠方便地看出Swap空間的已用和未用資源的大小。
應該使Swap負載保持在30%以下,這樣才能保證系統的良好性能。
有關Swap操作的系統命令
增加Swap空間,分以下幾步:
1)成為超級用戶
$su - root
2)創建Swap文件
# dd if=/dev/zero of=swapfile bs=1024 count=65536
創建一個有連續空間的交換文件。
3)激活Swap文件
#/usr/sbin/swapon swapfile
swapfile指的是上一步創建的交換文件。 4)現在新加的Swap文件已經起作用了,但系統重新啟動以後,並不會記住前幾步的操作。因此要在/etc/fstab文件中記錄文件的名字,和Swap類型,如:
/path/swapfile none Swap sw,pri=3 0 0
5)檢驗Swap文件是否加上
/usr/sbin/swapon -s
刪除多餘的Swap空間。
1)成為超級用戶
2)使用Swapoff命令收回Swap空間。
#/usr/sbin/swapoff swapfile
3)編輯/etc/fstab文件,去掉此Swap文件的實體。
4)從文件系統中回收此文件。
#rm swapfile
5)當然,如果此Swap空間不是一個文件,而是一個分區,則需創建一個新的文件系統,再掛接到原來的文件系統上。
❸ linux內存頁式管理的映射原理
內存頁式管理,不是linux,也不是window的,而是CPU保護模式下的一個功能,其分頁基址,由cr3寄存器保存,這個寄存器,保存了1024個分頁目錄首的地址,每個分頁目錄保存1024個分頁表的首地址,,最終由分頁表指向一個實際的物理地址(每個表指向4096位元組的首地址) 這個內容涉及到CPU的保護模式,很深的,如果你想真正的了解,可以加我QQ 419938049,我可以跟你大概的講講,雖然我也不怎樣。。。呵呵
❹ Linux虛擬地址,地址映射表,物理內存與虛擬內存的關系
內存是一種存儲設備,現在一般是DDR SDRAM,地址是用來標記內存的數據的。在操作系統中物理內存指實際的DDR SDRAM,而虛擬內存指的是在硬碟中的緩存,windows中是頁面文件,linux中是swap分區。cpu產生的地址是虛擬地址也可以稱作有效地址,而在cpu外地址線上的信號稱作實際地址或物理地址。這2類地址有某種對應關系,由操作系統管理。如果是x86架構的話,物理地址和虛擬地址中間還有線性地址的概念。
❺ Linux的內存管理機制是什麼樣的
,程序是直接運行在物理內存上的。換句話說,就是程序在運行的過程中訪問的都是物理地址。如果這個系統只運行一個程序
❻ LINUX內存映射問題
Linux的內存模型,一般為:
地址
作用
說明
>=0xc000 0000
內核虛擬存儲器
用戶代碼不可見區域
<0xc000 0000
Stack(用戶棧)
ESP指向棧頂
↓
↑
空閑內存
>=0x4000 0000
文件映射區
<0x4000 0000
↑
空閑內存
Heap(運行時堆)
通過brk/sbrk系統調用擴大堆,向上增長。
.data、.bss(讀寫段)
從可執行文件中載入
>=0x0804 8000(0x00008000 for arm linux)
.init、.text、.rodata(只讀段)
從可執行文件中載入
<0x0804 8000(0x00008000 for arm linux)
保留區域
運行一個測試程序,觀察其結果:
#include <stdio.h>
int main(int argc, char* argv[])
{
int first = 0;
int* p0 = malloc(1024);
int* p1 = malloc(1024 * 1024);
int* p2 = malloc(512 * 1024 * 1024 );
int* p3 = malloc(1024 * 1024 * 1024 );
printf("main=%p print=%p\n", main, printf);
printf("first=%p\n", &first);
printf("p0=%p p1=%p p2=%p p3=%p\n", p0, p1, p2, p3);
getchar();
return 0;
}
運行後,輸出結果為:
main=0x8048404 print=0x8048324
first=0xbfcd1264
p0=0x9253008 p1=0xb7ec0008 p2=0x97ebf008 p3=0x57ebe008
my pc (fc5)輸出結果如下:
main=0x80483f4 print=0x8048324
first=0xbf848660
p0=0x9ab2008 p1=0xb7e38008 p2=0x97e37008 p3=(nil)
arm-linux輸出如下結果:
main=0x8528 print=0x8404
first=0xbec9fe10
p0=0x11008 p1=0x4005a008 p2=(nil) p3=(nil)
main和print兩個函數是代碼段(.text)的,其地址符合表一的描述。
first是第一個臨時變數,由於在first之前還有一些環境變數,它的值並非0xbfffffff,而是0xbfcd1264,這是正常的。
p0是在堆中分配的,其地址小於0x4000 0000,這也是正常的。
但p1和p2也是在堆中分配的,而其地址竟大於0x4000 0000,與表一描述不符。
原因在於:運行時堆的位置與內存管理演算法相關,也就是與malloc的實現相關。關於內存管理演算法的問題,我們在後繼文章中有詳細描述,這里只作簡要說明。在glibc實現的內存管理演算法中,Malloc小塊內存是在小於0x4000 0000的內存中分配的,通過brk/sbrk不斷向上擴展,而分配大塊內存,malloc直接通過系統調用mmap實現,分配得到的地址在文件映射區,所以其地址大於0x4000 0000。
❼ linux基本原理
計算機體系結構:運算器 控制器 存儲器 輸入設備 輸出設備
詳解:存儲即內存:編址的存儲單元。即每一個存儲單元在都有一個編址。
控制器告訴運算器加數在存儲器的哪個存儲單元。
poll:(拉的機制)CPU不停地查看誰發生的電信號
interrupt:(中斷,即硬體通知機制)敲完鍵盤:鍵盤會通知CPU,CPU就來看看鍵盤幹了什麼事。
CPU通過控制晶元知道是哪個設備發出的信號。一根線上有不同的設備。
為了充分利用CPU,多任務利用,(想第一件事10秒,想第二件事10秒,然後接著想第一件事。那麼第一件事的10秒記憶存儲在內存中)。那麼就需要劃分了,cpu被切分為一個個slice。內存分成多個段。這都是由操作系統完成的。
32位操作系統:最多物理內存2^
程序:放在操作系統上,只要不刪就一直存在。執行入口,進程:是有生命周期的,一定時間過後就消失。
.庫:一堆的程序,自己不能獨立執行,只提供調用介面,可被程序調用執行。
操作系統:有了操作系統之後,任何一個進程要跟硬體打交道,都得經過操作系統。操作系統通過最底層的調用:system call(系統調用)。然後封裝之後,形成了庫。
shell:人機交互介面
❽ linux內存管理的特點
什麼是虛擬內存?
Linux支持虛擬內存(virtual memory),虛擬內存是指使用磁碟當作RAM的擴展,這樣可用的內存的大小就相應地增大了。內核會將暫時不用的內存塊的內容寫到硬碟上,這樣一來,這塊內存就可用於其它目的。當需要用到原始的內容時,它們被重新讀入內存。這些操作對用戶來說是完全透明的;Linux下運行的程序只是看到有大量的內存可供使用而並沒有注意到時不時它們的一部分是駐留在硬碟上的。當然,讀寫硬碟要比直接使用真實內存慢得多(要慢數千倍),所以程序就不會象一直在內存中運行的那樣快。用作虛擬內存的硬碟部分被稱為交換空間(swap space)。
Linux能夠使用文件系統中的一個常規文件或一個獨立的分區作為交換空間。交換分區要快一些,但是很容易改變交換文件的大小(也就無需重分區整個硬碟,並且可以從臨時分區中安裝任何東西)。當你知道你需要多大的交換空間時,你應該使用交換分區,但是如果你不能確定的話,你可以首先使用一個交換文件,然後使用一陣子系統,你就可以感覺到要有多大的交換空間,此時,當你能夠確信它的大小時就創建一個交換分區。
你應該知道,Linux允許同時使用幾個交換分區以及/或者交換文件。這意味著如果你只是偶爾地另外需要一個交換空間時,你可以在當時設置一個額外的交換文件,而不是一直分配這個交換空間。
操作系統術語注釋:計算機科學常常將交換[swapping](將整個進程寫到交換空間)與頁面調度[paging](在某個時刻,僅僅固定大小的幾千位元組寫到交換空間內)加以區別。頁面調度通常更有效,這也是Linux的做法,但是傳統的Linux術語卻指的是交換。
創建交換空間
一個交換文件是一個普通的文件;對內核來說一點也不特殊。對內核有關系的是它不能有孔,並且它是用mkswap來准備的。而且,它必須駐留在一個本地硬碟上,它不能由於實現的原因而駐留在一個通過NFS載入的文件系統中。
關於孔是重要的。交換文件保留了磁碟空間,以至於內核能夠快速地交換出頁面而無需做分配磁碟扇區給文件時所要做的一些事。內核僅僅是使用早已分配給交換文件的任何扇區而已。因為文件中的一個孔意味著沒有磁碟扇區分配(給該文件的孔的相應部分),對內核來說就不能使用這類有孔的文件。
創建無孔的交換文件的一個好方法是通過下列命令:
$ dd if=/dev/zero of=/extra-swap bs=1024 count=1024 \
上面/extra-swap是交換文件的名字,大小由count=後面的數值給出。大小最好是4的倍數,因為內核寫出的內存頁面(memory pages)大小是4千位元組。如果大小不是4的倍數,最後幾千位元組就用不上了。
一個交換分區也並沒有什麼特別的。你可以象創建其它分區一樣地創建它;唯一的區別在於它是作為一個原始的分區使用的,也即,它不包括任何的文件系統。將交換分區標記為類型82(Linux交換分區)是個好主意;這將使得分區的列表更清楚,盡管對內核來說並不是一定要這樣的。
在創建了一個交換文件或一個交換分區以後,你必須在它的開頭部分寫上一個簽名;這個簽名中包括了一些由內核使用的管理信息。這是用\cmd{mkswap}命令來做到的,用法如下:
$ mkswap /extra-swap 1024
Setting up swapspace, size = 1044480 bytes
請注意此時交換空間還沒有被使用:它已存在,但內核還沒有用它作為虛擬內存。你必須非常小心地使用mkswap,因為它不檢查這個文件或分區是否已被別人使用。你可以非常容易地使用mkswap來覆蓋重要的文件以及分區!幸運的是,僅僅在安裝系統時,你才需要使用mkswap。
Linux內存管理程序限制每個交換空間最大約為127MB(由於各種技術上的原因,實際的限制大小為(4096-10) * 8 * 4096 = 133890048$ 位元組,或127.6875兆位元組)。然而,你可以同時使用多至16個交換空間,總容量幾乎達2GB。
交換空間的使用
一個已初始化的交換空間是使用命令swapon投入正式使用的。該命令告訴內核這個交換空間可以被使用了。到交換空間的路徑是作為參數給出的,所以,開始在一個臨時交換文件上使用交換的命令如下:
$ swapon /extra-swap
通過把交換空間列入/etc/fstab文件中就能被自動地使用了。
/dev/hda8 none swap sw 0 0
/swapfile none swap sw 0 0
啟動描述文件會執行命令swapon –a,這個命令會啟動列於/etc/fstab中的所有交換空間。因此,swapon命令通常僅用於需要有外加的交換空間時。
你可以用free命令監視交換空間的使用情況。它將給出已使用了多少的交換空間。
total used free shared buffers
Swap: 32452 6684 25768
輸出的第一行(Mem:)顯示出物理內存的使用情況。總和(total)列中並沒有顯示出被內核使用的內存,它通常將近一兆位元組。已用列(used column)顯示出已用內存的總和(第二行沒有把緩沖算進來)。空閑列(free column)顯示了所有未被使用的空閑內存。共享列(shared column)顯示出了被幾個進程共享的內存的大小;共享的內存越多,情況就越好。緩存列(buffer column)顯示出了當前磁碟緩存的大小。已緩沖列(cached column)顯示出了已使用的緩存的大小。
最後一行(Swap:)顯示出了與交換空間相應的信息。如果這一行的數值都是零,表示你的交換空間沒有被擊活。
也可通過用top命令來獲得同樣的信息,或者使用proc文件系統中的文件/proc/meminfo 。通常要取得指定交換空間的使用情況是困難的。
可以使用命令swapoff來移去一個交換空間。通常沒有必要這樣做,但臨時交換空間除外。一般,在交換空間中的頁面首先被換入內存;如果此時沒有足夠的物理內存來容納它們又將被交換出來(到其他的交換空間中)。如果沒有足夠的虛擬內存來容納所有這些頁面,Linux就會波動而不正常;但經過一段較長的時間Linux會恢復,但此時系統已不可用了。在移去一個交換空間之前,你應該檢查(例如,用free)是否有足夠的空閑內存。
任何由swapon –a而自動被使用的所有交換空間都能夠用swapoff –a命令移去;該命令參考/etc/fstab文件來確定移去什麼。任何手工設置使用的交換空間將始終可以被使用。
有時,盡管有許多的空閑內存,仍然會有許多的交換空間正被使用。這是有可能發生的,例如如果在某一時刻有進行交換的必要,但後來一個佔用很多物理內存的大進程結束並釋放內存時。被交換出的數據並不會自動地交換進內存,除非有這個需要時。此時物理內存會在一段時間內保持空閑狀態。對此並沒有什麼可擔心的,但是知道了是怎麼一回事我們也就放心了。
許多操作系統使用了虛擬內存的方法。因為它們僅在運行時才需要交換空間,以即決不會在同一時間使用交換空間,因此,除了當前正在運行的操作系統的交換空間,其它的就是一種浪費。所以讓它們共享一個交換空間將會更有效率。這是可能的,但需要有一定的了解。在HOWTO技巧文檔中含有如何實現這種做法的一些建議。
有些人會對你說需要用物理內存的兩倍容量來分配交換空間,但這是不對的。下面是合適的做法:
。估計你的總內存需求。這是某一時刻你所需要的最大的內存容量,也就是在同一時刻你想運行的所有程序所需內存的總和。通過同時運行所有的程序你可以做到這一點。
例如,如果你要運行X,你將給它分配大約8MB內存,gcc需要幾兆位元組(有些文件要求異呼尋常的大量的內存量,多至幾十兆位元組,但通常約4兆位元組應該夠了),等等。內核本身要用大約1兆位元組、普通的shell以及其它一些工具可能需要幾百千位元組(就說總和要1兆位元組吧)。並不需要進行精確的計算,粗率的估計也就足夠了,但你必須考慮到最壞的情況。
注意,如果會有幾個人同時使用這個系統,他們都將消耗內存。然而,如果兩個人同時運行一個程序,內存消耗的總量並不是翻倍,因為代碼頁以及共享的庫只存在一份。
Free以及ps命令對估計所需的內存容量是很有幫助的。
對第一步中的估計放寬一些。這是因為對程序在內存中佔用多少的估計通常是不準的,因為你很可能忘掉幾個你要運行的程序,以及,確信你還要有一些多餘的空間用於以防萬一。這需幾兆位元組就夠了。(多分配總比少分配交換空間要好,但並不需要過分這樣以至於使用整個硬碟,因為不用的交換空間是浪費的空間;參見後面的有關增加交換空間。)同樣,因為處理數值更好做,你可以將容量值加大到整數兆位元組。
基於上面的計算,你就知道了你將需要總和為多少的內存。所以,為了分配交換空間,你僅需從所需總內存量中減去實際物理內存的容量,你就知道了你需要多少的交換空間。(在某些UNIX版本中,你還需要為物理內存的映像分配空間,所以第二步中算出的總量正是你所需要的交換空間的容量,而無需再做上述中的減法運算了。)
如果你計算出的交換空間容量遠遠大於你的物理內存(大於兩倍以上),你通常需要再買些內存來,否則的話,系統的性能將非常低。
有幾個交換空間是個好主意,即使計算指出你一個都不需要。Linux系統常常動不動就使用交換空間,以保持盡可能多的空閑物理內存。即使並沒有什麼事情需要內存,Linux也會交換出暫時不用的內存頁面。這可以避免等待交換所需的時間:當磁碟閑著,就可以提前做好交換。
可以將交換空間分散在幾個硬碟之上。針對相關磁碟的速度以及對磁碟的訪問模式,這樣做可以提高性能。你可能想實驗幾個方案,但是你要認識到這些實驗常常是非常困難的。不要相信其中一個方案比另一個好的說法,因為並不總是這樣的。
高速緩沖
與訪問(真正的)的內存相比,磁碟[3]的讀寫是很慢的。另外,在相應較短的時間內多次讀磁碟同樣的部分也是常有的事。例如,某人也許首先閱讀了一段e-mail消息,然後為了答復又將這段消息讀入編輯器中,然後又在將這個消息拷貝到文件夾中時,使得郵件程序又一次讀入它。或者考慮一下在一個有著許多用戶的系統中ls命令會被使用多少次。通過將信息從磁碟上僅讀入一次並將其存於內存中,除了第一次讀以外,可以加快所有其它讀的速度。這叫作磁碟緩沖(disk buffering),被用作此目的的內存稱為高速緩沖(buffer cache)。
不幸的是,由於內存是一種有限而又不充足的資源,高速緩沖不可能做的很大(它不可能包容要用到的所有數據)。當緩沖充滿了數據時,其中最長時間不用的數據將被舍棄以騰出內存空間用於新的數據。
對寫磁碟操作來說磁碟緩沖技術同樣有效。一方面,被寫入磁碟的數據常常會很快地又被讀出(例如,原代碼文件被保存到一個文件中,又被編譯器讀入),所以將要被寫的數據放入緩沖中是個好主意。另一方面,通過將數據放入緩沖中,而不是將其立刻寫入磁碟,程序可以加快運行的速度。以後,寫的操作可以在後台完成,而不會拖延程序的執行。
大多數操作系統都有高速緩沖(盡管可能稱呼不同),但是並不是都遵守上面的原理。有些是直接寫(write-through):數據將被立刻寫入磁碟(當然,數據也被放入緩存中)。如果寫操作是在以後做的,那麼該緩存被稱為後台寫(write-back)。後台寫比直接寫更有效,但也容易出錯:如果機器崩潰,或者突然掉電,或者是軟盤在緩沖中等待寫的數據被寫入軟盤之前被從驅動器中取走,緩沖中改變過的數據就被丟失了。如果仍未被寫入的數據含有重要的薄記信息,這甚至可能意味著文件系統(如果有的話)已不完整。
由於上述原因,在使用適當的關閉過程之前,絕對不要關掉電源(見第六章),不要在卸載(如果已被載入)之前將軟盤從驅動器中取出來,也不要在任何正在使用軟盤的程序指示出完成了軟盤操作並且軟盤燈熄滅之前將軟盤取出來。sync命令傾空(flushes)緩沖,也即,強迫所有未被寫的數據寫入磁碟,可用以確定所有的寫操作都已完成。在傳統的UNIX系統中,有一個叫做update的程序運行於後台,每隔30秒做一次sync操作,因此通常無需手工使用sync命令了。Linux另外有一個後台程序,bdflush,這個程序執行更頻繁的但不是全面的同步操作,以避免有時sync的大量磁碟I/O操作所帶來的磁碟的突然凍結。
在Linux中,bdflush是由update啟動的。通常沒有理由來擔心此事,但如果由於某些原因bdflush進程死掉了,內核會對此作出警告,此時你就要手工地啟動它了(/sbin/update)。
緩存(cache)實際並不是緩沖文件的,而是緩沖塊的,塊是磁碟I/O操作的最小單元(在Linux中,它們通常是1KB)。這樣,目錄、超級塊、其它文件系統的薄記數據以及非文件系統的磁碟數據都可以被緩沖了。
緩沖的效力主要是由它的大小決定的。緩沖大小太小的話等於沒用:它只能容納一點數據,因此在被重用時,所有緩沖的數據都將被傾空。實際的大小依賴於數據讀寫的頻次、相同數據被訪問的頻率。只有用實驗的方法才能知道。
如果緩存有固定的大小,那麼緩存太大了也不好,因為這會使得空閑的內存太小而導致進行交換操作(這同樣是慢的)。為了最有效地使用實際內存,Linux自動地使用所有空閑的內存作為高速緩沖,當程序需要更多的內存時,它也會自動地減小緩沖的大小。
在Linux中,你不需要為使用緩沖做任何事情,它是完全自動處理的。除了上面所提到的有關按照適當的步驟來關機和取出軟盤,你不用擔心它。
❾ linux中使用了什麼內存管理方法,為什麼
「事實勝於雄辯」,我們用一個小例子(原形取自《User-Level Memory Management》)來展示上面所講的各種內存區的差別與位置。
進程的地址空間對應的描述結構是「內存描述符結構」,它表示進程的全部地址空間,——包含了和進程地址空間有關的全部信息,其中當然包含進程的內存區域。
進程內存的分配與回收
創建進程fork()、程序載入execve()、映射文件mmap()、動態內存分配malloc()/brk()等進程相關操作都需要分配內存給進程。不過這時進程申請和獲得的還不是實際內存,而是虛擬內存,准確的說是「內存區域」。進程對內存區域的分配最終都會歸結到do_mmap()函數上來(brk調用被單獨以系統調用實現,不用do_mmap()),
內核使用do_mmap()函數創建一個新的線性地址區間。但是說該函數創建了一個新VMA並不非常准確,因為如果創建的地址區間和一個已經存在的地址區間相鄰,並且它們具有相同的訪問許可權的話,那麼兩個區間將合並為一個。如果不能合並,那麼就確實需要創建一個新的VMA了。但無論哪種情況,do_mmap()函數都會將一個地址區間加入到進程的地址空間中--無論是擴展已存在的內存區域還是創建一個新的區域。
同樣,釋放一個內存區域應使用函數do_ummap(),它會銷毀對應的內存區域。
如何由虛變實!
從上面已經看到進程所能直接操作的地址都為虛擬地址。當進程需要內存時,從內核獲得的僅僅是虛擬的內存區域,而不是實際的物理地址,進程並沒有獲得物理內存(物理頁面——頁的概念請大家參考硬體基礎一章),獲得的僅僅是對一個新的線性地址區間的使用權。實際的物理內存只有當進程真的去訪問新獲取的虛擬地址時,才會由「請求頁機制」產生「缺頁」異常,從而進入分配實際頁面的常式。
該異常是虛擬內存機制賴以存在的基本保證——它會告訴內核去真正為進程分配物理頁,並建立對應的頁表,這之後虛擬地址才實實在在地映射到了系統的物理內存上。(當然,如果頁被換出到磁碟,也會產生缺頁異常,不過這時不用再建立頁表了)
這種請求頁機制把頁面的分配推遲到不能再推遲為止,並不急於把所有的事情都一次做完(這種思想有點像設計模式中的代理模式(proxy))。之所以能這么做是利用了內存訪問的「局部性原理」,請求頁帶來的好處是節約了空閑內存,提高了系統的吞吐率。要想更清楚地了解請求頁機制,可以看看《深入理解linux內核》一書。
這里我們需要說明在內存區域結構上的nopage操作。當訪問的進程虛擬內存並未真正分配頁面時,該操作便被調用來分配實際的物理頁,並為該頁建立頁表項。在最後的例子中我們會演示如何使用該方法。
系統物理內存管理
雖然應用程序操作的對象是映射到物理內存之上的虛擬內存,但是處理器直接操作的卻是物理內存。所以當應用程序訪問一個虛擬地址時,首先必須將虛擬地址轉化成物理地址,然後處理器才能解析地址訪問請求。地址的轉換工作需要通過查詢頁表才能完成,概括地講,地址轉換需要將虛擬地址分段,使每段虛地址都作為一個索引指向頁表,而頁表項則指向下一級別的頁表或者指向最終的物理頁面。
每個進程都有自己的頁表。進程描述符的pgd域指向的就是進程的頁全局目錄。下面我們借用《linux設備驅動程序》中的一幅圖大致看看進程地址空間到物理頁之間的轉換關系。
上面的過程說起來簡單,做起來難呀。因為在虛擬地址映射到頁之前必須先分配物理頁——也就是說必須先從內核中獲取空閑頁,並建立頁表。下面我們介紹一下內核管理物理內存的機制。
物理內存管理(頁管理)
Linux內核管理物理內存是通過分頁機制實現的,它將整個內存劃分成無數個4k(在i386體系結構中)大小的頁,從而分配和回收內存的基本單位便是內存頁了。利用分頁管理有助於靈活分配內存地址,因為分配時不必要求必須有大塊的連續內存[3],系統可以東一頁、西一頁的湊出所需要的內存供進程使用。雖然如此,但是實際上系統使用內存時還是傾向於分配連續的內存塊,因為分配連續內存時,頁表不需要更改,因此能降低TLB的刷新率(頻繁刷新會在很大程度上降低訪問速度)。
鑒於上述需求,內核分配物理頁面時為了盡量減少不連續情況,採用了「夥伴」關系來管理空閑頁面。夥伴關系分配演算法大家應該不陌生——幾乎所有操作系統方面的書都會提到,我們不去詳細說它了,如果不明白可以參看有關資料。這里只需要大家明白Linux中空閑頁面的組織和管理利用了夥伴關系,因此空閑頁面分配時也需要遵循夥伴關系,最小單位只能是2的冪倍頁面大小。內核中分配空閑頁面的基本函數是get_free_page/get_free_pages,它們或是分配單頁或是分配指定的頁面(2、4、8…512頁)。
注意:get_free_page是在內核中分配內存,不同於malloc在用戶空間中分配,malloc利用堆動態分配,實際上是調用brk()系統調用,該調用的作用是擴大或縮小進程堆空間(它會修改進程的brk域)。如果現有的內存區域不夠容納堆空間,則會以頁面大小的倍數為單位,擴張或收縮對應的內存區域,但brk值並非以頁面大小為倍數修改,而是按實際請求修改。因此Malloc在用戶空間分配內存可以以位元組為單位分配,但內核在內部仍然會是以頁為單位分配的。
另外,需要提及的是,物理頁在系統中由頁結構structpage描述,系統中所有的頁面都存儲在數組mem_map[]中,可以通過該數組找到系統中的每一頁(空閑或非空閑)。而其中的空閑頁面則可由上述提到的以夥伴關系組織的空閑頁鏈表(free_area[MAX_ORDER])來索引。
內核內存使用
Slab
所謂尺有所長,寸有所短。以頁為最小單位分配內存對於內核管理系統中的物理內存來說的確比較方便,但內核自身最常使用的內存卻往往是很小(遠遠小於一頁)的內存塊——比如存放文件描述符、進程描述符、虛擬內存區域描述符等行為所需的內存都不足一頁。這些用來存放描述符的內存相比頁面而言,就好比是麵包屑與麵包。一個整頁中可以聚集多個這些小塊內存;而且這些小塊內存塊也和麵包屑一樣頻繁地生成/銷毀。
為了滿足內核對這種小內存塊的需要,Linux系統採用了一種被稱為slab分配器的技術。Slab分配器的實現相當復雜,但原理不難,其核心思想就是「存儲池[4]」的運用。內存片段(小塊內存)被看作對象,當被使用完後,並不直接釋放而是被緩存到「存儲池」里,留做下次使用,這無疑避免了頻繁創建與銷毀對象所帶來的額外負載。
Slab技術不但避免了內存內部分片(下文將解釋)帶來的不便(引入Slab分配器的主要目的是為了減少對夥伴系統分配演算法的調用次數——頻繁分配和回收必然會導致內存碎片——難以找到大塊連續的可用內存),而且可以很好地利用硬體緩存提高訪問速度。
Slab並非是脫離夥伴關系而獨立存在的一種內存分配方式,slab仍然是建立在頁面基礎之上,換句話說,Slab將頁面(來自於夥伴關系管理的空閑頁面鏈表)撕碎成眾多小內存塊以供分配,slab中的對象分配和銷毀使用kmem_cache_alloc與kmem_cache_free。
Kmalloc
Slab分配器不僅僅只用來存放內核專用的結構體,它還被用來處理內核對小塊內存的請求。當然鑒於Slab分配器的特點,一般來說內核程序中對小於一頁的小塊內存的請求才通過Slab分配器提供的介面Kmalloc來完成(雖然它可分配32到131072位元組的內存)。從內核內存分配的角度來講,kmalloc可被看成是get_free_page(s)的一個有效補充,內存分配粒度更靈活了。
有興趣的話,可以到/proc/slabinfo中找到內核執行現場使用的各種slab信息統計,其中你會看到系統中所有slab的使用信息。從信息中可以看到系統中除了專用結構體使用的slab外,還存在大量為Kmalloc而准備的Slab(其中有些為dma准備的)。
內核非連續內存分配(Vmalloc)
夥伴關系也好、slab技術也好,從內存管理理論角度而言目的基本是一致的,它們都是為了防止「分片」,不過分片又分為外部分片和內部分片之說,所謂內部分片是說系統為了滿足一小段內存區(連續)的需要,不得不分配了一大區域連續內存給它,從而造成了空間浪費;外部分片是指系統雖有足夠的內存,但卻是分散的碎片,無法滿足對大塊「連續內存」的需求。無論何種分片都是系統有效利用內存的障礙。slab分配器使得一個頁面內包含的眾多小塊內存可獨立被分配使用,避免了內部分片,節約了空閑內存。夥伴關系把內存塊按大小分組管理,一定程度上減輕了外部分片的危害,因為頁框分配不在盲目,而是按照大小依次有序進行,不過夥伴關系只是減輕了外部分片,但並未徹底消除。你自己比劃一下多次分配頁面後,空閑內存的剩餘情況吧。
所以避免外部分片的最終思路還是落到了如何利用不連續的內存塊組合成「看起來很大的內存塊」——這里的情況很類似於用戶空間分配虛擬內存,內存邏輯上連續,其實映射到並不一定連續的物理內存上。Linux內核借用了這個技術,允許內核程序在內核地址空間中分配虛擬地址,同樣也利用頁表(內核頁表)將虛擬地址映射到分散的內存頁上。以此完美地解決了內核內存使用中的外部分片問題。內核提供vmalloc函數分配內核虛擬內存,該函數不同於kmalloc,它可以分配較Kmalloc大得多的內存空間(可遠大於128K,但必須是頁大小的倍數),但相比Kmalloc來說,Vmalloc需要對內核虛擬地址進行重映射,必須更新內核頁表,因此分配效率上要低一些(用空間換時間)
與用戶進程相似,內核也有一個名為init_mm的mm_strcut結構來描述內核地址空間,其中頁表項pdg=swapper_pg_dir包含了系統內核空間(3G-4G)的映射關系。因此vmalloc分配內核虛擬地址必須更新內核頁表,而kmalloc或get_free_page由於分配的連續內存,所以不需要更新內核頁表。
vmalloc分配的內核虛擬內存與kmalloc/get_free_page分配的內核虛擬內存位於不同的區間,不會重疊。因為內核虛擬空間被分區管理,各司其職。進程空間地址分布從0到3G(其實是到PAGE_OFFSET,在0x86中它等於0xC0000000),從3G到vmalloc_start這段地址是物理內存映射區域(該區域中包含了內核鏡像、物理頁面表mem_map等等)比如我使用的系統內存是64M(可以用free看到),那麼(3G——3G+64M)這片內存就應該映射到物理內存,而vmalloc_start位置應在3G+64M附近(說"附近"因為是在物理內存映射區與vmalloc_start期間還會存在一個8M大小的gap來防止躍界),vmalloc_end的位置接近4G(說"接近"是因為最後位置系統會保留一片128k大小的區域用於專用頁面映射,還有可能會有高端內存映射區,這些都是細節,這里我們不做糾纏)。
上圖是內存分布的模糊輪廓
由get_free_page或Kmalloc函數所分配的連續內存都陷於物理映射區域,所以它們返回的內核虛擬地址和實際物理地址僅僅是相差一個偏移量(PAGE_OFFSET),你可以很方便的將其轉化為物理內存地址,同時內核也提供了virt_to_phys()函數將內核虛擬空間中的物理映射區地址轉化為物理地址。要知道,物理內存映射區中的地址與內核頁表是有序對應的,系統中的每個物理頁面都可以找到它對應的內核虛擬地址(在物理內存映射區中的)。
而vmalloc分配的地址則限於vmalloc_start與vmalloc_end之間。每一塊vmalloc分配的內核虛擬內存都對應一個vm_struct結構體(可別和vm_area_struct搞混,那可是進程虛擬內存區域的結構),不同的內核虛擬地址被4k大小的空閑區間隔,以防止越界——見下圖)。與進程虛擬地址的特性一樣,這些虛擬地址與物理內存沒有簡單的位移關系,必須通過內核頁表才可轉換為物理地址或物理頁。它們有可能尚未被映射,在發生缺頁時才真正分配物理頁面。
這里給出一個小程序幫助大家認清上面幾種分配函數所對應的區域。
#include<linux/mole.h>
#include<linux/slab.h>
#include<linux/vmalloc.h>
unsignedchar*pagemem;
unsignedchar*kmallocmem;
unsignedchar*vmallocmem;
intinit_mole(void)
{
pagemem = get_free_page(0);
printk("<1>pagemem=%s",pagemem);
kmallocmem = kmalloc(100,0);
printk("<1>kmallocmem=%s",kmallocmem);
vmallocmem = vmalloc(1000000);
printk("<1>vmallocmem=%s",vmallocmem);
}
voidcleanup_mole(void)
{
free_page(pagemem);
kfree(kmallocmem);
vfree(vmallocmem);
}
實例
內存映射(mmap)是Linux操作系統的一個很大特色,它可以將系統內存映射到一個文件(設備)上,以便可以通過訪問文件內容來達到訪問內存的目的。這樣做的最大好處是提高了內存訪問速度,並且可以利用文件系統的介面編程(設備在Linux中作為特殊文件處理)訪問內存,降低了開發難度。許多設備驅動程序便是利用內存映射功能將用戶空間的一段地址關聯到設備內存上,無論何時,只要內存在分配的地址范圍內進行讀寫,實際上就是對設備內存的訪問。同時對設備文件的訪問也等同於對內存區域的訪問,也就是說,通過文件操作介面可以訪問內存。Linux中的X伺服器就是一個利用內存映射達到直接高速訪問視頻卡內存的例子。
熟悉文件操作的朋友一定會知道file_operations結構中有mmap方法,在用戶執行mmap系統調用時,便會調用該方法來通過文件訪問內存——不過在調用文件系統mmap方法前,內核還需要處理分配內存區域(vma_struct)、建立頁表等工作。對於具體映射細節不作介紹了,需要強調的是,建立頁表可以採用remap_page_range方法一次建立起所有映射區的頁表,或利用vma_struct的nopage方法在缺頁時現場一頁一頁的建立頁表。第一種方法相比第二種方法簡單方便、速度快,但是靈活性不高。一次調用所有頁表便定型了,不適用於那些需要現場建立頁表的場合——比如映射區需要擴展或下面我們例子中的情況。
我們這里的實例希望利用內存映射,將系統內核中的一部分虛擬內存映射到用戶空間,以供應用程序讀取——你可利用它進行內核空間到用戶空間的大規模信息傳輸。因此我們將試圖寫一個虛擬字元設備驅動程序,通過它將系統內核空間映射到用戶空間——將內核虛擬內存映射到用戶虛擬地址。從上一節已經看到Linux內核空間中包含兩種虛擬地址:一種是物理和邏輯都連續的物理內存映射虛擬地址;另一種是邏輯連續但非物理連續的vmalloc分配的內存虛擬地址。我們的例子程序將演示把vmalloc分配的內核虛擬地址映射到用戶地址空間的全過程。
程序里主要應解決兩個問題:
第一是如何將vmalloc分配的內核虛擬內存正確地轉化成物理地址?
因為內存映射先要獲得被映射的物理地址,然後才能將其映射到要求的用戶虛擬地址上。我們已經看到內核物理內存映射區域中的地址可以被內核函數virt_to_phys轉換成實際的物理內存地址,但對於vmalloc分配的內核虛擬地址無法直接轉化成物理地址,所以我們必須對這部分虛擬內存格外「照顧」——先將其轉化成內核物理內存映射區域中的地址,然後在用virt_to_phys變為物理地址。
轉化工作需要進行如下步驟:
找到vmalloc虛擬內存對應的頁表,並尋找到對應的頁表項。
獲取頁表項對應的頁面指針
通過頁面得到對應的內核物理內存映射區域地址。
如下圖所示:
第二是當訪問vmalloc分配區時,如果發現虛擬內存尚未被映射到物理頁,則需要處理「缺頁異常」。因此需要我們實現內存區域中的nopaga操作,以能返回被映射的物理頁面指針,在我們的實例中就是返回上面過程中的內核物理內存映射區域中的地址。由於vmalloc分配的虛擬地址與物理地址的對應關系並非分配時就可確定,必須在缺頁現場建立頁表,因此這里不能使用remap_page_range方法,只能用vma的nopage方法一頁一頁的建立。
程序組成
map_driver.c,它是以模塊形式載入的虛擬字元驅動程序。該驅動負責將一定長的內核虛擬地址(vmalloc分配的)映射到設備文件上。其中主要的函數有——vaddress_to_kaddress()負責對vmalloc分配的地址進行頁表解析,以找到對應的內核物理映射地址(kmalloc分配的地址);map_nopage()負責在進程訪問一個當前並不存在的VMA頁時,尋找該地址對應的物理頁,並返回該頁的指針。
test.c它利用上述驅動模塊對應的設備文件在用戶空間讀取讀取內核內存。結果可以看到內核虛擬地址的內容(ok!),被顯示在了屏幕上。
執行步驟
編譯map_driver.c為map_driver.o模塊,具體參數見Makefile
載入模塊:insmodmap_driver.o
生成對應的設備文件
1在/proc/devices下找到map_driver對應的設備命和設備號:grepmapdrv/proc/devices
2建立設備文件mknodmapfilec 254 0(在我的系統里設備號為254)
利用maptest讀取mapfile文件,將取自內核的信息列印到屏幕上。