From b28fc572f77bae10aebf1f28238fb11883a3f529 Mon Sep 17 00:00:00 2001 From: Jz0ojiang Date: Wed, 12 Aug 2026 16:38:19 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=BB=E6=9C=BA=E5=86=85=E5=AD=98=E6=80=BB?= =?UTF-8?q?=E9=87=8F=E6=94=B9=E4=BB=8E=20/proc/meminfo=20=E8=AF=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit os.totalmem() 底层是 sysinfo() 系统调用,LXC 里 lxcfs 只虚拟化 /proc 文件、拦不住系统调用,导致容器里显示的是宿主机的物理内存总量。 从 /proc/meminfo 读 MemTotal 才是容器限额,且与 MemAvailable 同源。 --- server/host.ts | 3 ++- shared/host-info.test.ts | 12 ++++++++++++ shared/host-info.ts | 22 +++++++++++++++++++--- 3 files changed, 33 insertions(+), 4 deletions(-) diff --git a/server/host.ts b/server/host.ts index e5032e7..e3b0f85 100644 --- a/server/host.ts +++ b/server/host.ts @@ -12,6 +12,7 @@ import { cpuUsageBetween, parseGateway, parseMemAvailable, + parseMemTotal, parseNameservers, } from '../shared/host-info.ts' import type { CpuTimes, ExitProbe, HostInfo } from '../shared/host-info.ts' @@ -56,7 +57,7 @@ export async function hostInfo(): Promise { return { cpuUsage, cpuCount: os.cpus().length, - memTotal: os.totalmem(), + memTotal: parseMemTotal(meminfo) ?? os.totalmem(), memAvailable: parseMemAvailable(meminfo) ?? os.freemem(), // loadavg 在 Windows 上恒为 0,那不是「负载为零」而是「没有这个概念」 load1: process.platform === 'win32' ? null : load, diff --git a/shared/host-info.test.ts b/shared/host-info.test.ts index 5d52a28..51d8dfc 100644 --- a/shared/host-info.test.ts +++ b/shared/host-info.test.ts @@ -3,9 +3,21 @@ import { cpuUsageBetween, parseGateway, parseMemAvailable, + parseMemTotal, parseNameservers, } from './host-info.ts' +describe('parseMemTotal', () => { + test('取 MemTotal 并换算成字节', () => { + const meminfo = 'MemTotal: 2048000 kB\nMemFree: 100000 kB\nMemAvailable: 512000 kB\n' + expect(parseMemTotal(meminfo)).toBe(2048000 * 1024) + }) + + test('读不到 /proc/meminfo(非 Linux 平台)返回 null', () => { + expect(parseMemTotal('')).toBeNull() + }) +}) + describe('parseMemAvailable', () => { test('取 MemAvailable 并换算成字节', () => { const meminfo = 'MemTotal: 2048000 kB\nMemFree: 100000 kB\nMemAvailable: 512000 kB\n' diff --git a/shared/host-info.ts b/shared/host-info.ts index b10bdd5..fa4eb9c 100644 --- a/shared/host-info.ts +++ b/shared/host-info.ts @@ -11,6 +11,7 @@ export interface HostInfo { /** 0-100。第一次请求没有参照快照,是 null */ cpuUsage: number | null cpuCount: number + /** 内存总量。Linux 用 MemTotal,其余平台退回 totalmem */ memTotal: number /** 还能用的内存。Linux 用 MemAvailable,其余平台退回 freemem */ memAvailable: number @@ -37,13 +38,28 @@ export interface ExitProbe { message?: string } -/** /proc/meminfo 里的 MemAvailable(内核估算的「还能用多少」),换算成字节。 */ -export function parseMemAvailable(meminfo: string): number | null { - const match = /^MemAvailable:\s+(\d+)\s*kB/m.exec(meminfo) +function meminfoBytes(meminfo: string, field: string): number | null { + const match = new RegExp(`^${field}:\\s+(\\d+)\\s*kB`, 'm').exec(meminfo) if (!match) return null return Number(match[1]) * 1024 } +/** + * /proc/meminfo 里的 MemTotal,换算成字节。 + * + * 不能用 os.totalmem():它底层是 sysinfo() 系统调用,LXC 里的 lxcfs 只虚拟化 + * /proc 下的文件、拦不住系统调用,所以 sysinfo() 返回的是宿主机的物理内存总量。 + * 从 /proc/meminfo 读才是容器视角,且与 MemAvailable 出自同一份数据。 + */ +export function parseMemTotal(meminfo: string): number | null { + return meminfoBytes(meminfo, 'MemTotal') +} + +/** /proc/meminfo 里的 MemAvailable(内核估算的「还能用多少」),换算成字节。 */ +export function parseMemAvailable(meminfo: string): number | null { + return meminfoBytes(meminfo, 'MemAvailable') +} + /** * /proc/net/route 里的默认网关。 *