en

blog

Trang nén Mac nhảy từ 5% lên 30% vì một vòng lặp chờ log

Một subagent của Claude Code chờ log e2e bằng vòng lặp đọc /dev/zero. Một process bash phình tới 47,7 GB, số trang nén của Mac nhảy từ 5% lên 30% giới hạn trong khi RAM vẫn báo trống 71%.

Ngày đăng
macOSClaude CodeKernel PanicBash
Cửa sổ terminal có một vòng lặp đọc input toàn số 0, bên dưới là 2 thanh đo, thanh RAM khả dụng vẫn dài còn thanh số trang bộ nén tô gradient mới tới gần một phần ba giới hạn

Ngày 02/11/2026, số trang trong bộ nén của Mac tôi nhảy từ 5% lên 30% giới hạn chỉ trong vài phút, trong khi RAM vẫn báo khả dụng 71%. Thủ phạm là một process bash do subagent của Claude Code tự sinh ra để chờ log, và vmmap báo Physical footprint của nó là 47,7 GB trên một máy 32 GB.

Đây là bài thứ ba trong chuỗi về giới hạn số trang nén của macOS, sau kernel panic data.kalloc.1024 và vụ WebKit của Playwright. Lần này máy không panic, nhưng nguyên nhân khác hẳn 2 lần trước.

13:05 đến 13:14: trang nén từ 5% lên 30%, RAM vẫn trống 71%

Máy là Mac Studio 32 GB, macOS 26, khoảng 10 phiên Claude Code chạy song song trong tmux. Dashboard theo dõi báo "Trang nén" từ 5% lên 27 đến 30% giới hạn trong khoảng 13:05 đến 13:14.

Giới hạn đó là sysctl vm.compressor.pages_compressed_limit, trên máy này bằng 13.192.448 trang. Mốc 85% là ngưỡng script canh xin khởi động lại, còn 100% là panic như vụ 31/10.

Các chỉ số khác trong cùng lúc không có gì bất thường:

Chỉ sốGiá trị
RAM khả dụng71%
Áp lực bộ nhớmức 1
Dung lượng bộ nén6,5 GB
IOSurface384, đứng yên

IOSurface đứng yên nghĩa là lần này không phải WebKit như vụ 31/10. Chỉ có một dòng lạ trong danh sách process chiếm nhiều bộ nhớ: bash 5,3G.

Thủ phạm: vòng lặp chờ log của một subagent

Một subagent của một phiên dự án đang chờ bộ e2e chạy xong, nên nó viết vòng lặp này:

bash
for i in $(seq 1 55); do
  grep -qE "passed" e2e-full.log && break
  read -t 9 < /dev/zero || true
done

Ý của nó là ngủ 9 giây giữa 2 lần kiểm log. Nó không dùng sleep vì harness của Claude Code chặn lệnh sleep chạy foreground, nên model tự chế cách chờ khác.

Vì sao bash phình tới 47,7 GB mà RAM vẫn báo trống

read đọc cho tới khi gặp ký tự xuống dòng. /dev/zero chỉ trả byte 0 và không bao giờ có xuống dòng, nên bash gom byte 0 vào buffer suốt 9 giây của mỗi vòng, cho tới khi -t 9 ngắt.

vmmap --summary của process bash đó cho Physical footprint là 47,7 GB. RSS chỉ 5,3 GB, đúng con số bash 5,3G ở danh sách process chiếm nhiều bộ nhớ, vì phần còn lại đã bị nén.

Trang toàn số 0 nén cực tốt. Vì thế RAM vẫn "trống", bộ nén chỉ phình tới 6,5 GB, và chỉ có số trang trong bộ nén tăng vọt.

Truy ra process bằng ps và vmmap

Khi trang nén tăng mà RAM và IOSurface đều bình thường, tìm process bash con của claude:

bash
ps -axo pid,ppid,rss,etime,command -m | head
vmmap --summary <pid> | grep "Physical footprint"

Tham số lệnh của process bash đó hiện ngay read -t 9 < /dev/zero. Cờ -m sắp xếp theo bộ nhớ, và etime cho biết nó chạy được bao lâu.

Không kill được: chờ vòng lặp tự kết thúc

Lệnh kill process bash bị auto mode của Claude Code chặn, lý do "Interfere With Workloads".

13:14 vòng lặp xong, trang nén về 7%. Từ 13:14 đến 13:19 là lượt chờ cuối của subagent: trang nén lên 15% rồi về 6%.

Cách sửa: hook chặn đọc /dev/zero, không dặn trong prompt

Dặn subagent "đừng chờ bằng /dev/zero" không đủ, vì lời dặn trong prompt không chặn được một agent đang tìm đường lách. Hook thì chặn được, kể cả subagent.

Hook PreToolUse ram-gate giờ luôn deny lệnh Bash đọc /dev/zero hoặc /dev/urandom vào read hay vào biến:

  • read … < /dev/zero
  • cat /dev/zero | read
  • $(< /dev/zero)
  • $(cat /dev/urandom)

Lệnh có giới hạn kích thước vẫn qua: head -c N /dev/urandom, dd … count=N.

Cái giá: hook khớp theo chuỗi trong lệnh, nên chuỗi đó nằm dưới dạng chữ (pattern của grep, heredoc) cũng bị chặn nhầm.

Cách chờ đúng

  • Chạy lệnh dài ở nền rồi đợi thông báo khi nó thoát.
  • Hoặc dùng Monitor với vòng until kiểm điều kiện.

Bài học: chỉ số RAM trống nói dối, luật cứng phải nằm trong hook

  • Chỉ số "RAM trống" nói dối khi dữ liệu toàn số 0. Phải theo dõi số trang nén trên giới hạn, không chỉ dung lượng bộ nén.
  • Cùng một con số trang nén tăng có thể do kernel (IOSurface ở vụ 31/10) hoặc do một process người dùng. ps cộng vmmap phân biệt được 2 trường hợp trong vài giây.
  • Agent tự chế cách lách luật: sleep bị chặn thì thành read < /dev/zero.
  • Luật cần cưỡng chế thì đặt bằng hook, vì hook chặn cả subagent còn lời dặn thì không.

Bài viết liên quan

tất cả bài viết →