应用进程、线程设置亲核性

方法一:使用taskset命令

在 Linux/Android 系统中,每个线程都有自己唯一的 ID,我们通常称之为 TID 或 LWP (Light-weight Process ID)。taskset 命令既可以对整个进程(使用 PID),也可以对进程中的特定线程(使用 TID)进行操作。

查询应用线程的 CPU 亲和性

  1. 查找进程 PID 这一步和之前一样,首先找到你的目标应用所在的进程 PID。

    adb shell ps -A | grep your.app.package.name

    假设我们找到的 PID 是 1234

  2. 查找目标线程的 TID 使用 ps -T 命令可以列出指定进程(-p <PID>)下的所有线程及其 TID。

    adb shell ps -T -p 1234

    输出结果会是类似下面这样:

    USER      PID   TID  VSIZE  RSS   WCHAN            ADDR   S NAME
    u0_a123   1234  1234 2582964 123456 SyS_epoll_wait  0      your.app.package.name  <-- 主线程,TID通常等于PID
    u0_a123   1234  1255 2582964 123456 futex_wait_queue_me 0      Jit thread pool
    u0_a123   1234  1256 2582964 123456 futex_wait_queue_me 0      HeapTaskDaemon
    u0_a123   1234  1258 2582964 123456 SyS_epoll_wait  0      RenderThread          <-- 这是渲染线程
    ...

    在这个输出中:

    • PID 列显示的是主进程 ID。
    • TID 列显示的是每个线程各自的 ID。
    • NAME 列是线程的名称,可以帮助你识别目标线程(例如 RenderThreadAudioThread 或你自己命名的线程)。

    你需要从这里找到你关心的那个线程的 TID。例如,我们想查询 RenderThread 的亲和性,它的 TID 是 1258

  3. 使用 TID 查询亲和性 taskset-p 参数既可以接受 PID 也可以接受 TID。现在我们用获取到的 TID 来查询。

    adb shell taskset -p 1258

    输出会显示该线程的 CPU 亲和性掩码,例如: pid 1258's current affinity mask: f0

    解读掩码 f0 (假设是8核CPU):

    • f0 (十六进制) = 11110000 (二进制)
    • 这意味着 TID 为 1258 的这个线程被允许在 CPU 4, CPU 5, CPU 6, 和 CPU 7 上运行。这在很多安卓设备上很常见,系统会将渲染线程这类对性能敏感的任务调度到大核(Performance Cores)上。

设置应用线程的 CPU 亲和性

设置线程亲和性的步骤和查询类似,只是命令格式略有不同。

  1. 获取 PID 和 TID 按照上面的步骤,先找到目标应用的 PID,再找到目标线程的 TID。假设 TID 仍然是 1258

  2. 使用 TID 设置亲和性 使用 taskset -p <mask> <TID> 的格式来设置。

    adb shell taskset -p <mask> <TID>

    示例: 假设我们有一个 8 核的 CPU,我们想把 TID 为 1258RenderThread 线程固定在 CPU 7 这一个性能最强的核心上运行。

    • 首先计算掩码:

      • 只在 CPU 7 上运行,二进制掩码是 10000000
      • 转换为十六进制就是 80
    • 执行命令:

      adb shell taskset -p 80 1258

    执行后,你可以立刻再次使用查询命令来验证设置是否生效:

    adb shell taskset -p 1258

    如果成功,输出应该是:pid 1258's current affinity mask: 80

总结与注意事项:

  • 核心区别: 操作进程用 PID,操作线程用 TID。
  • 如何找TID: adb shell ps -T -p <PID> 是关键命令。
  • 命令通用性: taskset -p 命令对 PID 和 TID 都有效。
  • Root 权限: 和操作进程一样,修改线程的亲和性通常也需要 root 权限 (adb root)。
  • 线程生命周期: 线程是由应用动态创建和销毁的。如果你要操作的线程退出了,它的 TID 就会失效。你需要确保在操作时,该线程是存活的。
  • 性能影响: 对线程(尤其是像 RenderThread 这样的关键系统线程)的亲和性进行不当修改,可能会对应用的流畅度和响应性产生非常直接且负面的影响。请谨慎测试和使用。

方法二:通过进程运行信息方式

  1. 查找进程 PID 这一步和之前一样,首先找到你的目标应用所在的进程 PID。

    adb shell ps -A | grep your.app.package.name

    假设我们找到的 PID 是 1234

  2. 查找目标线程的 TID 使用 ps -T 命令可以列出指定进程(-p <PID>)下的所有线程及其 TID。

    adb shell ps -T -p 1234 | grep "xxx"
cat /proc/26207/task/26289/status                                                                                                                                                                                                                                       
Name:	TaskStackChange
Umask:	0077
State:	S (sleeping)
Tgid:	26207
Ngid:	0
Pid:	26289
PPid:	1227
TracerPid:	0
Uid:	10137	10137	10137	10137
Gid:	10137	10137	10137	10137
FDSize:	512
Groups:	1077 3001 3002 3003 3007 9997 20137 50137 
VmPeak:	17452596 kB
VmSize:	17157476 kB
VmLck:	       0 kB
VmPin:	       0 kB
VmHWM:	  606676 kB
VmRSS:	  215616 kB
RssAnon:	   89884 kB
RssFile:	  111072 kB
RssShmem:	   14660 kB
VmData:	 2071176 kB
VmStk:	    8192 kB
VmExe:	       8 kB
VmLib:	  171360 kB
VmPTE:	    2568 kB
VmSwap:	   74048 kB
CoreDumping:	0
THP_enabled:	1
Threads:	84
SigQ:	0/20307
SigPnd:	0000000000000000
ShdPnd:	0000000000000000
SigBlk:	0000000080001204
SigIgn:	0000000000000001
SigCgt:	0000006f400084f8
CapInh:	0000000000000000
CapPrm:	0000000000000000
CapEff:	0000000000000000
CapBnd:	0000000000000000
CapAmb:	0000000000000000
NoNewPrivs:	0
Seccomp:	2
Seccomp_filters:	1
Speculation_Store_Bypass:	thread vulnerable
//重点在这里
Cpus_allowed:	ff
Cpus_allowed_list:	0-7
Mems_allowed:	1
Mems_allowed_list:	0
voluntary_ctxt_switches:	5589
nonvoluntary_ctxt_switches:	15019