linux 將物理內存分為內存段,叫做頁面。交換是指內存頁面被復制到預先設定好的硬盤空間(叫做交換空間)的過程,目的是釋放這份內存頁面。物理內存和交換空間的總大小是可用的虛擬內存的總量。
什么是 Swap
我們知道 swap space 是磁盤上的一塊區域,可以是一個分區,也可以是一個文件,或者以它們的組合方式出現。簡單點說,當系統物理內存吃緊時,Linux 系統會將內存中不常訪問的數據保存到 swap 上,這樣系統就有更多的物理內存為其他進程服務,而當系統需要訪問 swap 上存儲的內容時,系統會再將 swap 上的數據加載到內存中,這就是我們常說的 swap out 和 swap in 了。
Swap 原理
Swap 說白了就是把一塊磁盤空間或者一個本地文件(以下講解以磁盤為例),當成內存來使用。它包括換出和換入兩個過程。
- 所謂換出,就是把進程暫時不用的內存數據存儲到磁盤中,并釋放這些數據占用的內存。
- 而換入,則是在進程再次訪問這些內存的時候,把它們從磁盤讀到內存中來。
一個很典型的場景就是,即使內存不足時,有些應用程序也并不想被 OOM 殺死,而是希望能緩一段時間,等待人工介入,或者等系統自動釋放其他進程的內存,再分配給它。
除此之外,我們常見的筆記本電腦的休眠和快速開機的功能,也基于 Swap 。休眠時,把系統的內存存入磁盤,這樣等到再次開機時,只要從磁盤中加載內存就可以。這樣就省去了很多應用程序的初始化過程,加快了開機速度。
Swap 的優缺點
[1] 主要優點如下所示
對于一些大型的應用程序(如Libreoffice等),在啟動的過程中會使用大量的內存,但這些內存很多時候只是在啟動的時候用一下,后面的運行過程中很少再用到這些內存。有了 swap 之后,系統就可以將這部分不這么使用的內存數據保存到 swap 上去,從而釋放出更多的物理內存供系統使用。
很多發行版(如ubuntu)的休眠功能依賴于 swap 分區,當系統休眠的時候,會將內存中的數據保存到 swap 分區上,等下次系統啟動的時候,再將數據加載到內存中,這樣可以加快系統的啟動速度,所以如果要使用休眠的功能,必須要配置 swap 分區,并且大小一定要大于等于物理內存在某些情況下,物理內存有限,但又想運行耗內存的程序怎么辦?這時可以通過配置足夠的 swap 空間來達到目標,雖然慢一點,但至少可以運行。
[2] 主要缺點如下所示
swap 是存放在磁盤上的,磁盤的速度和內存比較起來慢了好幾個數量級,如果不停的讀寫 swap,那么對系統的性能肯定有影響,尤其是當系統內存很吃緊的時候,讀寫 swap 空間發生的頻率會很高,導致系統運行很慢,像死了一樣,這個時候添加物理內存是唯一的解決辦法。
Swap 的大小配置
既然配置 swap 對桌面系統有幫助,那么配置多少大小的 swap 比較合適呢?下面是 ubuntu 給出的建議:
- 當物理內存小于 1G 且不需要休眠時,設置和內存同樣大小的 swap 空間即可;當需要休眠時,建議配置兩倍物理內存的大小,但最大值不要超過兩倍內存大小。
- 當物理內存大于 1G 且不需要休眠時,建議大小為 sqrt(RAM),其中 RAM 為物理內存大小;當需要休眠時,建議大小是 RAM+round(sqrt(RAM)),但最大值不要超過兩倍內存大小。
- 如果兩倍物理內存大小的 swap 空間還不夠用,建議增加內存而不是增加swap。
Swap 常用操作
當我們確定好配置多大的 swap 空間后,具體應該怎么配置呢?Linux 下有兩種類型的 swap 空間,swap 分區和 swap 文件,它們有各自的特點:
swap 分區:
- swap 分區上面由于沒有文件系統,所以相當于內核直接訪問連續的磁盤空間,效率相對要高點,但由于 swap 分區一般安裝系統時就分配好了,后期要縮減空間和擴容都很不方便。
swap 文件:
- swap 文件放在指定分區的文件系統里面,所以有可能受文件系統性能的影響,但據說2.6版本以后的內核可以直接訪問swap文件對應的物理磁盤地址,相當于跳過了文件系統直接訪問磁盤。不過如果 swap 文件在磁盤上的物理位置不連續時,還是會對性能產生不利影響,但其優點就是靈活,隨時可以增加和移除swap文件。
交換分區在物理內存被填滿時用來保持內存中的內容。當 RAM 被耗盡,Linux 會將內存中不活動的頁移動到交換空間中,從而空出內存給系統使用。雖然如此,但交換空間不應被認為是物理內存的替代品。大多數情況下,建議交換內存的大小為物理內存的 1 到 2 倍。也就是說如果你有 8GB 內存, 那么交換空間大小應該介于 8-16GB。
查看系統中已經配置的 swap 分配情況
# Filename: 類型是分區則顯示分區路徑,類型是文件則顯示文件路徑
# Type: partition代表是一個swap分區,file代表是一個swap文件
# Size: 顯示swap的大小,默認單位是KB
# Used: 已經被使用的大小,0表示還沒有被使用到
# Priority: 優先級高將會被優先使用,同等優先級將會均勻使用(設置: swapon -p)
escape@App:~$ swapon -s
Filename Type Size Used Priority
/data/.swapfile file 10485756 6534248 -1
/data1/.swapfile file 10485756 3246088 -2
# 指定交換區的優先順序
$ sudo swapon -p xxx
# 啟動某個交換swap
$ sudo swapon /dev/sda2
# 啟動所有系統配置的swap
$ sudo swapon -a
# 關閉某個交換swap
$ sudo swapoff /dev/sda2
# 關閉所有系統配置的swap
$ sduo swapoff -a
固定使永久生效
# 寫入磁盤配置文件
# <file system> <mount point> <type> <options> <dump> <pass>
$ cat /etc/fstab
/data1/.swapfile none swap sw 0 0
/data1/.swapfile2 none swap sw 0 0
查看系統中 swap in/out 的情況
# 并不是swap空間占用多就一定性能下降
# 真正影響性能是swap in和out的頻率,頻率越高對系統的性能影響越大
escape@app:~$ vmstat 2
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
3 1 9795592 2037192 282460 14034552 8 8 51 46 0 0 10 1 88 0 0
3 0 9795592 2025832 282472 14044688 0 0 68279 270 5416 6425 35 6 54 5 0
擴大swap
1.先查看一下自己的服務器swap大小,命令:
2.使用 cd /usr 進入 /usr 文件夾,新建一個名叫swap的文件夾,使用ll命令可以看到多了一個swap的文件夾
3.下一步使用 cd swap 進入swap文件夾,創建swap文件
swapfile文件創建后,需要構建swap格式于/usr/swap/swapfile 上
用命令激活swap,立即啟用交換分區文件
配置 Swap 緩存
操作系統使用 ZFS 文件系統
添加 swap 分區
# 1.創建邏輯卷
$ sudo zpool create -V 2G rpool/swap
# 2.使用mkswap命令來格式化交換分區
$ sudo mkswap -f rpool/swap
# 3.激活新建的交換分區
swapon -a /dev/zvol/dsk/rpool/swap2
# 4.永久生效
$ sudo vi /etc/fstab
/dev/zvol/dsk/rpool/swap2 swap swap default 0 0
取消 swap 配置
# 1.關閉某個交換swap
$ sudo swapoff /dev/zvol/dsk/rpool/swap2
# 2.刪除邏輯卷
$ sudo zpool destroy rpool/swap
# 3.修改/etc/fstab
將添加的Swap記錄一并刪除,否則下次重啟后,系統又會重新掛載相應的swap分區和文件
系統資源緊張時的內存分配
有新的大塊內存分配請求,但是剩余內存不足。這個時候系統就需要回收一部分內存(比如前面提到的緩存),進而盡可能地滿足新內存請求。這個過程通常被稱為直接內存回收。
除了直接內存回收,還有一個專門的內核線程用來定期回收內存,也就是kswapd0。為了衡量內存的使用情況,kswapd0 定義了三個內存閾值(watermark,也稱為水位),分別是
頁最小閾值(pages_min)、頁低閾值(pages_low)和頁高閾值(pages_high)。剩余內存,則使用 pages_free 表示。
這里,我畫了一張圖表示它們的關系。
kswapd0 定期掃描內存的使用情況,并根據剩余內存落在這三個閾值的空間位置,進行內存的回收操作。
剩余內存小于頁最小閾值,說明進程可用內存都耗盡了,只有內核才可以分配內存。
剩余內存落在頁最小閾值和頁低閾值中間,說明內存壓力比較大,剩余內存不多了。這時 kswapd0 會執行內存回收,直到剩余內存大于高閾值為止。
剩余內存落在頁低閾值和頁高閾值中間,說明內存有一定壓力,但還可以滿足新內存請求。
剩余內存大于頁高閾值,說明剩余內存比較多,沒有內存壓力。
我們可以看到,一旦剩余內存小于頁低閾值,就會觸發內存的回收。這個頁低閾值,其實可以通過內核選項
/proc/sys/vm/min_free_kbytes 來間接設置。
Linux 提供了一個 /proc/sys/vm/swappiness 選項,用來調整使用 Swap 的積極程度。
swappiness 的范圍是 0-100,數值越大,越積極使用 Swap,也就是更傾向于回收匿名頁;數值越小,越消極使用 Swap,也就是更傾向于回收文件頁。
提高或降低swap使用的方法
在內存資源緊張時,Linux 會通過 Swap ,把不常訪問的匿名頁換出到磁盤中,下次訪問的時候再從磁盤換入到內存中來。你可以設置
/proc/sys/vm/min_free_kbytes,來調整系統定期回收內存的閾值;也可以設置 /proc/sys/vm/swappiness,來調整文件頁和匿名頁的回收傾向。
當 Swap 變高時,你可以用 sar、/proc/zoneinfo、/proc/pid/status 等方法,查看系統和進程的內存使用情況,進而找出 Swap 升高的根源和受影響的進程。
反過來說,通常,降低 Swap 的使用,可以提高系統的整體性能。要怎么做呢?這里,我也總結了幾種常見的降低方法。
- 禁止 Swap,現在服務器的內存足夠大,所以除非有必要,禁用 Swap 就可以了。隨著云計算的普及,大部分云平臺中的虛擬機都默認禁止 Swap。
- 如果實在需要用到 Swap,可以嘗試降低 swappiness 的值,減少內存回收時 Swap 的使用傾向。
- 響應延遲敏感的應用,如果它們可能在開啟 Swap 的服務器中運行,你還可以用庫函數 mlock() 或者 mlockall() 鎖定內存,阻止它們的內存換出。
swappiness參數的含義
swappiness是Linux的一個內核參數,控制系統在進行swap時,內存使用的相對權重。
swappiness參數值可設置范圍在0到100之間。 此參數值越低,就會讓Linux系統盡量少用swap分區,多用內存;參數值越高就是反過來,使內核更多的去使用swap空間。Ubuntu系統swappiness默認值為60,表示的含義可以這樣來理解,當剩余物理內存低于40%(40=100-60)時,開始使用swap分區。centos系統此參數的默認值是30。我個人喜歡將作為服務器的Linux系統的swappiness參數設置為10。設置為100可能會影響整體性能,如果內存充足,就可以將這個值設置很低,甚至為0,以避免系統進行swap而影響性能。
swappiness=0究竟意味著什么?
我們都知道,Linux的進程使用的內存分為2種:
- file-backed pages(有文件背景的頁面,比如代碼段、比如read/write方法讀寫的文件、比如mmap讀寫的文件,它們有對應的硬盤文件,因此如果要交換,可以直接和硬盤對應的文件進行交換;比如讀取一個文件,沒有關閉,也沒有修改,交換時,就可以將這個文件直接放回硬盤,代碼處理其實就是刪除這部分內容,只保留一個索引,讓系統知道這個文件還處于打開狀態,只是它的內容不在內存,還在硬盤上),此部分頁面叫做page cache;
- anonymous pages(匿名頁,如stack,heap,CoW后的數據段等;他們沒有對應的硬盤文件,因此如果要交換,只能交換到swap分區),此部分頁面,如果系統內存不充分,可以被swap到swapfile或者硬盤的swap分區。
因此,Linux在進行內存回收(memory reclaim)的時候,實際上可以從1類和2類這兩種頁面里面進行回收,而swappiness值就決定了回收這2類頁面的優先級。swappiness越大,越傾向于回收匿名頁;swappiness越小,越傾向于回收file-backed的頁面。當然,它們的回收方法都是一樣的LRU算法。
修改swappiness的值
查看你的系統里面的swappiness
修改swappiness值為10
但是這只是臨時性的修改,在你重啟系統后會恢復默認的60,所以,還要做一步:
linux查找占用swap的進程的腳本
#!/bin/bash
########################################
# 腳本功能 : 列出正在占用swap的進程。
########################################
echo -e "PIDtSwaptProc_Name"
# 拿出/proc目錄下所有以數字為名的目錄(進程名是數字才是進程,其他如sys.NET等存放的是其他信息)
for pid in `ls -l /proc|awk '/^d/ {print $NF}'|grep ^[0-9]`
do
# Do not check init process
if [ $pid -eq 1 ];then continue;fi
# 判斷改進程是否占用了swap
grep -q "Swap" /proc/$pid/smaps 2>/dev/null
if [ $? -eq 0 ];then # 如果占用了swap
swap=$(grep Swap /proc/$pid/smaps| gawk '{ sum+=$2} END{ print sum }')
# 進程名
#proc_name=$(ps aux | grep -w "$pid" | grep -v grep
proc_name=$(ps -eo pid,comm | grep -w "$pid" | grep -v grep|awk '{print $NF}')
if [ $swap -ge 0 ];then # 如果占用了swap則輸出其信息
echo -e "$pidt${swap}t$proc_name"
fi
fi
done | sort -k2 -n | gawk -F't' '{
pid[NR]=$1;
size[NR]=$2;
name[NR]=$3;
}
END{
for(id=1;id<=length(pid);id++)
{
if(size[id]<1024)
printf("%-10st%10sKBt%sn",pid[id],size[id],name[id]);
else if(size[id]<1048576)
printf("%-10st%10.2fMBt%sn",pid[id],size[id]/1024,name[id]);
else
printf("%-10st%10.2fGBt%sn",pid[id],size[id]/1048576,name[id]);
}
}'
釋放緩存區內存的方法
a)清理pagecache(頁面緩存)
1
# echo 1 > /proc/sys/vm/drop_caches 或者 # sysctl -w vm.drop_caches=1
b)清理dentries(目錄緩存)和inodes
1
# echo 2 > /proc/sys/vm/drop_caches 或者 # sysctl -w vm.drop_caches=2
c)清理pagecache、dentries和inodes
1
# echo 3 > /proc/sys/vm/drop_caches 或者 # sysctl -w vm.drop_caches=3