首页
/ Deno 系统信息 API 的平台适配实现:以 deno_os 扩展为例解析 loadavg、hostname、mem_info 等跨平台系统调用

Deno 系统信息 API 的平台适配实现:以 deno_os 扩展为例解析 loadavg、hostname、mem_info 等跨平台系统调用

2026-09-04 17:10:36作者:何举烈Damon

本篇围绕 Deno 仓库中的 deno_os 扩展(crate 名为 deno_os)展开:先完整继承其 README 中"每个系统信息 API 在各平台分别调用哪个系统调用"的对照表,再结合 lib.rssys_info.rs 与 JS 侧 30_os.js 的源码,讲清这些 API 从 Deno 命名空间一路落到 libc/Win32 调用的完整链路、权限校验规则与错误处理细节,读完可以复现 Deno 跨平台系统 API 的实现方式并自行扩展类似能力。

1. deno_os 是什么:一个 Rust 扩展 + 两段懒加载 JS

deno_os crate 的职责在 Cargo.toml 中一句话概括:OS specific APIs for Deno。它通过 deno_core::extension! 宏声明为一组 op 的集合,注册入口在 lib.rs

  • op 清单op_envop_exec_pathop_exitop_delete_envop_get_envop_gidop_hostnameop_loadavgop_network_interfacesop_os_releaseop_os_uptimeop_set_envop_set_exit_codeop_system_memory_infoop_uid,以及信号三件套 op_signal_bind / op_signal_poll / op_signal_unbind(实现在 ops/signal.rs);
  • JS 侧lazy_loaded_js = ["30_os.js", "40_signals.js"],即两段脚本按需加载,分别承载 Deno 系统信息/环境/退出相关方法与信号监听;
  • 依赖:核心依赖为 deno_coredeno_permissions(权限检查)、deno_signals(信号注册)、libc(Unix 系统调用),Windows 目标额外引入 windows-sys,启用了 Wdk_System_SystemServicesRtlGetVersion)、Win32_Networking_WinSockGetHostNameW)、Win32_System_ProcessStatus(内存信息)等 feature,这些 feature 与后文各平台实现一一对应。

这种"Rust 实现 op + JS 薄封装 + 权限容器"的分层,是 Deno 所有扩展的标准形态;下文以 README 点名的五个 API 为主线逐一对比各平台的真实实现。

2. loadavg:负载平均值的三种取法

README 给出的平台对照表如下(完全继承原文档):

目标平台族 使用的系统调用 说明
Linux sysinfo -
Windows - 返回 DEFAULT_LOADAVG,Windows 上不存在 loadavg 概念
macOS、BSD getloadavg 参见 FreeBSD 的 getloadavg 手册

源码印证在 sys_info.rsloadavg() 中,三条编译分支与表格严格对应:

  • Linux/Androidlibc::sysinfo() 成功后,把 info.loads[0..3] 分别除以 1 << SI_LOAD_SHIFT 还原为浮点负载值(内核结构体里是以移位整数存储的);
  • Apple 与 FreeBSD/OpenBSDlibc::getloadavg(&mut l, 3),返回值小于 3 时回退 DEFAULT_LOADAVG
  • Windows:直接返回 DEFAULT_LOADAVG = (0.0, 0.0, 0.0),不做任何系统调用。

op 层包装见 lib.rsop_loadavg 先通过 PermissionsContainer::check_sys("loadavg", "Deno.loadavg()") 做权限检查,再调用 sys_info::loadavg(),最终由 30_os.jsloadavg() 直接透传,暴露为 Deno.loadavg(),返回长度为 3 的数组(1 分钟、5 分钟、15 分钟负载)。

值得注意的边界行为:任何系统调用失败都会静默回退为 (0,0,0) 而非抛错,这使得在受限环境(如部分容器)中 API 仍然"可用",但取到的可能是兜底值。

3. os_release:内核/系统版本的四个分支

README 对照表(继承原文档):

目标平台族 使用的系统调用 说明
Linux 读取 /proc/sys/kernel/osrelease -
Windows RtlGetVersion 返回 dwMajorVersion.dwMinorVersion.dwBuildNumber
macOS sysctl([CTL_KERN, KERN_OSRELEASE]) -

实际实现在 sys_info.rsos_release(),比表格还多一个 Android 分支:

  • Linuxstd::fs::read_to_string("/proc/sys/kernel/osrelease"),成功则 pop() 掉末尾换行,失败返回空串;
  • Android(表格未单列,源码存在该分支):libc::uname()utsname.release
  • macOS/BSDsysctl([CTL_KERN, KERN_OSRELEASE]),固定 256 字节缓冲(注释说明"256 is enough"),失败返回字符串 "Unknown"
  • Windows:调用 WDK 的 RtlGetVersion 填充 OSVERSIONINFOEXW(先初始化 dwOSVersionInfoSize 字段),失败返回空串,成功则格式化为 主版本.次版本.构建号

op 入口 op_os_releaselib.rs)对应的权限项是 osRelease。JS 侧 30_os.js 将其暴露为 Deno.osRelease()

4. hostname:缓冲区大小如何决定

README 对照表(继承原文档):

目标平台族 使用的系统调用 说明
Unix gethostname(sysconf(_SC_HOST_NAME_MAX)) -
Windows GetHostNameW -

sys_info.rshostname() 展示了两种截然不同的缓冲区策略:

  • Unix:先 libc::sysconf(libc::_SC_HOST_NAME_MAX) 向系统询问主机名的最大长度,按 buf_size + 1 分配缓冲区再调 gethostname,最后强制 NUL 终止并做 lossy 转码——即"先问系统上限,再精确分配";
  • Windows:固定 256 个宽字符(WCHAR)缓冲区调用 GetHostNameW。更隐蔽的一点是:调用前必须通过 WINSOCKET_INIT: Once 先执行一次 WSAStartup(0x0202, ...)(MAKEWORD(2,2)),否则 GetHostNameW 行为不正确;启动失败会直接 panic!

op 入口 op_hostnamelib.rs)对应 Deno.hostname(),权限项为 hostname

5. mem_info:系统内存信息的 MemInfo 结构

Deno.systemMemoryInfo() 返回的结构在 sys_info.rs 定义为 7 个 u64 字段:totalfreeavailablebufferscachedswap_totalswap_free,通过 ToV8 派生直接转为 JS 对象。

README 对照表(继承原文档):

目标平台族 使用的系统调用 说明
Linux sysinfo/proc/meminfo -
Windows sysinfoapi::GlobalMemoryStatusEx -
macOS sysctl([CTL_HW, HW_MEMSIZE])sysctl([CTL_VM, VM_SWAPUSAGE])host_statistics64(mach_host_self(), HOST_VM_INFO64) -

各分支实现(sys_info.rsmem_info())细节:

  • Linuxlibc::sysinfo() 提供 totalram/freeram/bufferram/totalswap/freeswap,并且所有值要乘以 info.mem_unit(较新内核中字段单位是 mem_unit 而非字节);available 不能从 sysinfo 得到,需要额外读取 /proc/meminfoMemAvailable: 行(单位 kB,乘 1024 转为字节)——这正是 README 里"sysinfo 和 /proc/meminfo"两个来源的原因;
  • macOS:物理内存来自 sysctl([CTL_HW, HW_MEMSIZE]);swap 总量/空闲来自 sysctl([CTL_VM, VM_SWAPUSAGE])xsw_usageavailablefree 由 Mach 的 host_statistics64(HOST_VM_INFO64) 结合页大小计算,其中 available = (free_count + inactive_count) × pagesizefree = (free_count - speculative_count) × pagesize
  • WindowsGlobalMemoryStatusEx 只给出物理内存(ullTotalPhys/ullAvailPhys);swap 相关字段因 MEMORYSTATUSEX.ullTotalPageFile 不可靠,源码转而调用 GetPerformanceInfo,用 PageSize × (CommitLimit - PhysicalTotal) 计算 swap_total、再减去 PhysicalAvailable 得到 swap_free

op 入口 op_system_memory_infolib.rs)对应权限项 systemMemoryInfo,返回 Option<MemInfo>(成功时为 Some)。

6. cpu_usage:进程 CPU 时间的两个来源

README 对照表(继承原文档):

目标平台族 使用的系统调用 说明
Linux getrusage -
Windows processthreadsapi::GetProcessTimes -
macOS getrusage -

这段描述对应的是运行时(进程级) CPU 用量,实现在 lib.rsop_runtime_cpu_usage + get_cpu_usage()

  • Unix(含 Linux/macOS)libc::getrusage(libc::RUSAGE_SELF, ...),把 ru_stimeru_utime 的秒/微秒分别合成 Duration
  • WindowsGetProcessTimes(GetCurrentProcess(), ...) 取 kernel/user FILETIME,再经 FileTimeToSystemTime 转换,且对两个转换结果做了四象限容错(任一失败则该侧回零);
  • 其他平台(源码中的兜底分支)返回默认零值。

op 以 #[buffer] out: &mut [f64] 的形式把 [sys, user] 两个微秒值写回 JS 侧缓冲区,避免了每次调用的对象分配。另有一个姊妹 op op_runtime_memory_usagelib.rs)以同样方式写回 4 个值:RSS、堆总量、已用堆、外部内存,其中 RSS 按平台分别来自 /proc/self/statm(Linux)、task_info(macOS)、KERN_PROC_PID sysctl(OpenBSD)、GetProcessMemoryInfo(Windows)。

7. 从 op 到 Deno API:权限检查与 JS 封装

所有系统信息 op 都遵循同一模式:先查 sys 权限子项,再执行系统调用。以 op_loadavg 为例(lib.rs),第二个参数是用于报错提示的 API 名 "Deno.loadavg()"。源码中可见的全部 sys 权限子项为:loadavghostnameosReleasenetworkInterfacessystemMemoryInfogiduidosUptime,它们都通过 check_sys(子项, 提示) 检查,检查逻辑位于 runtime/permissions/lib.rs。因此用户可以以 --allow-sys=hostname,osRelease 这类粒度授权,而不是整包放开系统信息。

JS 侧 30_os.js 只是对每个 op 的一层薄封装(loadavg()hostname()osRelease()osUptime()systemMemoryInfo()networkInterfaces()gid()uid() 等),并在 lib.rslazy_loaded_js 中声明为懒加载脚本——只有真正触碰相关 API 时脚本才会被求值。

8. 同 crate 的配套能力:env、exit 与信号

README 聚焦五个只读 API,但同一个 crate 还承载了进程环境与生命周期管理,简单补充:

  • Deno.envop_env/op_get_env/op_set_env/op_delete_envlib.rs)实现了 key 校验(空 key、含 = 或 NUL 的 key、含 NUL 的 value 均抛 TypeError 类错误,错误变体见 OsError)、权限检查,以及一个静态互斥锁 ProcessEnvGuard 协调运行时对进程环境的访问;修改 TZ 时会顺带调用 tzset/_tzset 并通知 V8 重新检测时区。tests/unit/os_test.ts 中的 avoidEmptyNamedEnvenvToObjectKeysAreValid 用例逐条验证了这些规则;
  • Deno.exit:JS 侧先 op_set_exit_code,再派发 unload 事件,最后 op_exit() 走 Rust 的 exit(code)lib.rs);在 --watch 模式下 op_exit 会改为终止当前 isolate 而非杀进程(WatcherExitHandle);
  • 信号监听40_signals.jsaddSignalListener/removeSignalListener 管理监听集合,首个监听者注册时才通过 op_signal_bind 创建信号资源,最后一个移除时 op_signal_unbind 解绑;Rust 侧 ops/signal.rs 用 tokio watch channel 把信号回调投递给异步的 op_signal_poll,资源关闭时调用 deno_signals::unregister

9. 验证方式

  • 行为测试集中在 tests/unit/os_test.ts,覆盖 env 的成功/缺省/删除/权限拒绝、非法 key/value 抛错、toObject()get() 的一致性(含 Windows 下 =C: 这类隐藏变量的回归测试)以及系统信息 API 的调用;
  • Rust 侧自带单元测试:lib.rs 用多线程 + mpsc 验证了 ProcessEnvGuard 在时区通知回调期间全程持有 PROCESS_ENV_LOCKlib.rs 则保证 Node 环境变量白名单数组有序(因为它依赖二分查找跳过权限检查);
  • 权限矩阵测试可参考 tests/unit/permissions_test.ts 中对 --allow-sys 相关场景的断言。

小结

deno_os 是 Deno 中"把 OS 特定行为收敛到单一 crate"的样板:README 的五张平台×系统调用对照表(loadavg / os_release / hostname / mem_info / cpu_usage)在 sys_info.rslib.rs 中都有精确到 cfg 分支的对应实现,且每个 API 都统一挂接 sys 权限子项检查与懒加载 JS 封装。理解这条"JS API → op → 平台系统调用"的链路后,扩展或排查 Deno 的系统信息类 API 时,可以直接按权限子项定位到对应的 op_* 函数与 sys_info 模块。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
527
590
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
904
1.82 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
docsdocs
暂无描述
Markdown
889
5.78 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.52 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.33 K
1.45 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
980
502
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384