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_OTHER0 (静态)非实时背景任务
先进先出实时SCHED_FIFO1 ~ 99短促高实时、不可抢占
轮转实时SCHED_RR1 ~ 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;
}
线程创建与绑定完整流程 应用程序 pthread_attr_init 设置调度策略 SCHED_FIFO / SCHED_RR 设置 CPU 亲和性 CPU_SET / setaffinity_np pthread_create 线程就绪 · 绑定生效 提示 必须设置 PTHREAD_EXPLICIT_SCHED 否则继承调用方调度策略

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 级给内核关键任务(如中断处理)。