1. linux 內核的內存管理 - 概念
Concepts overview — The Linux Kernel documentation
Linux中的內存管理是一個復雜的系統,經過多年的發展,它包含越來越多的功能,以支持從 MMU-less microcontrollers 到 supercomputers 的各種系統。
沒有MMU內存管理的系統被稱為 nommu ,它值得寫一份專門的文檔進行描述。
盡管有些概念是相同的,這里我們假設MMU可用,CPU可以將虛擬地址轉換為物理地址。
計算機系統中的物理內存是有限資源,即便支持內存熱插拔,其可以安裝的內存也有限的。物理內存不一定必須是連續的;它可以作為一組不同的地址范圍被訪問。此外,不同的CPU架構,甚至同架構的不同實現對如何定義這些地址范圍都是不同的。
這使得直接處理物理內存異常復雜,為了避免這種復雜性,開發了 虛擬內存 (virtual memory) 的概念。
虛擬內存從應用軟體中抽象出物理內存的細節,只允許在物理內存中保留需要的信息 (demand paging) ,並提供一種機制來保護和控制進程之間的數據共享。
通過虛擬內存,每次內存訪問都訪問一個 虛擬地址 。當CPU對從系統內存讀取(或寫入)的指令進行解碼時,它將該指令中編碼的虛擬地址轉換為內存控制器可以理解的物理地址。
物理內存被切分為 頁幀 page frames 或 頁 pages 。頁的大小是基於架構的。一些架構允許從幾個支持的值中選擇頁大小;此選擇在內核編譯時設置到內核配置。
每個物理內存頁都可以映射為一個或多個 虛擬頁(virtual pages) 。映射關系描述在 頁表(page tables) 中,頁表將程序使用的虛擬地址轉換為物理內存地址。頁表以層次結構組織。
最底層的表包含軟體使用的實際內存頁的物理地址。較高層的表包含較低層表頁的物理地址。頂層表的指針駐留在寄存器中。
當CPU進行地址轉換的時候,它使用寄存器訪問頂級頁表。
虛擬地址的高位,用於頂級頁表的條目索引。然後,通過該條目訪問下級,下級的虛擬地址位又作為其下下級頁表的索引。虛擬地址的最低位定義實際頁內的偏移量。
地址轉換需要多次內存訪問,而內存訪問相對於CPU速度來說比較慢。為了避免在地址轉換上花費寶貴的處理器周期,CPU維護著一個稱為 TLB (Translation Lookaside Buffer)的用於地址轉換緩存(cache)。通常TLB是非常稀缺的資源,需要大內存工作應用程序會因為TLB未命中而影響性能。
很多現代CPU架構允許頁表的高層直接映射到內存頁。例如,x86架構,可以通過二級、三級頁表的條目映射2M甚至1G內存頁。在Linux中,這些內存頁稱為 大頁 (Huge) 。大頁的使用顯著降低了TLB的壓力,提高了TLB命中率,從而提高了系統的整體性能。
Linux提供兩種機制開啟使用大頁映射物理內存。
第一個是 HugeTLB 文件系統,即 hugetlbfs 。它是一個偽文件系統,使用RAM作為其存儲。在此文件系統中創建的文件,數據駐留在內存中,並使用大頁進行映射。
關於 HugeTLB Pages
另一個被稱為 THP (Transparent HugePages) ,後出的開啟大頁映射物理內存的機制。
與 hugetlbfs 不同,hugetlbfs要求用戶和/或系統管理員配置系統內存的哪些部分應該並可以被大頁映射;THP透明地管理這些映射並獲取名稱。
關於 Transparent Hugepage Support
通常,硬體對不同物理內存范圍的訪問方式有所限制。某些情況下,設備不能對所有可定址內存執行DMA。在其他情況下,物理內存的大小超過虛擬內存的最大可定址大小,需要採取特殊措施來訪問部分內存。還有些情況,物理內存的尺寸超過了虛擬內存的最大可定址尺寸,需要採取特殊措施來訪問部分內存。
Linux根據內存頁的使用情況,將其組合為多個 zones 。比如, ZONE_DMA 包含設備用於DMA的內存, ZONE_HIGHMEM 包含未永久映射到內核地址空間的內存, ZONE_NORMAL 包含正常定址內存頁。
內存zones的實際層次架構取決於硬體,因為並非所有架構都定義了所有的zones,不同平台對DMA的要求也不同。
多處理器機器很多基於 NUMA (Non-Uniform Memory Access system - 非統一內存訪問系統 )架構。 在這樣的系統中,根據與處理器的「距離」,內存被安排成具有不同訪問延遲的 banks 。每個 bank 被稱為一個 node ,Linux為每個 node 構造一個獨立的內存管理子系統。 Node 有自己的zones集合、free&used頁面列表,以及各種統計計數器。
What is NUMA?
NUMA Memory Policy
物理內存易失,將數據放入內存的常見情況是讀取文件。讀取文件時,數據會放入 頁面緩存(page cache) ,可以在再次讀取時避免耗時的磁碟訪問。同樣,寫文件時,數據也會被放入 頁面緩存 ,並最終進入存儲設備。被寫入的頁被標記為 臟頁(dirty page) ,當Linux決定將其重用時,它會將更新的數據同步到設備上的文件。
匿名內存 anonymous memory 或 匿名映射 anonymous mappings 表示沒有後置文件系統的內存。這些映射是為程序的stack和heap隱式創建的,或調用mmap(2)顯式創建的。通常,匿名映射只定義允許程序訪問的虛擬內存區域。讀,會創建一個頁表條目,該條目引用一個填充有零的特殊物理頁。寫,則分配一個常規物理頁來保存寫入數據。該頁將被標記為臟頁,如果內核決定重用該頁,則臟頁將被交換出去 swapped out 。
縱貫整個系統生命周期,物理頁可用於存儲不同類型的數據。它可以是內核內部數據結構、設備驅動DMA緩沖區、讀取自文件系統的數據、用戶空間進程分配的內存等。
根據內存頁使用情況,Linux內存管理會區別處理。可以隨時釋放的頁面稱為 可回收(reclaimable) 頁面,因為它們把數據緩存到了其他地方(比如,硬碟),或者被swap out到硬碟上。
可回收頁最值得注意的是 頁面緩存 和 匿名頁面 。
在大多數情況下,存放內部內核數據的頁,和用作DMA緩沖區的頁無法重用,它們將保持現狀直到用戶釋放。這樣的被稱為 不可回收頁(unreclaimable) 。
然而,在特定情況下,即便是內核數據結構佔用的頁面也會被回收。
例如,文件系統元數據的緩存(in-memory)可以從存儲設備中重新讀取,因此,當系統存在內存壓力時,可以從主內存中丟棄它們。
釋放可回收物理內存頁並重新調整其用途的過程稱為 (surprise!) reclaim 。
Linux支持非同步或同步回收頁,取決於系統的狀態。
當系統負載不高時,大部分內存是空閑的,可以立即從空閑頁得到分配。
當系統負載提升後,空閑頁減少,當達到某個閾值( low watermark )時,內存分配請求將喚醒 kswapd 守護進程。它將以非同步的方式掃描內存頁。如果內存頁中的數據在其他地方也有,則釋放這些內存頁;或者退出內存到後置存儲設備(關聯 臟頁 )。
隨著內存使用量進一步增加,並達到另一個閾值- min watermark -將觸發回收。這種情況下,分配將暫停,直到回收到足夠的內存頁。
當系統運行時,任務分配並釋放內存,內存變得碎片化。
雖然使用虛擬內存可以將分散的物理頁表示為虛擬連續范圍,但有時需要分配大的連續的物理內存。這種需求可能會提升。例如,當設備驅動需要一個大的DMA緩沖區時,或當THP分配一個大頁時。
內存地址壓縮(compaction ) 解決了碎片問題。
該機制將佔用的頁從內存zone的下部移動到上部的空閑頁。壓縮掃描完成後,zone開始處的空閑頁就並在一起了,分配較大的連續物理內存就可行了。
與 reclaim 類似, compaction 可以在 kcompactd守護進程中非同步進行,也可以作為內存分配請求的結果同步進行。
在存在負載的機器上,內存可能會耗盡,內核無法回收到足夠的內存以繼續運行。
為了保障系統的其餘部分,引入了 OOM killer 。
OOM killer 選擇犧牲一個任務來保障系統的總體健康。選定的任務被killed,以期望在它退出後釋放足夠的內存以繼續正常的操作。
2. Linux - 用戶態內存映射 和 內核態內存映射
操作系統的內存管理,主要分為三個方面。
第一,物理內存的管理,相當於會議室管理員管理會議室。
第二,虛擬地址的管理,也即在項目組的視角,會議室的虛擬地址應該如何組織。
第三,虛擬地址和物理地址如何映射,也即會議室管理員如果管理映射表。
那麼虛擬地址和物理地址如何映射呢?
每一個進程都有一個列表vm_area_struct,指向虛擬地址空間的不同的內存塊,這個變數的名字叫mmap。
其實內存映射不僅僅是物理內存和虛擬內存之間的映射,還包括將文件中的內容映射到虛擬內存空間。這個時候,訪問內存空間就能夠訪問到文件裡面的數據。而僅有物理內存和虛擬內存的映射,是一種特殊情況。
如果我們要申請小塊內存,就用brk。brk函數之前已經解析過了,這里就不多說了。如果申請一大塊內存,就要用mmap。對於堆的申請來講,mmap是映射內存空間到物理內存。
另外,如果一個進程想映射一個文件到自己的虛擬內存空間,也要通過mmap系統調用。這個時候mmap是映射內存空間到物理內存再到文件。可見mmap這個系統調用是核心,我們現在來看mmap這個系統調用。
用戶態的內存映射機制包含以下幾個部分。
物理內存根據NUMA架構分節點。每個節點裡面再分區域。每個區域裡面再分頁。
物理頁面通過夥伴系統進行分配。分配的物理頁面要變成虛擬地址讓上層可以訪問,kswapd可以根據物理頁面的使用情況對頁面進行換入換出。
對於內存的分配需求,可能來自內核態,也可能來自用戶態。
對於內核態,kmalloc在分配大內存的時候,以及vmalloc分配不連續物理頁的時候,直接使用夥伴系統,分配後轉換為虛擬地址,訪問的時候需要通過內核頁表進行映射。
對於kmem_cache以及kmalloc分配小內存,則使用slub分配器,將夥伴系統分配出來的大塊內存切成一小塊一小塊進行分配。
kmem_cache和kmalloc的部分不會被換出,因為用這兩個函數分配的內存多用於保持內核關鍵的數據結構。內核態中vmalloc分配的部分會被換出,因而當訪問的時候,發現不在,就會調用do_page_fault。
對於用戶態的內存分配,或者直接調用mmap系統調用分配,或者調用malloc。調用malloc的時候,如果分配小的內存,就用sys_brk系統調用;如果分配大的內存,還是用sys_mmap系統調用。正常情況下,用戶態的內存都是可以換出的,因而一旦發現內存中不存在,就會調用do_page_fault。
3. linux兩種共享內存的區別
持久性不同、文件反射方式不同。
1、持久性不同。sysvshm是持久化的,除非被一個進程明確的芹嘩刪除,否則它始終存在州粗於內存里,直到系統關機。mmap映射的內存在不是持久化的,假如進程關閉,映射隨即失效,除非事前已經映射到了一個文件上。
2、文件反射方式不同。前者用COW的方式,把文件映射到當前的進程空間,修改操作不會改動源文件。後者直接把文件映射到當前的進程空間,所有的修改會直接反應到文件的page cache,而後由冊首鎮內核自動同步到映射文件上。
4. linux內核中虛擬內存是怎樣映射到物理內存
當程序在運行的時輪陸候,會檢燃桐拿測到數據在虛擬內存中,並沒在物理內存中,皮搭這時候會產生一個缺頁中斷, 有缺頁中斷來映射。
5. 嵌入式 linux基於arm中,其中的 內存映射 是什麼意思具體完成什麼過程一定採納
內存映射主要是在linux底層部分,就是把硬體外設的各種寄存器直接變為可以操作回的地址,也就是說,每答一個硬體都會有一塊內存來代表它,我們直接操作這塊內存就等於操作了硬體,比方說I2c,它有控制寄存器,數據寄存器,這些寄存器都會被映射到內存里,我們直接操作這塊內存,就操作了寄存器,也就控制了設備
6. Linux 如何映射Windows主機上中文名的共享文件夾
CentOS 6.3安裝時不會進入圖形安裝界面的問題
前段時間重新下載了CentOS6.3的完整包打算完整安裝,因為內用的是容VBOX,
默認的設置內存為512M,但是安裝的時候還是進入文字界面而不是進入圖形界面。
一再上網找都沒找到原因,似乎大家裝的都很順利,後來開始找虛擬機配置的問題,
在把內存調整到1G時,順利的進入了圖形界面。 www.2cto.com
裝完後,又把虛擬內存調整回512M,運行正常。足夠學慣用了。
做個小記錄,怕以後自己忘記了,這個問題在現在應該比較少遇到,但是如果玩
vbox裝的話,如果只用默認的設置建虛擬機還是會遇到這種問題的。
7. Linux將設備地址映射到用戶空間內存映射與VMA
一般情況下,用戶空間是不可能也不應該直接訪問設備的,但是,設備驅動程序中可實現mmap ()函數,這個函數可使得用戶空間能直接訪問設備的物理地址。實際上,mmap ()實現了這樣的一個映射過程:它將用戶空間的一段內存與設備內存關聯,當用戶訪問用戶空間的這段地址范圍時,實際上會轉化為對設備的訪問。
這種能力對於顯示適配器一類的設備非常有意義,如果用戶空間可直接通過內存映射訪問顯存的話,屏幕幀的各點像素將不再需要一個從用戶空間到內核空間的復制的過程。
mmap ()必須以PAGE_SIZE為單位進行映射,實際上,內存只能以頁為單位進行映射,若要映射非PAGE_SIZE整數倍的地址范圍,要先進行頁對齊,強行以PAGE_SIZE的倍數大小進行映射。
從file_operations文件操作結構體可以看出,驅動中mmap ()函數的原型如下:
int ( *mmap)(struct file *, struct vm_area_struct* ) ;
驅動中的mmap () 函數將在用戶進行mmap ()系統調用時最終被調用,mmap ()系統調用的原型與file_operations中mmap ()的原型區別很大,如下所示:
caddr_t mmap (caddr_t addr,size_t len,int prot,int flags,int fd,off_t offset);
參數fd為文件描述符,一般由open ()返回,fd也可以指定為-1,此時需指定flags參數中的MAP_ANON,表明進行的是匿名映射。
len是映射到調用用戶空間的位元組數,它從被映射文件開頭offset個位元組開始算起,offset參數一般設為0,表示從文件頭開始映射。
prot參數指定訪問許可權,可取如下幾個值的「或」:PROT_READ(可讀)、PROT_WRITE(可寫)、PROT_EXEC(可執行)和PROT_NONE(不可訪問)。
參數addr指定文件應被映射到用戶空間的起始地址,一般被指定為NULL,這樣,選擇起始地址的任務將由內核完成,而函數的返回值就是映射到用戶空間的地址。其類型caddr_t實際上就是void*。
當用戶調用mmap ())的時候,內核會進行如下處理。
1)在進程的虛擬空間查找一塊VMA。
2)將這塊VMA進行映射。
3)如果設備驅動程序或者文件系統的file_operations定義了mmap ()操作,則調用它。
4)將這個VMA插入進程的VMA鏈表中。
file_operations中mmap ()函數的第一個參數就是步驟1)找到的VMA。
由mmap ()系統調用映射的內存可由munmap ()解除映射,這個函數的原型如下:
int munmap(caddr_t addr, size_t len ) ;
驅動程序中mmap ()的實現機制是建立頁表,並填充VMA結構體中vm_operations_struct指針。
8. linux下的root,bin, cdrom,etc,initrd,lib分別主要放哪些文件的啊
總體的,不好歸類的
/media 用來掛載存儲設備,DVD, CD-ROM等
/mnt 用來臨時掛載文件系統,可插拔的設備應該掛載到/media上去
/home 除root之外的用戶目錄的默認所在地
/root root用戶目錄
/bin 最常用的命令
/sbin 系統管理員使用的命令(sbin=system bin)
/usr/local 使用源碼安裝的話,一般把prefix目錄指定到這里,如/usr/local/ruby
/usr/share/applications desktop文件是桌面的菜單項
~/.gnome*,~/.gconf* gnome面板的個人配置信息,當gnome面板亂了,可以嘗試刪除這些文件來恢復默認面板
/boot目錄,kernel相關部分
/boot/symvers-%{KRELEASE}.gz 保存著內核中所有符號的crc值
/boot/System.map-%{KRELEASE} 給kernel使用的符號表(symbol table)
/boot/vmlinuz-%{KRELEASE} 可引導的、壓縮的內核
/boot/initrd-%{KRELEASE}.img 包含了支持 Linux 系統兩階段引導過程所需要的必要可執行程序和系統文件
/boot/config-%{KRELEASE} 包括kernel的make config
/boot/message cpio格式的打包文件,存放Grub的配置信息,裡麵包括了圖片,文字說明等內容
/boot目錄,grub配置
/boot/grub/menu.lst 一個鏈接文件,真實文件是grub.conf
/boot/grub/grub.conf grub的配置文件
/boot/grub/device.map 設備的映射文件
/boot/grub/splash.xpm.gz grub開機畫面的gzip壓縮包
/boot/grub/stageN 一般有stage1和stage2,是grub的核心,受限於mbr512位元組的大小限制,所以切開成幾個,stage1是用來載入stage2的
/boot/grub/XXX_stage1_5 stage2文件較大,一般存放於文件系統中,需要XXX_stage1_5來識別各種各樣的文件系統
/etc目錄,系統用戶/用戶組
/etc/passwd 存放所有系統用戶及相關信息
/etc/shadow 存放所有系統用戶的密碼信息
/etc/group 存放所有系統用戶組及相關信息
/etc/gshadow 存放所有系統用戶組的密碼信息
/etc目錄,系統啟動流程相關
/etc/issue 發行版信息
/etc/redhat-release redhat版本信息
/etc/inittab 系統初始化配置
/etc/init.d 存放服務腳本的地方
/etc/rc[0-6S].d 每個運行級別對應的服務,里邊的腳本都是鏈接到/etc/init.d目錄
/etc/rc rc啟動腳本
/etc/rc.local 在所有init腳本結束後調用
/etc/rc.sysinit 在系統啟動時運行一次
/etc/profile 環境變數配置
/etc/profile.d 保存一些腳本,可在/etc/profile中調用
~/.bash_profile 針對某個用戶的配置,會調用.bash_rc
~/.bashrc 針對某個用戶的配置,會調用/etc/bashrc
/etc/bashrc 使用bash時,可設置全局環境配置
~/.bash_history 命令的歷史記錄
~/.bash_logout 用戶退出時執行
/etc/xinetd.conf xinetd的配置文件
/etc/xinetd.d 存放xinetd服務的地方
/etc目錄,基本應用配置相關
/etc/skel 存放用戶文件的「骨架」,當一個用戶創建的時候,里邊的文件就會拷貝到相應的home目錄
/etc/X11 存放X Window的系統配置文件,例如xorg.conf
/etc/DIR_COLORS ls的時候,文件/文件夾顯示的顏色
/etc/mtab 記錄目前掛載的文件系統信息
/etc/fastboot 由shutdown -f 所產生的 ,在重啟之後, 系統會去檢查這個文件是否存在以決定是否要執行fsck
/etc/nologin 系統關閉的時候自動產生,里邊放著shutdown message。在這個時候如果有用戶企圖登錄,就會列印出這個文件存放的message,然後阻止你登錄
/etc/fstab 默認的文件系統掛載情況
/etc/virc vi的配置
/etc/vimrc vim的配置
/etc/wgetrc wget的配置
/etc/yum.conf yum的配置
/etc/yum.repos.d yum源的存放位置
/etc/kmp.conf kmp內核的配置文件
/etc/my.cnf mysql的配置文件
/etc/ssh ssh的配置文件目錄,重要的有sshd_config
/etc/syslog.conf syslog的配置文件
/etc/updatedb.conf updatedb的配置文件
/etc/mtools.conf mtools配置,用於在*UNIX系統中直接訪問dos/win文件系統
/etc/sysctl.conf sysctl預載入的配置文件
/etc/moprobe.conf modprobe的配置文件
/etc/ld.so.conf 載入動態鏈接庫的配置文件,默認會載入ld.so.conf.d里邊的配置
/etc/ld.so.conf.d 存放動態鏈接庫的配置文件
/etc/ld.so.cache 動態鏈接庫的緩存,二進制文件,可以通過ldconfig --print-cache查看
/etc/services 網路服務列表(服務名,埠,協議等)
/etc目錄,域名解析,主機訪問控制
/etc/host.conf 定義DNS客戶端主機發出域名解析的處理順序,默認是先查看/etc/hosts文件,再發送遠程請求
/etc/hosts 自定義ip-域名解析
/etc/resolv.conf DNS伺服器地址
/etc/hosts.allow 和hosts.deny一起用來作為tcpd伺服器的配置文件,tcpd伺服器可以控制外部IP對本機服務的訪問。hosts.allow控制可以訪問本機的IP地址
/etc/hosts.deny 控制禁止訪問本機的IP。如果和hosts.allow的配置有沖突,以hosts.deny為准
/etc目錄,定時任務控制
/etc/crontab cron任務的配置文件,一般在里邊配置有cron.hourly,cron.daily,cron.weekly和cron.monthly
/etc/cron.d 如果你要在特殊的時間使用crontab,可以把配置放到文件夾里邊,配置的格式和/etc/crontab一樣
/etc /cron.daily 每天定時任務
/etc/cron.hourly 每小時定時任務
/etc/cron.monthly 每月定時任務
/etc/cron.weekly 每星期定時任務
/etc/cron.allow 指定那些用戶可以使用crontab
/etc/cron.deny 指定哪些用戶禁止使用crontab,如果文件存在且為空,所有人都可以使用,如果文件不存在,那麼只有root可以使用
/etc/at.allow 指定那些用戶可以使用at
/etc/at.deny 指定哪些用戶禁止使用at,如果文件存在且為空,所有人都可以使用,如果文件不存在,那麼只有root可以使用
/dev目錄 硬體設備信息
/dev/hd[a-z] 第幾個IDE硬碟
/dev/tty[0-9] 第幾個虛擬控制台
/dev/sd[a-z] 第幾個SCSI或SATA硬碟
/dev/zero 一個無窮盡地提 供0(NULL)的設備,可以用來初始化文件
/dev/null 一個空設備,可以向它輸出任何數據,而任何寫入它的輸出都會被拋棄。如果不想讓消息以標准輸出顯示或寫入文件,那麼可以將消息重定向到位桶
/dev/stderr 鏈接文件,指向/proc/self/fd/2(標准錯誤)
/dev/stdin 鏈接文件,指向/proc/self/fd/0(標准輸入)
/dev/stdout 鏈接文件,指向/proc/self/fd/1(標准輸出)
/dev/console 系統控制台,也就是直接和系統連接的監視器。如果你用cat查看該設備,並敲入一些內容,可以看到在屏幕上回顯
/dev/fd[0-9] 第幾個軟碟機設備
/dev/st SCSI磁帶驅動器
/dev/pty 提供遠程登陸偽終端支持。在進行Telnet登錄時就要用到該設備
/dev/ttys 計算機串列介面,對於DOS來說就是com1口
/dev/cua 計算機串列介面,與數據機一起使用的設備
/proc目錄 虛擬文件系統
/proc/apm Advanced Power Management(APM)系統信息,與apm命令相關
/proc/buddyinfo 每個內存區中的每個order有多少塊可用,和內存碎片問題有關
/proc/cmdline 啟動時傳遞給kernel的參數信息
/proc/cpuinfo cpu的信息
/proc/crypto 內核使用的所有已安裝的加密密碼及細節
/proc/devices 已經載入的設備並分類
/proc/dma 已注冊使用的ISA DMA頻道列表
/proc/execdomains Linux內核當前支持的execution domains
/proc/fb 幀緩沖設備列表,包括數量和控制它的驅動
/proc/filesystems 內核當前支持的文件系統類型
/proc/interrupts x86架構中的每個IRQ中斷數
/proc/iomem 每個物理設備當前在系統內存中的映射
/proc/ioports 一個設備的輸入輸出所使用的注冊埠范圍
/proc/kcore 代表系統的物理內存,存儲為核心文件格式,里邊顯示的是位元組數,等於RAM大小加上4kb
/proc/kmsg 記錄內核生成的信息,可以通過/sbin/klogd或/bin/dmesg來處理
/proc/loadavg 根據過去一段時間內CPU和IO的狀態得出的負載狀態,與uptime命令有關
/proc/locks 內核鎖住的文件列表
/proc/mdstat 多硬碟,RAID配置信息(md=multiple disks)
/proc/meminfo RAM使用的相關信息
/proc/misc 其他的主要設備(設備號為10)上注冊的驅動
/proc/moles 所有載入到內核的模塊列表
/proc/mounts 系統中使用的所有掛載
/proc/mtrr 系統使用的Memory Type Range Registers (MTRRs)
/proc/partitions 分區中的塊分配信息
/proc/pci 系統中的PCI設備列表
/proc/slabinfo 系統中所有活動的 slab 緩存信息
/proc/stat 所有的CPU活動信息
/proc/sysrq-trigger 使用echo命令來寫這個文件的時候,遠程root用戶可以執行大多數的系統請求關鍵命令,就好像在本地終端執行一樣。要寫入這個文件,需要把/proc/sys/kernel/sysrq不能設置為0。這個文件對root也是不可讀的
/proc/uptime 系統已經運行了多久
/proc/swaps 交換空間的使用情況
/proc/version Linux內核版本和gcc版本
/proc/bus 系統匯流排(Bus)信息,例如pci/usb等
/proc/driver 驅動信息
/proc/fs 文件系統信息
/proc/ide ide設備信息
/proc/irq 中斷請求設備信息
/proc/net 網卡設備信息
/proc/scsi scsi設備信息
/proc/tty tty設備信息
/proc/net/dev 顯示網路適配器及統計信息
/proc/vmstat 虛擬內存統計信息
/proc/vmcore 內核panic時的內存映像
/proc/diskstats 取得磁碟信息
/proc/schedstat kernel調度器的統計信息
/proc/zoneinfo 顯示內存空間的統計信息,對分析虛擬內存行為很有用
/proc目錄, 進程N的信息
/proc/N pid為N的進程信息
/proc/N/cmdline 進程啟動命令
/proc/N/cwd 鏈接到進程當前工作目錄
/proc/N/environ 進程環境變數列表
/proc/N/exe 鏈接到進程的執行命令文件
/proc/N/fd 包含進程相關的所有的文件描述符
/proc/N/maps 與進程相關的內存映射信息
/proc/N/mem 指代進程持有的內存,不可讀
/proc/N/root 鏈接到進程的根目錄
/proc/N/stat 進程的狀態
/proc/N/statm 進程使用的內存的狀態
/proc/N/status 進程狀態信息,比stat/statm更具可讀性
/proc/self 鏈接到當前正在運行的進程
/var目錄 存放經常變化數據的地方
/var/lib/rpm 存放大多數rpm相關的文件
/var/cache/yum yum升級時下載的rpm文件的臨時存放地,還包括系統中rpm包的頭信息
/var/spool/cron/$username 每個用戶自定義的cron任務,可以使用crontab或vi來操作
/var/lock 一般用來存放文件鎖
/var/log 一般用來存放日誌文件
/var/run 一般用來存放pid文件
/var/crash 一般是存放系統崩潰時產生的信息
/var/cache 一般用來存放緩存信息,例如yum package的緩存
/etc/sysconfig目錄 系統基本配置
/etc/sysconfig/amd 為amd提供操作參數,用來自動mount/unmount文件系統
/etc/sysconfig/apmd 由apmd使用來配置電源設置
/etc/sysconfig/arpwatch 在啟動的時候傳遞給arpwatch守護進程的參數
/etc/sysconfig/authconfig 設置主機使用的驗證方式
/etc/sysconfig/autofs 自動掛載設備的自定義選項
/etc/sysconfig/clock 系統硬體時鍾的設置
/etc/sysconfig/desktop 設置新用戶的桌面和進入運行級別5所使用的顯示管理器
/etc/sysconfig/dhcpd 在啟動的時候傳遞給dhcpd守護進程的參數
/etc/sysconfig/gpm 在啟動的時候傳遞給gpm守護進程的參數
/etc/sysconfig/hwconf 列出kudzu檢測到的所有硬體
/etc/sysconfig/i18n 默認系統語言,系統支持的所有語言,默認系統字體
/etc/sysconfig/init 系統啟動時的顯示方式
/etc/sysconfig/ip6tables-config 在系統啟動或者ip6tables服務啟動時,內核用來設置IPv6包過濾
/etc/sysconfig/iptables-config 在系統啟動或者iptables服務啟動時,內核用來設置包過濾
/etc/sysconfig/keyboard 控制鍵盤的行為
/etc/sysconfig/kudzu 在啟動的時候通過kudzu觸發一次安全的系統硬體探查
/etc/sysconfig/named 在啟動的時候傳遞給named守護進程的參數
/etc/sysconfig/netmp netmp服務的配置文件
/etc/sysconfig/network 網路的配置信息
/etc/sysconfig/ntpd 在啟動的時候傳遞給ntpd守護進程的參數
/etc/sysconfig/radvd 在啟動的時候傳遞給radvd守護進程的參數
/etc/sysconfig/samba 在啟動的時候傳遞給smbd/nmbd守護進程的參數
/etc/sysconfig/selinux selinux的基本控制選項
/etc/sysconfig/spamassassin 在啟動的時候傳遞給spamd守護進程的參數
/etc/sysconfig/squid 在啟動的時候傳遞給squid守護進程的參數
/etc/sysconfig/vncservers 配置vnc服務啟動的方式
/etc/sysconfig/xinetd 在啟動的時候傳遞給xinetd守護進程的參數
/proc/sys目錄 系統重要配置參數,涉及眾多內核參數
/proc/sys/fs/file-max 可以分配的文件句柄的最大數目
/proc/sys/fs/file-nr 已分配文件句柄的數目、已使用文件句柄的數目、文件句柄的最大數目
/proc/sys/fs/inode-* 任何以名稱「inode」開頭的文件所執行的操作與上面那些以名稱「file」開頭的文件所執行的操作一樣,但所執行的操作與索引節點有關,而與文件句柄無關
/proc/sys/fs/overflowuid 和 /proc/sys/fs/overflowgid 這兩個文件分別保存那些支持 16 位用戶標識和組標識的任何文件系統的用戶標識(UID)和組標識(GID)
/proc/sys/fs/super-max 該文件指定超級塊處理程序的最大數目。掛裝的任何文件系統需要使用超級塊,所以如果掛裝了大量文件系統,則可能會用盡超級塊處理程序
/proc/sys/fs/super-nr 顯示當前已分配超級塊的數目
/proc/sys/kernel/acct 該文件有三個可配置值,根據包含日誌的文件系統上可用空間的數量(以百分比表示),這些值控制何時開始進行進程記帳:如果可用空間低於這個百分比值,則停止進程記帳/如果可用空間高於這個百分比值,則開始進程記帳/檢查上面兩個值的頻率(以秒為單位)
/proc/sys/kernel/ctrl-alt-del 該值控制系統在接收到 ctrl+alt+delete 按鍵組合時如何反應
/proc/sys/kernel/domainname 配置網路域名
/proc/sys/kernel/hostname 主機名
/proc/sys/kernel/msgmax 指定了從一個進程發送到另一個進程的消息的最大長度
/proc/sys/kernel/msgmnb 指定在一個消息隊列中最大的位元組數
/proc/sys/kernel/msgmni 指定消息隊列標識的最大數目
/proc/sys/kernel/panic 如果發生「內核嚴重錯誤(kernel panic)」,內核在重新引導之前等待的時間
/proc/sys/kernel/printk 該文件有四個數字值,它們根據日誌記錄消息的重要性,定義將其發送到何處
/proc/sys/kernel/shmall 在任何給定時刻系統上可以使用的共享內存的總量(以位元組為單位)
/proc/sys/kernel/shmax 內核所允許的最大共享內存段的大小(以位元組為單位)
/proc/sys/kernel/shmmni 用於整個系統共享內存段的最大數目
/proc/sys/kernel/sysrq 如果該文件指定的值為非零,則激活 System Request Key
/proc/sys/kernel/threads-max 內核所能使用的線程的最大數目
/proc/sys/net/core/message_burst 寫新的警告消息所需的時間(以 1/10 秒為單位);在這個時間內所接收到的其它警告消息會被丟棄。這用於防止某些企圖用消息「淹沒」您系統的人所使用的拒絕服務攻擊
/proc/sys/net/core/message_cost 存有與每個警告消息相關的成本值。該值越大,越有可能忽略警告消息
/proc/sys/net/core/netdev_max_backlog 在介面接收數據包的速率比內核處理這些包的速率快時,允許送到隊列的數據包的最大數目
/proc/sys/net/core/optmem_max 每個套接字所允許的最大緩沖區的大小
/proc/sys/net/core/rmem_default 接收套接字緩沖區大小的預設值(以位元組為單位)
/proc/sys/net/core/rmem_max 接收套接字緩沖區大小的最大值(以位元組為單位)。
/proc/sys/net/core/wmem_default 發送套接字緩沖區大小的預設值(以位元組為單位)。
/proc/sys/net/core/wmem_max 發送套接字緩沖區大小的最大值(以位元組為單位)
/proc/sys/net/ipv4/ip_forward ip轉發是否生效
/proc/sys/net/ipv4/tcp_retrans_collapse 控制TCP雙方窗口協商出現錯誤的時候的一些重傳的行為。但是在老的2.6的核 (<2.6.18)里頭,這個重傳會導致kernel oops,kernel panic,所以如果出現有 tcp_retrans_*樣子的kernel panic,可以把這個參數給設置成0
/proc/sys/vm/buffermem 控制用於緩沖區內存的整個系統內存的數量(以百分比表示)。它有三個值,通過把用空格相隔的一串數字寫入該文件來設置這三個值。用於緩沖區的內存的最低百分比/如果發生所剩系統內存不多,而且系統內存正在減少這種情況,系統將試圖維護緩沖區內存的數量/用於緩沖區的內存的最高百分比
/proc/sys/vm/freepages 控制系統如何應對各種級別的可用內存。它有三個值,通過把用空格相隔的一串數字寫入該文件來設置這三個值。如果系統中可用頁面的數目達到了最低限制,則只允許內核分配一些內存/如果系統中可用頁面的數目低於這一限制,則內核將以較積極的方式啟動交換,以釋放內存,從而維持系統性能/內核將試圖保持這個數量的系統內存可用。低於這個值將啟動內核交換
/proc/sys/vm/kswapd 控制允許內核如何交換內存。它有三個值,通過把用空格相隔的一串數字寫入該文件來設置這三個值:內核試圖一次釋放的最大頁面數目。如果想增加內存交換過程中的帶寬,則需要增加該值/內核在每次交換中試圖釋放頁面的最少次數/內核在一次交換中所寫頁面的數目。這對系統性能影響最大。這個值越大,交換的數據越多,花在磁碟尋道上的時間越少。然而,這個值太大會因「淹沒」請求隊列而反過來影響系統性能
/proc/sys/vm/pagecache 該文件與/proc/sys/vm/buffermem 的工作內容一樣,但它是針對文件的內存映射和一般高速緩存
/proc/sys/vm/dirty_background_ratio 記錄當所有被更改頁面總大小占工作內存超過某個限制時,pdflush 會開始寫回工作,默認是10%
/proc/sys/vm/dirty_ratio 控制文件系統的文件系統寫緩沖區的大小,單位是百分比,表示系統內存的百分比,表示當寫緩沖使用到系統內存多少的時候,開始向磁碟寫出數據。默認是40%
/proc/sys/vm/dirty_writeback_centisecs 記錄pdflush進程把page cache里邊的內容寫入磁碟的時間周期,默認是5秒
/proc/sys/vm/dirty_expire_centisecs 控制一個更改過的頁面經過多長時間後被認為是過期的、必須被寫回的頁面,默認是30秒
/proc/sys/vm/laptop_mode 是否使用筆記本模式,在kernel2.6之後支持
9. 【深入淺出Linux】關於mmap的解析
看這篇文章之前需要知道一個概念
虛擬內存系統通過將虛擬內存分割為稱作虛擬頁(Virtual Page,VP)大小固定的塊,一般情況下,每個虛擬頁的大小默認是4096位元組。同樣的,物理內存也被分割為物理頁(Physical Page,PP),也為4096位元組。
在LINUX中我們可以使用mmap用來在進程虛擬內存地址空間中分配地址空間,創建和物理內存的映射關系。
映射關系可以分為兩種
1、文件映射
磁碟文件映射進程的虛擬地址空間,使用文件內容初始化物理內存。
2、匿名映射
初始化全為0的內存空間。
而對於映射關系是否共享又分為
1、私有映射(MAP_PRIVATE)
多進程間數據共享,修改不反應到磁碟實際文件,是一個-on-write(寫時復制)的映射方式。
2、共享映射(MAP_SHARED)
多進程間數據共享,修改反應到磁碟實際文件中。
因此總結起來有4種組合
1、私有文件映射
多個進程使用同樣的物理內存頁進行初始化,但是各個進程對內存文件的修改不會共享,也不會反應到物理文件中
2、私有匿名映射
mmap會創建一個新的映射,各個進程不共享,這種使用主要用於分配內存(malloc分配大內存會調用mmap)。
例如開辟新進程時,會為每個進程分配虛擬的地址空間,這些虛擬地址映射的物理內存空間各個進程間讀的時候共享,寫的時候會-on-write。
3、共享文件映射
多個進程通過虛擬內存技術共享同樣的物理內存空間,對內存文件 的修改會反應到實際物理文件中,他也是進程間通信(IPC)的一種機制。
4、共享匿名映射
這種機制在進行fork的時候不會採用寫時復制,父子進程完全共享同樣的物理內存頁,這也就實現了父子進程通信(IPC).
這里值得注意的是,mmap只是在虛擬內存分配了地址空間,只有在第一次訪問虛擬內存的時候才分配物理內存。
在mmap之後,並沒有在將文件內容載入到物理頁上,只上在虛擬內存中分配了地址空間。當進程在訪問這段地址時,通過查找頁表,發現虛擬內存對應的頁沒有在物理內存中緩存,則產生"缺頁",由內核的缺頁異常處理程序處理,將文件對應內容,以頁為單位(4096)載入到物理內存,注意是只載入缺頁,但也會受操作系統一些調度策略影響,載入的比所需的多。
1.write
因為物理內存是有限的,mmap在寫入數據超過物理內存時,操作系統會進行頁置換,根據淘汰演算法,將需要淘汰的頁置換成所需的新頁,所以mmap對應的內存是可以被淘汰的(若內存頁是"臟"的,則操作系統會先將數據回寫磁碟再淘汰)。這樣,就算mmap的數據遠大於物理內存,操作系統也能很好地處理,不會產生功能上的問題。
2.read
從圖中可以看出,mmap要比普通的read系統調用少了一次的過程。因為read調用,進程是無法直接訪問kernel space的,所以在read系統調用返回前,內核需要將數據從內核復制到進程指定的buffer。但mmap之後,進程可以直接訪問mmap的數據(page cache)。
測試結果來源於: 深入剖析mmap-從三個關鍵問題說起
1.讀性能分析
場景:對2G的文件進行順序寫入
可以看到mmap在100byte寫入時已經基本達到最大寫入性能,而write調用需要在4096(也就是一個page size)時,才能達到最大寫入性能。
從測試結果可以看出,在寫小數據時,mmap會比write調用快,但在寫大數據時,反而沒那麼快。
2.寫性能分析
場景:對2G的文件進行順序讀取(為了避免磁碟對測試的影響,2G文件都緩存在pagecache中)
由上可以看出,在read上面,mmap的性能還是非常好的。
優點如下:
1、對文件的讀取操作跨過了頁緩存,減少了數據的拷貝次數,用內存讀寫取代I/O讀寫,提高了文件讀取效率。
2、實現了用戶空間和內核空間的高效交互方式。兩空間的各自修改操作可以直接反映在映射的區域內,從而被對方空間及時捕捉。
3、提供進程間共享內存及相互通信的方式。不管是父子進程還是無親緣關系的進程,都可以將自身用戶空間映射到同一個文件或匿名映射到同一片區域。從而通過各自對映射區域的改動,達到進程間通信和進程間共享的目的。同時,如果進程A和進程B都映射了區域C,當A第一次讀取C時通過缺頁從磁碟復制文件頁到內存中;但當B再讀C的相同頁面時,雖然也會產生缺頁異常,但是不再需要從磁碟中復制文件過來,而可直接使用已經保存在內存中的文件數據。
4、可用於實現高效的大規模數據傳輸。內存空間不足,是制約大數據操作的一個方面,解決方案往往是藉助硬碟空間協助操作,補充內存的不足。但是進一步會造成大量的文件I/O操作,極大影響效率。這個問題可以通過mmap映射很好的解決。換句話說,但凡是需要用磁碟空間代替內存的時候,mmap都可以發揮其功效。
缺點如下:
1.文件如果很小,是小於4096位元組的,比如10位元組,由於內存的最小粒度是頁,而進程虛擬地址空間和內存的映射也是以頁為單位。雖然被映射的文件只有10位元組,但是對應到進程虛擬地址區域的大小需要滿足整頁大小,因此mmap函數執行後,實際映射到虛擬內存區域的是4096個位元組,11~4096的位元組部分用零填充。因此如果連續mmap小文件,會浪費內存空間。
3.如果更新文件的操作很多,會觸發大量的臟頁回寫及由此引發的隨機IO上。所以在隨機寫很多的情況下,mmap方式在效率上不一定會比帶緩沖區的一般寫快。
10. 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文件,將取自內核的信息列印到屏幕上。