zram:基於壓縮 RAM 的塊裝置

簡介

zram 模組建立名為 /dev/zram<id> (<id> = 0, 1, ...) 的基於 RAM 的塊裝置。寫入這些磁碟的頁面會被壓縮並存儲在記憶體本身中。這些磁碟允許非常快的 I/O,並且壓縮提供良好的記憶體節省。一些用例包括 /tmp 儲存、用作交換磁碟、/var 下的各種快取,可能還有更多。 :)

各個 zram 裝置的統計資訊透過 /sys/block/zram<id>/ 處的 sysfs 節點匯出。

用法

有幾種方法可以配置和管理 zram 裝置(-s)

  1. 使用 zram 和 zram_control sysfs 屬性

  2. 使用 util-linux 提供的 zramctl 實用程式 (util-linux@vger.kernel.org)。

在本文件中,我們將僅描述“手動” zram 配置步驟,即 zram 和 zram_control sysfs 屬性。

為了更好地瞭解 zramctl,請查閱 util-linux 文件、zramctl 手冊頁或 zramctl --help。請注意,zram 維護者不開發/維護 util-linux 或 zramctl,如果您有任何問題,請聯絡 util-linux@vger.kernel.org

以下顯示了使用 zram 的典型步驟序列。

警告

為了簡單起見,我們在下面的大多數示例中跳過了錯誤檢查部分。但是,您有責任處理錯誤。

如果出現錯誤,zram sysfs 屬性總是返回負值。可能的返回程式碼列表

-EBUSY

嘗試修改一旦裝置初始化後就無法更改的屬性。請先重置裝置。

-ENOMEM

zram 無法分配足夠的記憶體來滿足您的需求。

-EINVAL

提供了無效的輸入。

-EAGAIN

稍後重試操作(例如,嘗試同時執行重新壓縮和寫回時)。

如果您使用 'echo',則返回值由 'echo' 實用程式設定,並且在一般情況下,類似

echo foo > /sys/block/zram0/comp_algorithm
if [ $? -ne 0 ]; then
        handle_error
fi

應該足夠了。

1) 載入模組

modprobe zram num_devices=4

這將建立 4 個裝置:/dev/zram{0,1,2,3}

num_devices 引數是可選的,它告訴 zram 應預先建立多少個裝置。預設值:1。

2) 選擇壓縮演算法

使用 comp_algorithm 裝置屬性,您可以檢視可用和當前選定的(顯示在方括號中)壓縮演算法,或更改選定的壓縮演算法(一旦裝置初始化,就無法更改壓縮演算法)。

示例

#show supported compression algorithms
cat /sys/block/zram0/comp_algorithm
lzo [lz4]

#select lzo compression algorithm
echo lzo > /sys/block/zram0/comp_algorithm

目前,comp_algorithm 內容僅顯示 zram 支援的壓縮演算法。

3) 設定壓縮演算法引數:可選

壓縮演算法可能支援特定的引數,可以針對特定的資料集進行調整。 ZRAM 有一個 algorithm_params 裝置屬性,它提供每個演算法的引數配置。

例如,幾種壓縮演算法支援 level 引數。此外,某些壓縮演算法支援預訓練的字典,這會顯著改變演算法的特性。為了配置壓縮演算法以使用外部預訓練字典,請傳遞到 dict 的完整路徑以及其他引數

#pass path to pre-trained zstd dictionary
echo "algo=zstd dict=/etc/dictionary" > /sys/block/zram0/algorithm_params

#same, but using algorithm priority
echo "priority=1 dict=/etc/dictionary" > \
        /sys/block/zram0/algorithm_params

#pass path to pre-trained zstd dictionary and compression level
echo "algo=zstd level=8 dict=/etc/dictionary" > \
        /sys/block/zram0/algorithm_params

引數是演算法特定的:並非所有演算法都支援預訓練的字典,並非所有演算法都支援 level。此外,對於某些演算法,level 控制壓縮級別(值越高,壓縮率越好,對於某些演算法,它甚至可以採用負值),對於其他演算法,level 是加速級別(值越高,壓縮率越低)。

4) 設定磁碟大小

透過將值寫入 sysfs 節點 'disksize' 來設定磁碟大小。該值可以是位元組,也可以使用 mem 字尾。示例

# Initialize /dev/zram0 with 50MB disksize
echo $((50*1024*1024)) > /sys/block/zram0/disksize

# Using mem suffixes
echo 256K > /sys/block/zram0/disksize
echo 512M > /sys/block/zram0/disksize
echo 1G > /sys/block/zram0/disksize

注意:建立一個大於記憶體大小兩倍的 zram 意義不大,因為我們期望 2:1 的壓縮率。請注意,zram 在不使用時會佔用大約 0.1% 的磁碟大小,因此巨大的 zram 是浪費的。

5) 設定記憶體限制:可選

透過將值寫入 sysfs 節點 'mem_limit' 來設定記憶體限制。該值可以是位元組,也可以使用 mem 字尾。此外,您可以在執行時更改該值。示例

# limit /dev/zram0 with 50MB memory
echo $((50*1024*1024)) > /sys/block/zram0/mem_limit

# Using mem suffixes
echo 256K > /sys/block/zram0/mem_limit
echo 512M > /sys/block/zram0/mem_limit
echo 1G > /sys/block/zram0/mem_limit

# To disable memory limit
echo 0 > /sys/block/zram0/mem_limit

6) 啟用

mkswap /dev/zram0
swapon /dev/zram0

mkfs.ext4 /dev/zram1
mount /dev/zram1 /tmp

7) 新增/刪除 zram 裝置

zram 提供了一個控制介面,可以動態(按需)新增和刪除裝置。

為了新增新的 /dev/zramX 裝置,請對 hot_add 屬性執行讀取操作。這將返回新裝置的裝置 ID(意味著您可以使用 /dev/zram<id>)或錯誤程式碼。

示例

cat /sys/class/zram-control/hot_add
1

要刪除現有的 /dev/zramX 裝置(其中 X 是裝置 ID),請執行

echo X > /sys/class/zram-control/hot_remove

8) 統計資訊

每個裝置的統計資訊作為 /sys/block/zram<id>/ 下的各種節點匯出

下面是匯出的裝置屬性的簡要說明。有關更多詳細資訊,請閱讀 ABI 檔案測試/sysfs-block-zram

名稱

訪問

描述

disksize

RW

顯示和設定裝置的磁碟大小

initstate

RO

顯示裝置的初始化狀態

reset

WO

觸發裝置重置

mem_used_max

WO

重置 mem_used_max 計數器(稍後會介紹)

mem_limit

WO

指定 ZRAM 可以用來儲存壓縮資料的最大記憶體量

writeback_limit

WO

指定 zram 可以以 4KB 為單位寫入後備裝置的最大寫入 IO 量

writeback_limit_enable

RW

顯示和設定 writeback_limit 功能

comp_algorithm

RW

顯示和更改壓縮演算法

algorithm_params

WO

設定壓縮演算法引數

compact

WO

觸發記憶體壓縮

debug_stat

RO

此檔案用於 zram 除錯目的

backing_dev

RW

設定 zram 寫入的後端儲存

idle

WO

將分配的插槽標記為空閒

建議使用者空間使用以下檔案來讀取裝置統計資訊。

檔案 /sys/block/zram<id>/stat

表示塊層統計資訊。閱讀 /sys/block/<dev>/stat 中的塊層統計資訊 瞭解詳細資訊。

檔案 /sys/block/zram<id>/io_stat

stat 檔案表示裝置未由塊層統計的 I/O 統計資訊,因此在 zram<id>/stat 檔案中不可用。它由單行文字組成,包含以下以空格分隔的統計資訊

failed_reads

失敗的讀取次數

failed_writes

失敗的寫入次數

invalid_io

非頁面大小對齊的 I/O 請求數

notify_free

根據裝置的使用場景,它可以統計

  1. 由於交換插槽釋放通知而釋放的頁面數

  2. 由於 bio 傳送的 REQ_OP_DISCARD 請求而釋放的頁面數。前者在交換插槽被釋放時傳送到交換塊裝置,這意味著該磁碟正在用作交換磁碟。

後者由以 discard 選項掛載的檔案系統傳送,每當一些資料塊被丟棄時。

檔案 /sys/block/zram<id>/mm_stat

mm_stat 檔案表示裝置的 mm 統計資訊。它由單行文字組成,包含以下以空格分隔的統計資訊

orig_data_size

儲存在此磁碟中的資料的未壓縮大小。單位:位元組

compr_data_size

儲存在此磁碟中的資料的壓縮大小

mem_used_total

為此磁碟分配的記憶體量。這包括為此磁碟分配的分配器碎片和元資料開銷。因此,可以使用 compr_data_size 和此統計資訊來計算分配器空間效率。單位:位元組

mem_limit

ZRAM 可以用來儲存壓縮資料的最大記憶體量

mem_used_max

zram 已消耗的最大記憶體量,用於儲存資料

same_pages

寫入此磁碟的相同元素填充頁面的數量。沒有為此類頁面分配記憶體。

pages_compacted

壓縮期間釋放的頁面數

huge_pages

不可壓縮的頁面數

huge_pages_since

自 zram 設定以來不可壓縮的頁面數

檔案 /sys/block/zram<id>/bd_stat

bd_stat 檔案表示裝置的後備裝置統計資訊。它由單行文字組成,包含以下以空格分隔的統計資訊

bd_count

寫入後備裝置的資料大小。單位:4K 位元組

bd_reads

從後備裝置讀取的次數。單位:4K 位元組

bd_writes

寫入後備裝置的次數。單位:4K 位元組

9) 停用

swapoff /dev/zram0
umount /dev/zram1

10) 重置

將任何正值寫入 'reset' sysfs 節點

echo 1 > /sys/block/zram0/reset
echo 1 > /sys/block/zram1/reset

這將釋放為給定裝置分配的所有記憶體,並將磁碟大小重置為零。在重新使用裝置之前,您必須再次設定磁碟大小。

可選功能

IDLE 頁面跟蹤

zram 內建了對空閒頁面跟蹤(即已分配但未使用的頁面)的支援。此功能對於例如 zram 寫回和重新壓縮很有用。為了將頁面標記為空閒,請執行以下命令

echo all > /sys/block/zramX/idle

這將把所有已分配的 zram 頁面標記為空閒。只有在訪問頁面(塊)(例如,被覆蓋或釋放)時才會刪除空閒標記。此外,當啟用 CONFIG_ZRAM_TRACK_ENTRY_ACTIME 時,可以根據自上次訪問特定 zram 頁面以來經過的秒數將頁面標記為空閒

echo 86400 > /sys/block/zramX/idle

在此示例中,所有超過 86400 秒(一天)未被訪問的頁面都將被標記為空閒。

寫回

使用 CONFIG_ZRAM_WRITEBACK,zram 可以將空閒/不可壓縮的頁面寫入後備儲存,而不是將其儲存在記憶體中。要使用此功能,管理員應透過以下方式設定後備裝置

echo /dev/sda5 > /sys/block/zramX/backing_dev

在設定 disksize 之前。目前僅支援分割槽。如果管理員想要使用不可壓縮的頁面寫回,他們可以透過以下方式進行

echo huge > /sys/block/zramX/writeback

管理員可以在正確的時間透過以下方式請求寫回空閒頁面

echo idle > /sys/block/zramX/writeback

使用該命令,zram 會將空閒頁面從記憶體寫回到儲存。

此外,如果使用者選擇僅寫回巨大和空閒頁面,則可以透過以下方式完成

echo huge_idle > /sys/block/zramX/writeback

如果使用者選擇僅寫回不可壓縮的頁面(沒有任何演算法可以壓縮的頁面),則可以透過以下方式完成

echo incompressible > /sys/block/zramX/writeback

如果管理員想要將 zram 裝置中的特定頁面寫入後備裝置,他們可以將頁面索引寫入該介面

echo "page_index=1251" > /sys/block/zramX/writeback

在 Linux 6.16 中,此介面進行了一些修改。首先,該介面現在支援所有引數的 key=value 格式(type=huge_idle 等)。其次,引入了對 page_indexes 的支援,它指定要寫回的頁面的 LOW-HIGH 範圍(或範圍)。這減少了 syscall 的數量,但更重要的是,這實現了最佳的後處理目標選擇策略。用法示例

echo "type=idle" > /sys/block/zramX/writeback
echo "page_indexes=1-100 page_indexes=200-300" > \
        /sys/block/zramX/writeback

我們現在還允許每個呼叫使用多個 page_index 引數,以及單個頁面和頁面範圍的混合

echo page_index=42 page_index=99 page_indexes=100-200 \
        page_indexes=500-700 > /sys/block/zramX/writeback

如果有大量使用快閃記憶體裝置的寫入 IO,則可能會出現快閃記憶體磨損問題,因此管理員需要設計寫入限制,以保證整個產品生命週期的儲存健康。

為了克服此問題,zram 支援“writeback_limit”功能。“writeback_limit_enable”的預設值為 0,因此它不限制任何寫回。也就是說,如果管理員想要應用寫回預算,他們應該透過以下方式啟用 writeback_limit_enable

$ echo 1 > /sys/block/zramX/writeback_limit_enable

一旦設定了 writeback_limit_enable,zram 就不允許任何寫回,直到管理員透過 /sys/block/zramX/writeback_limit 設定預算。

(如果管理員不啟用 writeback_limit_enable,則透過 /sys/block/zramX/writeback_limit 分配的 writeback_limit 值沒有意義。)

如果管理員想要將每天的寫回限制為 400M,他們可以像下面這樣做

$ MB_SHIFT=20
$ 4K_SHIFT=12
$ echo $((400<<MB_SHIFT>>4K_SHIFT)) > \
        /sys/block/zram0/writeback_limit.
$ echo 1 > /sys/block/zram0/writeback_limit_enable

如果管理員想要在預算耗盡後再次允許進一步寫入,他們可以像下面這樣做

$ echo $((400<<MB_SHIFT>>4K_SHIFT)) > \
        /sys/block/zram0/writeback_limit

如果管理員想要檢視自上次設定以來剩餘的寫回預算

$ cat /sys/block/zramX/writeback_limit

如果管理員想要停用寫回限制,他們可以這樣做

$ echo 0 > /sys/block/zramX/writeback_limit_enable

每當您重置 zram 時(例如,系統重啟,echo 1 > /sys/block/zramX/reset),writeback_limit 計數將重置,因此,保留在重置 zram 之前發生的寫回次數,以便在下次設定中分配額外的寫回預算是使用者的工作。

如果管理員想要測量某個時間段內的寫回計數,他們可以透過 /sys/block/zram0/bd_stat 的第 3 列了解它。

重新壓縮

使用 CONFIG_ZRAM_MULTI_COMP,zram 可以使用備用(輔助)壓縮演算法重新壓縮頁面。基本思想是備用壓縮演算法可以提供更好的壓縮率,但代價是(可能)較慢的壓縮/解壓縮速度。備用壓縮演算法可以更成功地壓縮大頁面(那些預設演算法無法壓縮的頁面)。另一種應用是空閒頁面重新壓縮 - 可以使用更有效的演算法重新壓縮冷且位於記憶體中的頁面,從而減少 zsmalloc 記憶體使用。

使用 CONFIG_ZRAM_MULTI_COMP,zram 支援最多 4 種壓縮演算法:一種主演算法和最多 3 種輔助演算法。主 zram 壓縮器在“3) 選擇壓縮演算法”中進行了解釋,輔助演算法使用 recomp_algorithm 裝置屬性進行配置。

示例:

#show supported recompression algorithms
cat /sys/block/zramX/recomp_algorithm
#1: lzo lzo-rle lz4 lz4hc [zstd]
#2: lzo lzo-rle lz4 [lz4hc] zstd

備用壓縮演算法按優先順序排序。在上面的示例中,zstd 用作第一種備用演算法,其優先順序為 1,而 lz4hc 配置為優先順序為 2 的壓縮演算法。備用壓縮演算法的優先順序在演算法配置期間提供:

#select zstd recompression algorithm, priority 1
echo "algo=zstd priority=1" > /sys/block/zramX/recomp_algorithm

#select deflate recompression algorithm, priority 2
echo "algo=deflate priority=2" > /sys/block/zramX/recomp_algorithm

CONFIG_ZRAM_MULTI_COMP 啟用的另一個裝置屬性是 recompress,它控制重新壓縮。

示例:

#IDLE pages recompression is activated by `idle` mode
echo "type=idle" > /sys/block/zramX/recompress

#HUGE pages recompression is activated by `huge` mode
echo "type=huge" > /sys/block/zram0/recompress

#HUGE_IDLE pages recompression is activated by `huge_idle` mode
echo "type=huge_idle" > /sys/block/zramX/recompress

空閒頁面的數量可能很大,因此使用者空間可以將大小閾值(以位元組為單位)傳遞給 recompress knob:zram 將僅重新壓縮大小等於或大於該大小的頁面:

#recompress all pages larger than 3000 bytes
echo "threshold=3000" > /sys/block/zramX/recompress

#recompress idle pages larger than 2000 bytes
echo "type=idle threshold=2000" > /sys/block/zramX/recompress

也可以限制 zram 重新壓縮將嘗試重新壓縮的頁面數量:

echo "type=huge_idle max_pages=42" > /sys/block/zramX/recompress

在重新壓縮期間,對於每個符合重新壓縮標準的頁面,ZRAM 按照其優先順序的順序迭代已註冊的備用壓縮演算法列表。當重新壓縮成功(重新壓縮的物件小於原始物件)並符合重新壓縮標準(例如,大小閾值)或沒有剩餘的輔助演算法可嘗試時,ZRAM 停止。如果沒有輔助演算法可以成功地重新壓縮頁面,則將該頁面標記為不可壓縮,因此 ZRAM 將來不會嘗試重新壓縮它。

當它迭代已註冊的壓縮演算法列表時,這種重新壓縮行為增加了我們找到成功壓縮特定頁面的演算法的機會。但是,有時限制重新壓縮到僅一種特定演算法是很方便(有時甚至是必要的),這樣它就不會嘗試任何其他演算法。這可以透過提供 algopriority 引數來實現:

#use zstd algorithm only (if registered)
echo "type=huge algo=zstd" > /sys/block/zramX/recompress

#use zstd algorithm only (if zstd was registered under priority 1)
echo "type=huge priority=1" > /sys/block/zramX/recompress

記憶體跟蹤

使用 CONFIG_ZRAM_MEMORY_TRACKING,使用者可以瞭解 zram 塊的資訊。這可能有助於使用 *pagemap 捕獲程序的冷頁面或不可壓縮頁面。

如果啟用此功能,您可以透過 /sys/kernel/debug/zram/zram0/block_state 檢視塊狀態”。輸出如下

300    75.033841 .wh...
301    63.806904 s.....
302    63.806919 ..hi..
303    62.801919 ....r.
304   146.781902 ..hi.n
第一列

zram 的塊索引。

第二列

自系統啟動以來的訪問時間

第三列

塊的狀態

s

相同頁面

w

已寫入後備儲存的頁面

h

大頁面

i

空閒頁面

r

重新壓縮的頁面(輔助壓縮演算法)

n

沒有任何(包括輔助)演算法可以壓縮它

上面示例的第一行表示在 75.033841 秒訪問了第 300 個塊,並且該塊的狀態很大,因此它被寫回到後備儲存。這是一個除錯功能,因此任何人都不應依賴它來正常工作。

Nitin Gupta ngupta@vflare.org