Linux

當頁面未交換時,是否在 zram 空閒支持頁面上進行交換?

  • May 20, 2022

想像一下將 2GiB 換出到 zram 並壓縮到 1GiB 的場景。

一旦記憶體壓力減輕並且 2GiB 逐漸變得未交換,Linux 是否會釋放 1GiB 用於儲存壓縮 zram 頁面的頁面?

如果是這樣,它是否會對現有頁面進行碎片整理?

壓縮頁面中必須有多個頁面,當所有頁面都未交換時會發生什麼?是否所有頁面都保留在記憶體中,直到最後一頁也被釋放?

簡短回答:是的,zram 支持頁面會自動釋放。

通過實驗(核心 5.10.105)檢查後,似乎未使用的 zram 儲存被自動釋放,即使 zram 設備安裝時沒有discard.

**摘要:**下面的腳本執行一個分配大量記憶體的程序。

zram 使用量(通過 檢查zramctl)最初會增加,然後在停止程序並驅逐交換頁面後回到基線。

# * I've run this on a freshly booted VM *

# zram is mounted with nodiscard to exclude any effects of
# `discard`.
sudo grep zram /etc/fstab
# /dev/zram0 none swap nodiscard,pri=5

# We have ~6 GiB of RAM
grep -i memtotal /proc/meminfo
# MemTotal:        6386852 kB

# Show zram usage.
# `DATA` is the total amount of uncompressed data currently stored in zram.
zramctl
# NAME       ALGORITHM DISKSIZE DATA COMPR TOTAL STREAMS MOUNTPOINT
# /dev/zram0 zstd          3.1G   4K   58B    4K       4 [SWAP]

# Start a process that allocates 10 GiB of RAM
stress-ng -- --vm-bytes $((10*1024**3)) --vm-keep --vm 1 &

# *Wait some time for the stress test command to be swapped out*

# zram usage has gone up from 4 KiB to 3.1 GiB
zramctl
# NAME       ALGORITHM DISKSIZE  DATA COMPR TOTAL STREAMS MOUNTPOINT
# /dev/zram0 zstd          3.1G  3.1G  1.1G  1.2G       4 [SWAP]

# Stop stress test
kill %1

# zram usage decreased from 3.1 GiB to 0.3 GiB
zramctl
# NAME       ALGORITHM DISKSIZE   DATA COMPR TOTAL STREAMS MOUNTPOINT
# /dev/zram0 zstd          3.1G 336.8M 48.6M 57.6M       4 [SWAP]

# Read the first byte of all memory pages of all processes.
# This evicts all non-kernel swapped pages without using `swapoff`, which might
# reset the zram device.
sudo ./read_all_mem_pages.rb

# Now zram usage is almost back to zero
zramctl
# NAME       ALGORITHM DISKSIZE  DATA COMPR TOTAL STREAMS MOUNTPOINT
# /dev/zram0 zstd          3.1G 18.4M  3.5M  5.9M       4 [SWAP]

來源read_all_mem_pages.rb

#!/usr/bin/env ruby

def access_all_pages(pid)
 name = File.basename(File.readlink("/proc/#{pid}/exe")) rescue return
 puts "#{pid} (#{name})"
 File.open("/proc/#{pid}/mem", 'r') do |mem|
   for_each_mem_page(pid) do |page_address|
     mem.seek(page_address)
     mem.read(1) rescue nil
   end
 end
end

def for_each_mem_page(pid)
 File.foreach("/proc/#{pid}/maps") do |line|
   fields = line.split
   range, dest = fields[0], fields[-1]
   next if dest == "[vsyscall]"
   start, end_ = range.split('-').map { |x| x.to_i(16) }
   address = start
   while address < end_
     yield address
     address += 4096
   end
 end
end

pids = Dir.children('/proc').grep(/^\d+$/).map(&:to_i)
pids.each { |pid| access_all_pages(pid) }

預設情況下我會說不,但可以這樣做。

$ lsblk --discard /dev/zram0 
NAME  DISC-ALN DISC-GRAN DISC-MAX DISC-ZERO
zram0        0        4K       2T         0

這意味著zram0是一個具有丟棄能力的設備。

來自man swapon

-d,--discard[=policy]

如果交換支持設備支持丟棄或修剪操作,則啟用交換丟棄。

$$ … $$
/etc/fstab掛載選項 ,discarddiscard=oncediscard=pages可用於啟用丟棄標誌。

人們只需要弄清楚執行swapon命令或等效命令的特定 Linux 發行版,並在此位置(或至少是變體)相應地添加swapon --discard選項或mount選項。 discard``=pages

引用自:https://unix.stackexchange.com/questions/690457