二、多核处理器基础:什么是多核处理器、对称多处理(SMP)与非对称多处理(AMP)、MTK big.LITTLE架构详解

好,咱们进入正题。这一章我打算聊聊多核处理器的基础概念。说实话,我刚入行那会儿,对多核的理解就是“一个芯片里塞了好几个CPU核心”。后来做MTK平台的项目,踩了不少坑,才真正搞明白里面的门道。

2.1 什么是多核处理器?

多核处理器,说白了就是把两个或更多的独立计算核心集成到同一个芯片上。每个核心都有自己的L1缓存,共享L2或L3缓存。它们可以同时执行不同的任务。

你可能会问:“那跟单核处理器有啥本质区别?”

嗯,区别大了。单核处理器靠提升主频来提速,但功耗和发热是硬伤。多核处理器则靠“人多力量大”——每个核心跑得不用太快,但合起来干活效率高。我在一个平板项目里就遇到过,单核跑1.5GHz烫得能煎鸡蛋,换成四核1.2GHz,温度降了20度,性能反而提升了30%。

核心要点:多核处理器不是简单地把核心堆在一起,而是通过共享资源、协同调度来实现性能与功耗的平衡。

2.2 对称多处理(SMP)与非对称多处理(AMP)

这两个概念,我当年花了整整一周才吃透。咱们一个一个来。

2.2.1 对称多处理(SMP)

SMP,全称Symmetric Multi-Processing。所有核心地位平等,共享同一块内存空间,操作系统统一调度。你写一个多线程程序,系统会自动把线程分配到不同的核心上跑。

优点很明显:

  • 编程简单,开发者不用操心核心分配
  • 负载均衡好,系统自动调度
  • 兼容性好,现有软件基本都能跑

缺点呢?

  • 所有核心共享内存总线,高并发时容易成为瓶颈
  • 功耗控制不够精细,不适合对功耗敏感的场景

我在MT6739平台上做过一个SMP调优项目。当时发现四个核心全开时,内存带宽被占满,导致GPU帧率掉得厉害。后来通过限制核心频率和绑定中断亲和性,才把问题解决。

2.2.2 非对称多处理(AMP)

AMP,全称Asymmetric Multi-Processing。核心之间地位不同,有的负责高性能计算,有的负责低功耗后台任务。每个核心可以跑不同的操作系统,或者同一个操作系统的不同实例。

举个例子:

  • 大核跑Linux,处理复杂的UI渲染
  • 小核跑RTOS,处理传感器数据采集

AMP的优势:

  • 功耗控制极好,小核干小活,大核干大活
  • 实时性强,关键任务不会被其他任务干扰

但坑也不少:

  • 编程复杂,你得手动管理核心间的通信
  • 调试困难,两个系统之间的同步问题让人头大

避坑指南:我曾经在一个AMP项目中,大核和小核共享一个UART外设。结果两边同时写数据,导致数据错乱。后来加了信号量才搞定。记住:共享资源一定要做好互斥保护。

2.3 MTK big.LITTLE架构详解

big.LITTLE,这是ARM公司提出来的混合架构。MTK把它玩出了花。说白了,就是把高性能的“大核”和低功耗的“小核”组合在一起。

以MT6765为例:

  • 4个Cortex-A53小核(最高1.5GHz)
  • 4个Cortex-A53大核(最高2.3GHz)

等等,你可能注意到了——都是A53?没错,MTK早期方案里大小核用的是同一种架构,只是频率和电压不同。后来才引入Cortex-A75/A55这样的异构组合。

2.3.1 三种工作模式

模式 描述 适用场景
Cluster Switching 同一时间只能使用大核簇或小核簇,不能混用 简单任务切换,功耗敏感场景
In-Order Scheduling 系统根据负载动态调度任务到大核或小核 日常使用,兼顾性能与功耗
Global Task Scheduling 所有核心同时可用,系统智能分配任务 高性能场景,如游戏、视频编辑

我个人习惯用Global Task Scheduling模式。虽然功耗高一点,但用户体验好。记得有一次做视频播放优化,用Cluster Switching模式,切换核心时画面会卡顿半秒。换成Global Task Scheduling后,流畅度明显提升。

2.3.2 MTK的调度策略

MTK在Linux内核里加了自己的调度器——HMP(Heterogeneous Multi-Processing)。它比标准的CFS调度器更智能,能识别任务的特性:

  • 前台交互任务 → 优先分配到大核
  • 后台服务任务 → 分配到小核
  • 突发高负载 → 临时唤醒大核

这里有个小技巧:你可以通过/sys/devices/system/cpu/下的节点手动控制核心状态。比如:

# 查看当前在线核心
cat /sys/devices/system/cpu/online

# 手动关闭某个核心
echo 0 > /sys/devices/system/cpu/cpu3/online

# 设置大核最小频率
echo 1000000 > /sys/devices/system/cpu/cpu4/cpufreq/scaling_min_freq

经验之谈:调试阶段别直接改这些节点,容易把系统搞崩。我建议先在adb shell里测试,确认没问题再写到init脚本里。

2.3.3 实际项目中的坑

嗯,这里得说说我踩过的坑。

第一个坑:大小核切换延迟。在MT6771平台上,从大核切到小核大约需要2-3ms。如果任务对延迟敏感,比如音频处理,这2ms就可能导致爆音。解决方案是把音频线程绑定到大核上:

# 绑定进程到CPU4-7(大核)
taskset -p 0xf0 <PID>

第二个坑:功耗估算不准。MTK的功耗模型默认假设小核功耗是大核的1/3。但实际测试发现,小核跑满负载时功耗接近大核的60%。所以别盲目相信默认参数,一定要实测。

第三个坑:缓存一致性。big.LITTLE架构下,大核和小核的L2缓存是独立的。如果一个任务在大核上修改了数据,然后切到小核上继续跑,小核可能读到旧数据。MTK通过硬件一致性总线解决了这个问题,但如果你用DMA直接访问内存,还是得小心。

2.4 小结

这一章咱们聊了多核处理器的基本概念,SMP和AMP的区别,以及MTK big.LITTLE架构的细节。说白了,多核开发的核心就是“把合适的任务放到合适的核心上”。

下一章我会讲MTK平台的任务调度机制,包括CFS调度器、HMP调度器,以及如何通过cgroup做资源隔离。到时候咱们再细聊。

对了,如果你在项目中遇到大小核调度的问题,欢迎来公众号找我聊聊。我平时也会分享一些MTK开发的实战经验。

课后思考:假设你有一个实时音频处理任务和一个后台下载任务,你会怎么分配核心?为什么?