1. 關於linux進程資源回收問題 c語言
你可以隨時通過free命令查看。
2. linux系統的內存分配與回收採用什麼演算法
Linux的內存管理機制,遵循的原則是盡可能佔用,將一些程序調用過的硬碟數據讀入內存,利用內存讀寫的高速特性來提高Linux系統的數據訪問性能,所以當應用程序釋放內存的時候,系統本身不一定會把內存釋放,這樣下次應用程序啟動的時候能快速加...
3. Linux中內存的分配和回收是怎樣的
Linux 採用 Buddy 演算法有效分配和釋放物理頁塊。 linux系統內存管理的特點linux的進程結束後,它佔用的資源全部釋放,但是內存僅僅是設置了標志,標志了這部分內存已經不再使用,可以被重新分配的。當進程需要內存時,linux系統首先從空閑內存中...
4. 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文件,將取自內核的信息列印到屏幕上。
5. linux清理內存緩存命令
看緩存的命令
free -m
清理緩存的命令
echo 1 > /proc/sys/vm/drop_caches
echo 2 > /proc/sys/vm/drop_caches
echo 3 > /proc/sys/vm/drop_caches
echo 0 是不釋放緩存
echo 1 是釋放頁緩存 《Linux就該這么學》
ehco 2 是釋放dentries和inodes緩存
echo 3 是釋放 1 和 2 中說道的的所有緩存
6. linux內存回收的三種方式
1. 快速內存回收:處於get_page_from_freelist()函數中,在遍歷zonelist過程中,對每個zone都在分配前進行判斷,如果分配後zone的空閑內存數量 < 閥值 + 保留頁框數量,那麼此zone就會進行快速內存回收。其中閥值可能是min/low/high的任何一種,因為在快速內存分配,慢速內存分配和oom分配過程中如果回收的頁框足夠,都會調用到get_page_from_freelist()函數,所以快速內存回收不僅僅發生在快速內存分配中,在慢速內存分配過程中也會發生。
2. 直接內存回收:處於慢速分配過程中,直接內存回收只有一種情況下會使用,在慢速分配中無法從zonelist的所有zone中以min閥值分配頁框,並且進行非同步內存壓縮後,還是無法分配到頁框的時候,就對zonelist中的所有zone進行一次直接內存回收。注意,直接內存回收是針對zonelist中的所有zone的,它並不像快速內存回收和kswapd內存回收,只會對zonelist中空閑頁框不達標的zone進行內存回收。在直接內存回收中,有可能喚醒flush內核線程。
3. kswapd內存回收:發生在kswapd內核線程中,每個node有一個swapd內核線程,也就是kswapd內核線程中的內存回收,是只針對所在node的,並且只會對分配了order頁框數量後空閑頁框數量 < 此zone的high閥值 + 保留頁框數量的zone進行內存回收,並不會對此node的所有zone進行內存回收。
7. Linux下如何釋放內存
首先,查看/proc/sys/vm/drop_caches的值
[root@server test]# cat /proc/sys/vm/drop_caches
0
值默認為0
然後,運行sync命令
[root@server test]# sync
手動執行sync命令(描述:sync 命令運行 sync 子常式。如果必須停止系統,則運行sync 命令以確保文件系統的完整性。sync 命令將所有未寫的系統緩沖區寫到磁碟中,包含已修改的 i-node、已延遲的塊 I/O 和讀寫映射文件)
最後,輸入手動釋放內存的命令
[root@server test]# echo 1 > /proc/sys/vm/drop_caches
drop_caches的值可以是0-3之間的數字,代表不同的含義:
0:不釋放(系統默認值)
1:釋放頁緩存
2:釋放dentries和inodes
3:釋放所有緩存
釋放完內存後改回去讓系統重新自動分配內存
echo 0 >/proc/sys/vm/drop_caches
free -m #看內存是否已經釋放掉了。
如果我們需要釋放所有緩存,就輸入下面的命令:
[root@server test]# echo 3 > /proc/sys/vm/drop_caches
8. linux內存中的cache真的能被回收么
不了解。這樣的人的第一反應是:天啊,內存用了好多,70個多G,可是我幾乎沒有運行什麼大程序啊?為什麼會這樣?Linux好占內存!
自以為很了解。這樣的人一般自習評估過會說:嗯,根據我專業的眼光看出來,內存才用了17G左右,還有很多剩餘內存可用。buffers/cache佔用的較多,說明系統中有進程曾經讀寫過文件,但是不要緊,這部分內存是當空閑來用的。
真的很了解。這種人的反應反而讓人感覺最不懂Linux,他們的反應是:free顯示的是這樣,好吧我知道了。神馬?你問我這些內存夠不夠,我當然不知道啦!我特么怎麼知道你程序怎麼寫的?
根據目前網路上技術文檔的內容,我相信絕大多數了解一點Linux的人應該處在第二種層次。大家普遍認為,buffers和cached所佔用的內存空間是可以在內存壓力較大的時候被釋放當做空閑空間用的。但真的是這樣么?在論證這個題目之前,我們先簡要介紹一下buffers和cached是什麼意思:
什麼是buffer/cache?
buffer和cache是兩個在計算機技術中被用濫的名詞,放在不通語境下會有不同的意義。在Linux的內存管理中,這里的buffer指Linux內存的:Buffer cache。這里的cache指Linux內存中的:Page cache。翻譯成中文可以叫做緩沖區緩存和頁面緩存。在歷史上,它們一個(buffer)被用來當成對io設備寫的緩存,而另一個(cache)被用來當作對io設備的讀緩存,這里的io設備,主要指的是塊設備文件和文件系統上的普通文件。但是現在,它們的意義已經不一樣了。在當前的內核中,page cache顧名思義就是針對內存頁的緩存,說白了就是,如果有內存是以page進行分配管理的,都可以使用page cache作為其緩存來管理使用。當然,不是所有的內存都是以頁(page)進行管理的,也有很多是針對塊(block)進行管理的,這部分內存使用如果要用到cache功能,則都集中到buffer cache中來使用。(從這個角度出發,是不是buffer cache改名叫做block cache更好?)然而,也不是所有塊(block)都有固定長度,系統上塊的長度主要是根據所使用的塊設備決定的,而頁長度在X86上無論是32位還是64位都是4k。
明白了這兩套緩存系統的區別,就可以理解它們究竟都可以用來做什麼了。
什麼是page cache
Page cache主要用來作為文件系統上的文件數據的緩存來用,尤其是針對當進程對文件有read/write操作的時候。如果你仔細想想的話,作為可以映射文件到內存的系統調用:mmap是不是很自然的也應該用到page cache?在當前的系統實現里,page cache也被作為其它文件類型的緩存設備來用,所以事實上page cache也負責了大部分的塊設備文件的緩存工作。
什麼是buffer cache
Buffer cache則主要是設計用來在系統對塊設備進行讀寫的時候,對塊進行數據緩存的系統來使用。這意味著某些對塊的操作會使用buffer cache進行緩存,比如我們在格式化文件系統的時候。一般情況下兩個緩存系統是一起配合使用的,比如當我們對一個文件進行寫操作的時候,page cache的內容會被改變,而buffer cache則可以用來將page標記為不同的緩沖區,並記錄是哪一個緩沖區被修改了。這樣,內核在後續執行臟數據的回寫(writeback)時,就不用將整個page寫回,而只需要寫回修改的部分即可。
如何回收cache?
Linux內核會在內存將要耗盡的時候,觸發內存回收的工作,以便釋放出內存給急需內存的進程使用。一般情況下,這個操作中主要的內存釋放都來自於對buffer/cache的釋放。尤其是被使用更多的cache空間。既然它主要用來做緩存,只是在內存夠用的時候加快進程對文件的讀寫速度,那麼在內存壓力較大的情況下,當然有必要清空釋放cache,作為free空間分給相關進程使用。所以一般情況下,我們認為buffer/cache空間可以被釋放,這個理解是正確的。
但是這種清緩存的工作也並不是沒有成本。理解cache是干什麼的就可以明白清緩存必須保證cache中的數據跟對應文件中的數據一致,才能對cache進行釋放。所以伴隨著cache清除的行為的,一般都是系統IO飆高。因為內核要對比cache中的數據和對應硬碟文件上的數據是否一致,如果不一致需要寫回,之後才能回收。
在系統中除了內存將被耗盡的時候可以清緩存以外,我們還可以使用下面這個文件來人工觸發緩存清除的操作:
方法是:
當然,這個文件可以設置的值分別為1、2、3。它們所表示的含義為:
echo 1 > /proc/sys/vm/drop_caches:表示清除pagecache。
echo 2 > /proc/sys/vm/drop_caches:表示清除回收slab分配器中的對象(包括目錄項緩存和inode緩存)。slab分配器是內核中管理內存的一種機制,其中很多緩存數據實現都是用的pagecache。
echo 1 > /proc/sys/vm/drop_caches:表示清除pagecache和slab分配器中的緩存對象。
cache都能被回收么?
我們分析了cache能被回收的情況,那麼有沒有不能被回收的cache呢?當然有。我們先來看第一種情況:
tmpfs
大家知道Linux提供一種「臨時」文件系統叫做tmpfs,它可以將內存的一部分空間拿來當做文件系統使用,使內存空間可以當做目錄文件來用。現在絕大多數Linux系統都有一個叫做/dev/shm的tmpfs目錄,就是這樣一種存在。當然,我們也可以手工創建一個自己的tmpfs,方法如下:
於是我們就創建了一個新的tmpfs,空間是20G,我們可以在/tmp/tmpfs中創建一個20G以內的文件。如果我們創建的文件實際佔用的空間是內存的話,那麼這些數據應該佔用內存空間的什麼部分呢?根據pagecache的實現功能可以理解,既然是某種文件系統,那麼自然該使用pagecache的空間來管理。我們試試是不是這樣?
我們在tmpfs目錄下創建了一個13G的文件,並通過前後free命令的對比發現,cached增長了13G,說明這個文件確實放在了內存里並且內核使用的是cache作為存儲。再看看我們關心的指標: -/+ buffers/cache那一行。我們發現,在這種情況下free命令仍然提示我們有110G內存可用,但是真的有這么多麼?我們可以人工觸發內存回收看看現在到底能回收多少內存:
可以看到,cached佔用的空間並沒有像我們想像的那樣完全被釋放,其中13G的空間仍然被/tmp/tmpfs中的文件佔用的。當然,我的系統中還有其他不可釋放的cache佔用著其餘16G內存空間。那麼tmpfs佔用的cache空間什麼時候會被釋放呢?是在其文件被刪除的時候.如果不刪除文件,無論內存耗盡到什麼程度,內核都不會自動幫你把tmpfs中的文件刪除來釋放cache空間。
這是我們分析的第一種cache不能被回收的情況。還有其他情況,比如:
共享內存
共享內存是系統提供給我們的一種常用的進程間通信(IPC)方式,但是這種通信方式不能在shell中申請和使用,所以我們需要一個簡單的測試程序,代碼如下:
程序功能很簡單,就是申請一段不到2G共享內存,然後打開一個子進程對這段共享內存做一個初始化操作,父進程等子進程初始化完之後輸出一下共享內存的內容,然後退出。但是退出之前並沒有刪除這段共享內存。我們來看看這個程序執行前後的內存使用:
cached空間由16G漲到了18G。那麼這段cache能被回收么?繼續測試:
結果是仍然不可回收。大家可以觀察到,這段共享內存即使沒人使用,仍然會長期存放在cache中,直到其被刪除。刪除方法有兩種,一種是程序中使用shmdt(),另一種是使用ipcrm命令。我們來刪除試試:
刪除共享內存後,cache被正常釋放了。這個行為與tmpfs的邏輯類似。內核底層在實現共享內存(shm)、消息隊列(msg)和信號量數組(sem)這些POSIX:XSI的IPC機制的內存存儲時,使用的都是tmpfs。這也是為什麼共享內存的操作邏輯與tmpfs類似的原因。當然,一般情況下是shm佔用的內存更多,所以我們在此重點強調共享內存的使用。說到共享內存,Linux還給我們提供了另外一種共享內存的方法,就是:
mmap
mmap()是一個非常重要的系統調用,這僅從mmap本身的功能描述上是看不出來的。從字面上看,mmap就是將一個文件映射進進程的虛擬內存地址,之後就可以通過操作內存的方式對文件的內容進行操作。但是實際上這個調用的用途是很廣泛的。當malloc申請內存時,小段內存內核使用sbrk處理,而大段內存就會使用mmap。當系統調用exec族函數執行時,因為其本質上是將一個可執行文件載入到內存執行,所以內核很自然的就可以使用mmap方式進行處理。我們在此僅僅考慮一種情況,就是使用mmap進行共享內存的申請時,會不會跟shmget()一樣也使用cache?
同樣,我們也需要一個簡單的測試程序:
這次我們乾脆不用什麼父子進程的方式了,就一個進程,申請一段2G的mmap共享內存,然後初始化這段空間之後等待100秒,再解除影射所以我們需要在它sleep這100秒內檢查我們的系統內存使用,看看它用的是什麼空間?當然在這之前要先創建一個2G的文件./mmapfile。結果如下:
然後執行測試程序:
我們可以看到,在程序執行期間,cached一直為18G,比之前漲了2G,並且此時這段cache仍然無法被回收。然後我們等待100秒之後程序結束。
程序退出之後,cached佔用的空間被釋放。這樣我們可以看到,使用mmap申請標志狀態為MAP_SHARED的內存,內核也是使用的cache進行存儲的。在進程對相關內存沒有釋放之前,這段cache也是不能被正常釋放的。實際上,mmap的MAP_SHARED方��申請的內存,在內核中也是由tmpfs實現的。由此我們也可以推測,由於共享庫的只讀部分在內存中都是以mmap的MAP_SHARED方式進行管理,實際上它們也都是要佔用cache且無法被釋放的。
最後
我們通過三個測試例子,發現Linux系統內存中的cache並不是在所有情況下都能被釋放當做空閑空間用的。並且也也明確了,即使可以釋放cache,也並不是對系統來說沒有成本的。總結一下要點,我們應該記得這樣幾點:
當cache作為文件緩存被釋放的時候會引發IO變高,這是cache加快文件訪問速度所要付出的成本。
tmpfs中存儲的文件會佔用cache空間,除非文件刪除否則這個cache不會被自動釋放。
使用shmget方式申請的共享內存會佔用cache空間,除非共享內存被ipcrm或者shmdt,否則相關的cache空間都不會被自動釋放。
使用mmap方法申請的MAP_SHARED標志的內存會佔用cache空間,除非進程將這段內存munmap,否則相關的cache空間都不會被自動釋放。
實際上shmget、mmap的共享內存,在內核層都是通過tmpfs實現的,tmpfs實現的存儲用的都是cache。
9. linux的常用命令及技巧
一。 通用命令:
1. date :print or set the system date and time2. stty -a: 可以查看或者列印控制字元(Ctrl-C, Ctrl-D, Ctrl-Z等)3. passwd: print or set the system date and time (用passwd -h查看)4. logout, login: 登錄shell的登錄和注銷命令5. pwd: print or set the system date and time6. more, less, head tail: 顯示或部分顯示文件內容.7. lp/lpstat/cancel, lpr/lpq/lprm: 列印文件.8. 更改文件許可權: chmod u+x...9. 刪除非空目錄:rm -fr dir10.拷貝目錄: cp -R dir11. fg jobid :可以將一個後台進程放到前台。Ctrl-z 可以將前台進程掛起(suspend), 然後可以用bg jobid 讓其到後台運行。job & 可以直接讓job直接在後台運行。12. kill 的作用: send a signal to a process. eg: kill -9 發送的是SIG_KILL信號。。。 具體發送什麼信號 可以通過 man kill 查看。13. ps 的用法, ps -e 或 ps -o pid,ppid,session,tpgid, comm (其中session顯示的sessionid, tpgid顯示前台進程組id, comm顯示命令名稱。)二 .ubuntu常用命令:
1. dpkg: package manager for Debian* 安裝: dpkg -i package* 卸載: dpkg -r package* 卸載並刪除配置文件: dpkg -P |--purge package* 如果安裝一個包時。說依賴某些庫。 可以先 apt-get install somelib...* 查看軟體包安裝內容 :dpkg -L package* 查看文件由哪個軟體包提供: dpkg -S filename* 另外 dpkg還有 dselect和aptitude 兩個frontend.2. apt* 安裝: apt-get install packs* apt-get update : 更新源* apt-get upgrade: 升級系統。* apt-get dist-upgrade: 智能升級。安裝新軟體包,刪除廢棄的軟體包* apt-get -f install : -f == --fix broken 修復依賴* apt-get autoremove: 自動刪除無用的軟體* apt-get remove packages :刪除軟體* apt-get remove package --purge 刪除包並清除配置文件* 清除所以刪除包的殘余配置文件: dpkg -l |grep ^rc|awk '{print $2}' |tr [/n] [ ]|sudo xargs dpkg -P* 安裝軟體時候包的臨時存放目錄 : /var/cache/apt/archives* 清除該目錄: apt-get clean* 清除該目錄的舊版本的軟體緩存: apt-get autoclean* 查詢軟體some的依賴包: apt-cache depends some* 查詢軟體some被哪些包依賴: apt-get rdepends some* 搜索軟體: apt-cache search name|regexp* 查看軟體包的作用:apt-cache show package* 查看一個軟體的編譯依賴庫: apt-cache showsrc packagename|grep Build-Depends* 下載軟體的源代碼 : apt-get source packagename (注: sources.list 中應該有 deb-src 源)* 安裝軟體包源碼的同時, 安裝其編譯環境 :apt-get build-dep packagename (有deb-src源)* 如何將本地光碟加入安裝源列表: apt-cdrom add3. 系統命令:* 查看內核版本: uname -a* 查看ubuntu 版本: cat /etc/issue* 查看網卡狀態 : ethtool eth0* 查看內存,cpu的信息: cat /proc/meminfo ; cat /proc/cpuinfo(/proc下面的有很多系統信息)* 列印文件系統空間使用情況: df -h* 查看硬碟分區情況: fdisk -l* 產看文件大小: -h filename;* 查看目錄大小: -hs dirname ; -h dirname是查看目錄下所有文件的大小* 查看內存的使用: free -m|-g|-k* 查看進程: ps -e 或ps -aux -->顯示用戶* 殺掉進程: kill pid* 強制殺掉: killall -9 processname4. 網路相關: * 配置 ADSL: sudo pppoeconf* ADSL手工撥號: sudo pon dsl-provider* 激活 ADSL : sudo /etc/ppp/pppoe_on_boot* 斷開 ADSL: sudo poff* 根據IP查網卡地址: arping IP地址* 產看本地網路信息(包括ip等): ifconfig | ifconfig eth0* 查看路由信息: netstat -r* 關閉網卡: sudo ifconfig eth0 down* 啟用網卡: sudo ifconfig eth0 up* 添加一個服務: sudo update-rc.d 服務名 defaults 99* 刪除一個服務: sudo update-rc.d 服務名 remove* 臨時重啟一個服務: /etc/init.d/服務名 restart* 臨時關閉一個服務: /etc/init.d/服務名 stop* 臨時啟動一個服務: /etc/init.d/服務名 start* 控制台下顯示中文: sudo apt-get install zhcon* 查找某個文件: whereis filename 或 find 目錄 -name 文件名*通過ssh傳輸文件scp -rp /path/filename username@remoteIP:/path #將本地文件拷貝到伺服器上scp -rp username@remoteIP:/path/filename /path #將遠程文件從伺服器下載到本地5. 壓縮:*解壓縮 a.tar.gz: tar zxvf a.tar.gz*解壓縮 a.tar.bz2: tar jxvf a.tar.bz2*壓縮aaa bbb目錄為xxx.tar.gz: tar zcvf xxx.tar.gz aaa bbb*壓縮aaa bbb目錄為xxx.tar.bz2: tar jcvf xxx.tar.bz2 aaa bbb
6. Nautilus:特殊 URI 地址* computer:/// - 全部掛載的設備和網路* network:/// - 瀏覽可用的網路* burn:/// - 一個刻錄 CDs/DVDs 的數據虛擬目錄* smb:/// - 可用的 windows/samba 網路資源* x-nautilus-desktop:/// - 桌面項目和圖標* file:/// - 本地文件* trash:/// - 本地回收站目錄* ftp:// - FTP 文件夾* ssh:// - SSH 文件夾* fonts:/// - 字體文件夾,可將字體文件拖到此處以完成安裝* themes:/// - 系統主題文件夾* 顯示隱藏文件: Ctrl+h* 顯示地址欄: Ctrl+l* 查看已安裝字體: 在nautilus的地址欄里輸入」fonts:///「,就可以查看本機所有的fonts
7.補充部分:
* 查看本地所有的tpc,udp監聽埠: netstat -tupln (t=tcp, u=udp, p=program, l=listen, n=numric)* 通過man搜說相關命令: man -k keyword . eg: man -k user* 或者用 apropos* 統計文件所佔用的實際磁碟空間: ( - estimate file space usage)* 統計文件中的字元,位元組數: wc -c/-l/-w (wc - print the number of newlines, words, and bytes in files)* 查看文件的內容: od -x/-c/.... (od - mp files in octal and other formats)我認為od最有用的就是文件的位元組流了: od -t x1 filename查看文件的 Ascii 碼形式: od -t c filename (其中統計信息最左邊的是: 位元組數)* 查找命令所在文件的位置: which od 輸出: /usr/bin/od查看該文件由哪個包提供: dpkg -S /usr/bin/od 輸出: coreutils: /usr/bin/od再查看coreutils包的全部內容就知道了linux的核心命令: dpkg -L coreutils然後 info coreutils 哈哈,認真學吧, 滿世界都是命令!* 可以用man 命令產看某個命令的所有section 的解釋: man -a tty然後用q,和next 轉換到下一個section的解釋* bash 的好用的快捷鍵:ctrl+a:游標移到行首。ctrl+b:游標左移一個字母ctrl+c:殺死當前進程。ctrl+d:退出當前 Shell。ctrl+e:游標移到行尾。ctrl+h:刪除游標前一個字元,同 backspace 鍵相同。ctrl+k:清除游標後至行尾的內容。ctrl+l:清屏,相當於clear。ctrl+r:搜索之前打過的命令。會有一個提示,根據你輸入的關鍵字進行搜索bash的historyctrl+u: 清除游標前至行首間的所有內容。ctrl+w: 移除游標前的一個單詞ctrl+t: 交換游標位置前的兩個字元ctrl+y: 粘貼或者恢復上次的刪除ctrl+d: 刪除游標所在字母;注意和backspace以及ctrl+h的區別,這2個是刪除游標前的字元ctrl+f: 游標右移ctrl+z : 把當前進程轉到後台運行,使用』 fg 『命令恢復。比如top -d1 然後ctrl+z ,到後台,然後fg,重新恢復* 快速粘貼:先在一個地方選中文字,在欲粘貼的地方按滑鼠 中鍵 即可。* 等效中鍵:a 、按下滑輪等效於中鍵。b、同時按下滑鼠 左右鍵,等效於中鍵。* 快速重啟X服務: 同時按下: Alt + Ctrl + Backspace 三個鍵。* 打開運行窗口: 同時按下 Alt + F2 鍵。* 戴屏: a、全屏:直接按下 PrtScr 鍵。b、當前窗口:同時按下 Alt + PrtScr 鍵。c、延時戴屏:在 終端 或 運行窗口中輸入命令: gnome-screenshot --delay 3 ,將延時 3 秒後戴屏。* 直接將 文件管理器 中的文件拖到 GNOME終端 中就可以在終端中得到完整的路徑名。 8.ulimitulimit:顯示(或設置)用戶可以使用的資源的限制(limit),這限制分為軟限制(當前限制)和硬限制(上限),其中硬限制是軟限制的上限值,應用程序在運行過程中使用的系統資源不超過相應的軟限制,任何的超越都導致進程的終止。ulimited 不限制用戶可以使用的資源,但本設置對可打開的最大文件數(max open files)和可同時運行的最大進程數(max user processes)無效-a 列出所有當前資源極限-c 設置core文件的最大值.單位:blocks-d 設置一個進程的數據段的最大值.單位:kbytes-f Shell 創建文件的文件大小的最大值,單位:blocks-h 指定設置某個給定資源的硬極限。如果用戶擁有 root 用戶許可權,可以增大硬極限。任何用戶均可減少硬極限-l 可以鎖住的物理內存的最大值-m 可以使用的常駐內存的最大值,單位:kbytes-n 每個進程可以同時打開的最大文件數-p 設置管道的最大值,單位為block,1block=512bytes-s 指定堆棧的最大值:單位:kbytes-S 指定為給定的資源設置軟極限。軟極限可增大到硬極限的值。如果 -H 和 -S 標志均未指定,極限適用於以上二者-t 指定每個進程所使用的秒數,單位:seconds-u 可以運行的最大並發進程數-v Shell可使用的最大的虛擬內存,單位:kbyteseg: ulimit -c 1000(可以先通過ulimit -c 查看原來的值)
10. linux內存管理程序C填空,內存回收過程及實現。求高手解
不知道你空間里的程序想說明啥
目前,glibc 的 malloc 函數是用夥伴演算法分配內存的,速度很快,不需要整理,當然,代價就是連續分配定長內存時會導致較大的內碎片
不過,就常規的內存應用來說,怎麼看都比這些"FF""BF""WF"好多了