引言

每个移动应用都运行在操作系统分配的进程沙箱中。进程 ID(PID)、用户 ID(UID)、线程 ID(TID)、CPU 时间、环境变量——这些底层信息通常隐藏在应用框架的抽象层之下。但在性能诊断、安全审计和调试场景中,直接获取这些进程级数据是开发者必须掌握的能力。

HarmonyOS NEXT 通过 @ohos.process 模块将进程信息查询能力暴露给应用开发者。无需任何权限,即可同步读取当前进程的 PID/UID/TID、判断运行环境(64 位/隔离进程)、获取运行时间统计(进程运行时间、CPU 时间、启动耗时),以及通过 ProcessManager 实例查询环境变量、线程优先级和 UID 归属。

@ohos.process 属于 @kit.ArkTS,是语言运行时基础设施的一部分。与 Android 的 android.os.Process(静态方法为主 + 部分需要权限)和 iOS 的 NSProcessInfo(属性式读取)不同,鸿蒙采用"命名空间常量 + ProcessManager 实例方法"的双层架构——基础信息(pid/uid/tid/is64Bit/uptime)通过顶层函数和常量直接暴露,高级操作(环境变量查询、线程优先级、UID 归属判断)通过 ProcessManager 实例完成。

本文将深入讲解 @ohos.process 的进程身份识别、运行时间统计、环境变量查询和线程优先级四大核心能力,并构建一个"进程信息实验室"Demo,在一个页面中展示全部可查询的进程数据。

一、API 架构:命名空间 + ProcessManager 双层设计

1.1 核心设计理念

@ohos.process 的设计可以类比操作系统中的 /proc 文件系统——它提供只读的进程元数据查询能力(以及少数危险操作如 abort()kill())。API 分为两层:

顶层常量和函数(直接通过 process.xxx 调用):

  • process.pid / process.uid / process.tid — 进程/用户/线程标识
  • process.is64Bit() — 是否 64 位环境
  • process.isIsolatedProcess() — 是否隔离进程
  • process.uptime() — 进程运行时间(毫秒)
  • process.getPastCpuTime() — CPU 累计时间(毫秒)
  • process.getStartRealtime() — 系统启动到进程启动的耗时(毫秒)

ProcessManager 实例方法(通过 new process.ProcessManager() 调用):

  • isAppUid(v: number): boolean — 检查 UID 是否属于当前应用
  • getThreadPriority(v: number): number — 根据 TID 查询线程优先级
  • getEnvironmentVar(name: string): string — 读取环境变量值
  • getUidForName(v: string): number — 根据用户名反查 UID
  • getSystemConfig(name: number): number — 读取系统配置参数

这种双层设计有三个优势:

  • 高频操作零开销piduidis64Bit() 等高频查询不走实例化路径,直接从命名空间访问
  • 关注点分离:只读的基础信息与需要参数的高级查询分开,降低误用风险
  • API 演进兼容:API 8 及以下使用顶层函数(如 process.getUidForName(v)),API 9+ 统一迁移到 ProcessManager 实例方法,旧版标识 @deprecated 但保留兼容
import process from '@ohos.process';

// 顶层直接调用 — 无需参数
const myPid = process.pid;           // 当前进程 ID
const is64 = process.is64Bit();      // 是否 64 位
const up = process.uptime();         // 运行时长 (ms)

// ProcessManager 实例 — 带有参数查询
const mgr = new process.ProcessManager();
const priority = mgr.getThreadPriority(process.tid);  // 当前线程优先级
const homePath = mgr.getEnvironmentVar('HOME');       // 环境变量 HOME

1.2 进程标识三件套:pid / uid / tid

process.pidprocess.uidprocess.tid 是三个只读数字常量,在进程启动时由系统分配,进程生命周期内不变。

pid(Process ID):操作系统为当前应用进程分配的唯一标识。在鸿蒙中,每个 ArkUI 应用运行在独立的进程中,pid 可用于:

  • @ohos.hilog 配合,在日志中标记进程来源
  • 在调试工具中定位进程(如 DevEco Studio 的 Profiler)
  • 调用需要 pid 参数的系统 API

uid(User ID):Linux 用户标识,鸿蒙中每个应用签名的 HAP 包拥有唯一的 uid。uid 值通常 > 10000(应用沙箱范围),系统服务 uid 在 0-9999 之间。通过 uid 可以判断一段代码是否运行在特权上下文中。

tid(Thread ID):当前执行线程的标识。ArkUI 的主线程(UI 线程)有固定的 tid,Worker 线程有不同的 tid。在 taskpoolworker 场景中,可以通过对比 tid 确认代码正在哪个线程上执行。

@State pid: number = 0;
@State uid: number = 0;
@State tid: number = 0;

aboutToAppear(): void {
  this.pid = process.pid;
  this.uid = process.uid;
  this.tid = process.tid;
}

这三个值是同步读取的——不需要 await,不需要回调,直接读取即可。这使它们非常适合在 UI 渲染时直接绑定显示。

1.3 运行环境检测:is64Bit / isIsolatedProcess

is64Bit() 返回当前进程是否运行在 64 位环境中。HarmonyOS NEXT 是纯 64 位操作系统,所有官方设备均返回 true。但在某些模拟器或兼容模式下可能返回 false。开发者可以利用这个 API 在运行时判断是否加载 64 位专用的 native 库。

isIsolatedProcess() 检查当前进程是否为隔离进程。隔离进程是鸿蒙的安全机制——某些高风险操作(如 WebView 渲染、第三方插件执行)会在隔离进程中运行,隔离进程的文件系统和网络访问都受到严格限制。如果你的代码在隔离进程中执行,需要避免调用需要沙箱路径的 API。

if (process.isIsolatedProcess()) {
  // 隔离进程 — 文件访问受限,避免使用 filesDir
  console.warn('当前运行在隔离进程中');
} else {
  // 正常进程 — 沙箱路径可用
  const cacheDir = getContext().cacheDir;
}

二、运行时间统计:uptime / getPastCpuTime / getStartRealtime

2.1 三个时间维度

@ohos.process 提供了三个时间测量函数,从不同维度描述进程的运行状态:

函数 含义 单位 典型用途
uptime() 进程从启动到现在的总运行时间 毫秒 页面底部显示"已运行 X 小时 X 分钟"
getPastCpuTime() 进程累计消耗的 CPU 时间 毫秒 性能诊断:CPU 时间 vs 墙上时间
getStartRealtime() 从系统开机到进程启动的耗时 毫秒 冷启动性能分析:与系统启动时间对比

uptime() 测量的是墙上时钟时间(wall-clock time)——无论进程在计算还是在 I/O 等待,只要进程存在,计时就继续。getPastCpuTime() 测量的是 CPU 实际执行时间——只有当进程的线程被 CPU 调度执行时才计时。

通过对比这两个值,可以快速判断应用的 CPU 密集程度:

const wallTime = process.uptime();
const cpuTime = process.getPastCpuTime();
const cpuRatio = (cpuTime / wallTime * 100).toFixed(1);
// 如果 cpuRatio < 10%,说明应用大部分时间在等待(I/O 密集)
// 如果 cpuRatio > 50%,说明应用是计算密集型

getStartRealtime() 返回的是"从系统开机到进程启动"之间的毫秒数。它与 uptime() 相加等于系统总运行时间。这个值在冷启动分析中很有用——如果 getStartRealtime() 很大但 uptime() 很小,说明进程是最近才启动的(被系统杀死后重新创建)。

2.2 格式化时间显示

三个函数返回的都是原始毫秒数——直接显示给用户不友好。Demo 中实现了智能格式化函数:

private formatDuration(ms: number): string {
  if (ms < 1000) {
    return ms.toString() + ' ms';
  }
  if (ms < 60000) {
    return (ms / 1000).toFixed(1) + ' 秒';
  }
  if (ms < 3600000) {
    return (ms / 60000).toFixed(1) + ' 分钟';
  }
  return (ms / 3600000).toFixed(2) + ' 小时';
}

在 Demo 页面中,用户点击"刷新运行时间"按钮后,三个时间值会同步更新:

private refreshUptime(): void {
  this.uptime = this.formatDuration(process.uptime());
  this.cpuTime = this.formatDuration(process.getPastCpuTime());
  this.startRealtime = this.formatDuration(process.getStartRealtime());
}

多次点击刷新按钮观察 uptime() 的增长,可以直观感受进程运行时间的累积过程——每次刷新通常增加数千毫秒(取决于刷新间隔)。
在这里插入图片描述
在这里插入图片描述

三、ProcessManager 实例方法:环境变量、线程优先级与 UID 归属

3.1 环境变量查询

ProcessManager.getEnvironmentVar(name: string): string 读取系统环境变量的值。传入环境变量名返回字符串值;如果变量不存在则抛出异常。

const pm = new process.ProcessManager();
try {
  const path = pm.getEnvironmentVar('PATH');
  console.log('PATH: ' + path);
} catch (e) {
  console.error('环境变量 PATH 不存在');
}

常见的环境变量包括:

  • PATH — 可执行文件搜索路径
  • HOME — 用户主目录
  • PWD — 当前工作目录
  • LANG — 语言和区域设置

环境变量查询是同步的——不需要异步回调。Demo 中设计了四个预设快捷按钮(PATH、HOME、PWD、LANG),用户点击后自动填入查询并显示结果:

Button('PATH')
  .onClick(() => { this.envKey = 'PATH'; this.queryEnv(); })
Button('HOME')
  .onClick(() => { this.envKey = 'HOME'; this.queryEnv(); })

查询结果以等宽字体显示在灰色背景的文本框中,模拟终端效果。

3.2 线程优先级查询

ProcessManager.getThreadPriority(v: number): number 根据线程 ID(TID)查询线程的调度优先级。在 Linux 内核中,线程优先级的范围通常是 -20(最高优先级)到 19(最低优先级),默认值为 0。

const pm = new process.ProcessManager();
const priority = pm.getThreadPriority(process.tid);
console.log('当前线程优先级: ' + priority.toString());

Demo 默认填入当前线程的 TID,用户也可以手动输入其他 TID 进行查询。优先级数值的含义:

  • 负数(-20 ~ -1):高优先级,获得更多 CPU 调度时间
  • 0:默认优先级
  • 正数(1 ~ 19):低优先级,nice 值越高越"友好"(让出 CPU)

3.3 UID 归属检查

ProcessManager.isAppUid(v: number): boolean 检查给定的 UID 是否属于当前应用。这在安全审计场景中很有用——例如验证某个 IPC 调用者的 UID 是否与本应用一致。

const pm = new process.ProcessManager();
if (pm.isAppUid(20000)) {
  console.log('UID 20000 属于当前应用');
}

Demo 默认填入当前进程的 UID(process.uid),用户点击"检查"按钮后显示"属于应用 UID"或"不属于应用 UID"。输入 0(root UID)进行测试会返回 false,因为应用进程不可能以 root 身份运行。

四、实战 Demo:进程信息实验室

4.1 页面设计

"进程信息实验室"页面分为五个功能区域:

  1. 进程身份卡片:三列布局显示 PID/UID/TID,下方双列显示 64 位环境和隔离进程状态(布尔值以绿色/红色区分)

  2. 运行时间面板:三列显示进程运行时间、CPU 时间和启动耗时,下方"刷新运行时间"按钮可重新读取最新值

  3. 环境变量查询:TextInput 输入环境变量名 + "查询"按钮,下方四个预设快捷按钮(PATH/HOME/PWD/LANG),查询结果显示在等宽字体文本框中

  4. 线程优先级:带数字键盘的 TID 输入框(默认填入当前 TID)+ "查询优先级"按钮,结果显示优先级数值

  5. UID 检查:UID 输入框(默认填入当前 UID)+ "检查"按钮,结果显示归属判断

  6. 操作日志:按时间倒序记录所有查询操作和结果

4.2 核心实现

状态模型设计

@State pid: number = 0;
@State uid: number = 0;
@State tid: number = 0;
@State is64: boolean = false;
@State isIsolated: boolean = false;
@State uptime: string = '--';
@State cpuTime: string = '--';
@State startRealtime: string = '--';
@State envKey: string = 'PATH';
@State envValue: string = '--';
@State threadTid: string = '';
@State threadPriority: string = '--';
@State checkUid: string = '';
@State uidCheckResult: string = '--';
@State logs: LogEntry[] = [];
private procMgr: process.ProcessManager = new process.ProcessManager();

设计要点:

  • 进程标识(pid/uid/tid)使用 @State number 直接绑定显示
  • 布尔值(is64/isIsolated)用 @State boolean + 条件着色—— 显示绿色、 显示红色,隔离进程"是"为黄色(警告含义)
  • 运行时间用字符串存储格式化后的结果,原始毫秒数只在 formatDuration() 内部处理
  • procMgrprivate 而非 @State——它不是 UI 状态,只是一个无状态的查询器

4.3 交互方式

Demo 提供四个交互点:

  1. 刷新运行时间:点击按钮 → process.uptime() / getPastCpuTime() / getStartRealtime() 三个值同时更新。用户多次点击可观察运行时间的累积增长

  2. 环境变量查询:输入变量名或点击预设快捷按钮 → pm.getEnvironmentVar(key) 读取值 → 结果显示在文本框中。输入不存在的变量名 → 异常捕获 → 显示"(不存在)"

  3. 线程优先级查询:输入 TID(或使用默认值)→ pm.getThreadPriority(tid) 获取优先级数值 → 显示结果

  4. UID 检查:输入 UID → pm.isAppUid(uid) 判断归属 → 显示"属于应用 UID"或"不属于应用 UID"

每个交互操作都通过 addLog() 记录到操作日志中,日志按类别着色(success 绿色、error 红色、system 灰色)。

五、实际应用场景

5.1 性能诊断仪表盘

在应用的开发者模式或"关于"页面中嵌入进程运行时数据,帮助开发者快速了解应用状态:

function getProcessReport(): string {
  const wallTime = process.uptime();
  const cpuTime = process.getPastCpuTime();
  const cpuPct = (cpuTime / wallTime * 100).toFixed(1);

  return [
    'PID: ' + process.pid,
    'UID: ' + process.uid,
    '64位: ' + (process.is64Bit() ? '是' : '否'),
    '运行时间: ' + (wallTime / 1000).toFixed(0) + 's',
    'CPU占用: ' + cpuPct + '%',
    '启动耗时: ' + process.getStartRealtime() + 'ms'
  ].join('\n');
}

5.2 冷启动性能检测

EntryAbility.onCreate() 中记录 getStartRealtime() 值,然后在第一个页面的 onPageShow() 中对比,可以获取从系统启动到用户看到首页的端到端延迟:

// EntryAbility.onCreate
const startRealtime = process.getStartRealtime();

// Index.onPageShow
const currentUptime = process.uptime();
const coldStartTime = currentUptime + process.getStartRealtime();

5.3 线程调试辅助

在 Worker 线程中检查 tid,确认异步任务确实在非 UI 线程上执行:

// Worker 中
const worker = new worker.ThreadWorker('...');
const tid = process.tid;
console.log('Worker 线程 TID: ' + tid.toString());
// 该 tid 应不同于 UI 线程的 tid

六、总结

@ohos.process 是 HarmonyOS NEXT 中最轻量但最底层的 API 模块之一。通过本文的学习,你应该已经掌握:

  1. 进程标识pid/uid/tid 三个同步常量,无需权限,直接读取,用于进程/用户/线程的身份识别
  2. 运行环境检测is64Bit() 判断 64 位环境,isIsolatedProcess() 判断隔离进程——前者用于 native 库兼容,后者用于沙箱限制感知
  3. 运行时间统计uptime()(墙上时间)、getPastCpuTime()(CPU 时间)、getStartRealtime()(启动耗时)——三者结合可用于 CPU 占用率诊断和冷启动分析
  4. ProcessManager 实例getEnvironmentVar()查询环境变量、getThreadPriority()查询线程优先级、isAppUid()判断 UID 归属——均为同步方法但可能抛异常,需要 try/catch 防护
  5. API 演进:API 8 及以下使用顶层函数(部分已废弃),API 9+ 统一使用 ProcessManager 实例方法。新项目应直接使用 new process.ProcessManager() 模式

@ohos.process 的最佳使用模式可以总结为:

常量直读 pid/uid/tid → is64Bit/isIsolatedProcess 判断环境 → uptime/getPastCpuTime/getStartRealtime 统计时间 → ProcessManager 实例查询环境变量/线程优先级/UID 归属。所有 API 同步调用,无需权限,无需 context。异常场景用 try/catch 捕获。

进程信息虽然"底层",但在性能诊断、安全审计和调试场景中是不可或缺的工具。@ohos.process 以零权限、零配置、同步调用的方式提供这些能力,体现了 HarmonyOS 操作系统级 API 的设计哲学——把系统能力以最简单的方式暴露给开发者。

@ohos.process 属于 @kit.ArkTS,是语言运行时基础设施。它没有复杂的状态机,没有异步回调,没有生命周期管理——只有十几个同步的常量和函数。这种极简设计让它在 HarmonyOS 的 API 体系中独树一帜,也让它成为每个开发者都应该了解的基础模块。

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐