2. C++ 线程创建与绑定:pthread_create、设置调度策略与优先级、CPU 亲和性、绑定到指定核心
在 AOSP 底层模块中,线程的创建与绑定是保证实时性与模块隔离的基础。裸 pthread 接口比 C++ std::thread 更接近内核调度层,允许精细控制线程的运行行为。本章深入讲解从创建线程到绑定核心的完整技术路径,包含调度策略、优先级设置以及 CPU 亲和性控制,并给出可验证的代码示例与调试方法。
2.1 pthread_create – 线程创建的底层入口
所有 POSIX 线程都以 pthread_create 为起点。函数原型:
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine)(void *), void *arg);
- thread:输出参数,保存新线程 ID。
- attr:线程属性对象,若为 NULL 则使用默认属性;绑定调度参数必须在此传递。
- start_routine:线程入口函数。
- arg:传给入口函数的参数指针。
创建时若需要非默认调度策略或亲和性,必须通过 pthread_attr_set* 提前设置。后续再动态修改是可行的,但不如在创建时指定原子性高。
性能建议:在 AOSP 系统服务中,尽量在
pthread_create 时即完成全部属性设置,避免线程启动后再调整造成调度抖动。2.2 调度策略与优先级
Linux 支持三种主要调度策略:
| 策略 | 宏定义 | 优先级范围 | 适用场景 |
|---|---|---|---|
| 普通分时 | SCHED_OTHER | 0 (静态) | 非实时背景任务 |
| 先进先出实时 | SCHED_FIFO | 1 ~ 99 | 短促高实时、不可抢占 |
| 轮转实时 | SCHED_RR | 1 ~ 99 | 同优先级轮转、长实时任务 |
设置调度策略与优先级的核心 API:
// 方式一:通过属性对象在创建时设置
pthread_attr_t attr;
struct sched_param param;
pthread_attr_init(&attr);
pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
pthread_attr_setschedpolicy(&attr, SCHED_FIFO);
param.sched_priority = 80; // 实时优先级 80
pthread_attr_setschedparam(&attr, ¶m);
// 然后创建线程
pthread_t tid;
pthread_create(&tid, &attr, thread_func, arg);
pthread_attr_destroy(&attr);
注意事项:必须显式调用 pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED),否则新建线程会继承调用方的调度策略而非使用 attr 中的设置。
权限警告:设置
SCHED_FIFO 或 SCHED_RR 且优先级大于 0 时,调用进程必须具有 CAP_SYS_NICE 能力(通常需要 root 或属于 real-time 组)。在 AOSP 中,系统服务通常已具有此能力,但 vendor 层应用需显式配置 capability。2.3 CPU 亲和性(Affinity)
CPU 亲和性决定线程可以被调度到哪些 CPU 核心上运行。使用 pthread_setaffinity_np 和 pthread_getaffinity_np 操作。
#define _GNU_SOURCE
#include <pthread.h>
#include <sched.h>
cpu_set_t cpuset;
CPU_ZERO(&cpuset);
CPU_SET(2, &cpuset); // 允许在 core 2 上运行
CPU_SET(3, &cpuset); // 也允许在 core 3 上运行
pthread_t self = pthread_self();
int ret = pthread_setaffinity_np(self, sizeof(cpu_set_t), &cpuset);
if (ret != 0) {
// 处理错误,常见 EINVAL(core 不存在)或 EPERM
}
亲和性可以在线程创建后的任何时刻设置。若要在线程创建时即绑定,建议在入口函数开头立即调用 pthread_setaffinity_np,或在创建前通过 pthread_attr_setaffinity_np 设置属性。
// 通过属性设置亲和性(创建时绑定)
pthread_attr_t attr;
cpu_set_t cpuset;
CPU_ZERO(&cpuset);
CPU_SET(0, &cpuset);
pthread_attr_init(&attr);
pthread_attr_setaffinity_np(&attr, sizeof(cpu_set_t), &cpuset);
pthread_t tid;
pthread_create(&tid, &attr, thread_func, arg);
pthread_attr_destroy(&attr);
关键点:亲和性 mask 与调度策略相互独立。SCHED_FIFO 线程绑定到一个核心时,该核心对该线程是独占的(除非同优先级 FIFO 线程也绑在同一核心上)。
2.4 绑定到指定核心 – 完整示例
以下代码创建两个线程:线程 A 绑定 core 0,使用 SCHED_FIFO 优先级 90;线程 B 绑定 core 1,使用 SCHED_RR 优先级 70。
#define _GNU_SOURCE
#include <pthread.h>
#include <sched.h>
#include <stdio.h>
#include <errno.h>
void* worker(void* arg) {
int core = *(int*)arg;
printf("Thread running on core %d\n", core);
// 此处可验证当前核心:通过 sched_getcpu()
double result = 0.0;
for (volatile long i = 0; i < 1e9; ++i) result += 0.1;
return NULL;
}
int main() {
pthread_attr_t attr;
struct sched_param param;
cpu_set_t cpuset;
pthread_t tA, tB;
int coreA = 0, coreB = 1;
// ---- 线程 A:SCHED_FIFO, prio=90, 绑定 core 0 ----
pthread_attr_init(&attr);
pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
pthread_attr_setschedpolicy(&attr, SCHED_FIFO);
param.sched_priority = 90;
pthread_attr_setschedparam(&attr, ¶m);
CPU_ZERO(&cpuset);
CPU_SET(coreA, &cpuset);
pthread_attr_setaffinity_np(&attr, sizeof(cpu_set_t), &cpuset);
pthread_create(&tA, &attr, worker, &coreA);
pthread_attr_destroy(&attr);
// ---- 线程 B:SCHED_RR, prio=70, 绑定 core 1 ----
pthread_attr_init(&attr);
pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
pthread_attr_setschedpolicy(&attr, SCHED_RR);
param.sched_priority = 70;
pthread_attr_setschedparam(&attr, ¶m);
CPU_ZERO(&cpuset);
CPU_SET(coreB, &cpuset);
pthread_attr_setaffinity_np(&attr, sizeof(cpu_set_t), &cpuset);
pthread_create(&tB, &attr, worker, &coreB);
pthread_attr_destroy(&attr);
pthread_join(tA, NULL);
pthread_join(tB, NULL);
return 0;
}
2.5 调试与验证
运行时验证线程的调度策略、优先级和亲和性:
- 查看 /proc/[pid]/task/[tid]/status:
cat /proc/$(pidof app)/task/$(tid)/status | grep -E "Cpus_allowed|Rtprio" - 查看 /proc/[pid]/task/[tid]/sched:显示当前调度策略和参数。
- 代码内验证:
int policy;
struct sched_param param;
pthread_getschedparam(pthread_self(), &policy, ¶m);
printf("Policy: %s, Priority: %d\n",
(policy == SCHED_FIFO) ? "FIFO" :
(policy == SCHED_RR) ? "RR" : "OTHER",
param.sched_priority);
cpu_set_t mask;
CPU_ZERO(&mask);
pthread_getaffinity_np(pthread_self(), sizeof(cpu_set_t), &mask);
for (int i = 0; i < CPU_SETSIZE; i++) {
if (CPU_ISSET(i, &mask)) printf("Core %d allowed\n", i);
}
printf("Current core: %d\n", sched_getcpu());
调试技巧:在 AOSP 的
adb shell 中使用 taskset -p <mask> <pid> 可以临时改变整个进程的亲和性,适合快速验证。2.6 常见错误与避坑
| 错误 | 现象 | 原因 | 解决方法 |
|---|---|---|---|
| 调度策略未生效 | 线程仍是 SCHED_OTHER | 忘记设置 PTHREAD_EXPLICIT_SCHED | 显式设置 pthread_attr_setinheritsched |
| 亲和性 mask 无效 | pthread_setaffinity_np 返回 EINVAL | 指定的 CPU 编号超出系统核心数 | 通过 sysconf(_SC_NPROCESSORS_CONF) 获取核心数 |
| 实时权限不足 | pthread_attr_setschedparam 返回 EPERM | 调用者缺少 CAP_SYS_NICE | 以 root 运行或添加到 realtime 组,或使用 setcap cap_sys_nice+ep |
| 实时线程死锁 | 线程无法让步调度,系统响应卡死 | SCHED_FIFO 线程进入死循环且未主动 sched_yield | 加入 yield 或设计确定性退出条件;使用 watchdog |
2.7 性能与稳定性优化
- 避免跨核心迁移:一旦绑定核心,保持亲和性不变;频繁修改亲和性会导致缓存失效和调度开销。
- 实时线程优先级设计:高优先级线程应保持短促执行,避免长时间抢占低优先级实时线程(优先级反转风险)。
- 核隔离:在 AOSP 的
isolcpus内核启动参数中指定专属核心,并将实时线程绑定到隔离核上,减少干扰。 - 优先级帽:使用
rlimit限制非 root 进程的最大实时优先级,防止恶意线程抢占关键服务。 - 使用局部绑定:对于大量短生命周期线程,可以复用线程池并一次性绑定整个池子到特定核心组,避免每次创建都调用系统调用。
稳定性原则:在 AOSP 系统服务中,始终将实时线程的优先级设置在 50 ~ 90 之间,留出高 10 级给内核关键任务(如中断处理)。