MTK/Unisoc 平台 ARM64 / Android 内核与 BSP 开发 二阶段
第一部分 电源管理全景:Suspend/Resume 流程
1.1 Suspend/Resume 核心概念
Suspend (系统挂起) 和 Resume (系统唤醒) 是电源管理中最重要的两个操作。Suspend 将系统进入低功耗状态,保留内存内容但暂停 CPU 和外设。Resume 将系统恢复到正常工作状态。Android 系统通过 Linux 内核的 PM (Power Management) 框架实现 Suspend/Resume,并支持多种唤醒源(如电源键、RTC、USB 等)。
1.1.1 Suspend/Resume 状态迁移图
[正常工作状态 (Working State)] ↓ [用户触发 Suspend] ↓ [PM 核心层 (kernel/power/main.c)] ├── 通知设备准备挂起 (suspend_prepare) ├── 冻结进程 (freeze_processes) ├── 同步文件系统 (sync_filesystems) ├── 设备电源管理 (device_power_suspend) ├── 进入低功耗状态 (suspend_enter) └── 等待唤醒事件 ↓ [唤醒事件触发] ↓ [PM 核心层 (kernel/power/main.c)] ├── 设备电源恢复 (device_power_resume) ├── 解冻进程 (thaw_processes) ├── 通知设备恢复完成 (suspend_complete) └── 恢复到正常工作状态
1.1.2 Suspend/Resume 架构
[PM 核心层 (kernel/power/main.c)] ├── suspend_prepare() → 准备挂起 ├── suspend_enter() → 进入低功耗状态 ├── suspend_finish() → 完成挂起 └── suspend_complete() → 完成恢复 ↓ [设备 PM 层 (drivers/base/power/main.c)] ├── dpm_prepare() → 设备准备 ├── dpm_suspend() → 设备挂起 ├── dpm_resume() → 设备恢复 └── dpm_complete() → 设备完成 ↓ [平台 PM 层 (arch/arm64/kernel/suspend.c)] ├── platform_suspend_prepare() → 平台准备 ├── platform_suspend_enter() → 平台进入 ├── platform_suspend_finish() → 平台完成 └── platform_wake_up() → 平台唤醒 ↓ [硬件 PM 层] ├── CPU 空闲状态 (cpuidle) ├── GIC 唤醒中断 └── 电源域 (PM Domain)
1.2 核心数据结构
1.2.1 PM 核心结构 (代码出处: kernel/power/main.c)
// 代码出处: kernel/power/main.c
/**
* @struct pm_state
* @brief PM 状态结构,表示系统电源状态。
*/
struct pm_state {
const char *label; /**< 状态标签 */
const char *name; /**< 状态名称 */
suspend_state_t state; /**< 挂起状态值 */
int (*enter)(suspend_state_t state); /**< 进入状态函数 */
int (*valid)(suspend_state_t state); /**< 检查状态是否有效 */
int (*prepare)(suspend_state_t state); /**< 准备函数 */
int (*finish)(suspend_state_t state); /**< 完成函数 */
int (*wake)(suspend_state_t state); /**< 唤醒函数 */
};
/**
* @struct suspend_data
* @brief 挂起数据结构,存储挂起过程中的状态。
*/
struct suspend_data {
suspend_state_t state; /**< 挂起状态 */
struct pm_message msg; /**< PM 消息 */
int error; /**< 错误码 */
unsigned long start_time; /**< 开始时间 */
unsigned long end_time; /**< 结束时间 */
struct list_head device_list; /**< 设备列表 */
struct list_head platform_list; /**< 平台列表 */
spinlock_t lock; /**< 自旋锁 */
};
/**
* @struct pm_message
* @brief PM 消息结构,用于传递电源管理事件。
*/
struct pm_message {
int event; /**< 事件类型 */
int data; /**< 事件数据 */
union {
struct {
int flag; /**< 标志位 */
} flags;
struct {
int suspend_id; /**< 挂起 ID */
} suspend;
} u;
};
/**
* @struct device_pm_state
* @brief 设备 PM 状态结构。
*/
struct device_pm_state {
struct device *dev; /**< 设备指针 */
int state; /**< 设备状态 (D0/D1/D2/D3) */
unsigned long flags; /**< 标志位 */
struct list_head list; /**< 链表节点 */
struct device_pm_ops *ops; /**< 设备 PM 操作 */
spinlock_t lock; /**< 自旋锁 */
struct completion completion; /**< 完成信号 */
bool is_suspended; /**< 是否已挂起 */
bool can_wakeup; /**< 是否支持唤醒 */
int wakeup_irq; /**< 唤醒中断号 */
void *private_data; /**< 私有数据 */
};
1.3 核心代码实现
1.3.1 核心 Suspend 流程 (代码出处: kernel/power/main.c)
// 代码出处: kernel/power/main.c
/**
* @brief 核心挂起函数。
* @param state 挂起状态
* @return 0 成功,负数错误
*/
int suspend_enter(suspend_state_t state)
{
struct suspend_data data;
int ret;
// 1. 初始化挂起数据
data.state = state;
data.msg.event = PM_EVENT_SUSPEND;
data.error = 0;
data.start_time = jiffies;
INIT_LIST_HEAD(&data.device_list);
INIT_LIST_HEAD(&data.platform_list);
spin_lock_init(&data.lock);
// 2. 检查是否支持该状态
if (!pm_suspend_valid(state)) {
return -EINVAL;
}
// 3. 准备挂起
ret = suspend_prepare(state);
if (ret < 0) {
goto error;
}
// 4. 冻结进程
ret = freeze_processes();
if (ret < 0) {
goto error_prepare;
}
// 5. 执行设备挂起
ret = dpm_suspend(PMSG_SUSPEND);
if (ret < 0) {
goto error_freeze;
}
// 6. 同步文件系统
sys_sync();
// 7. 进入低功耗状态
ret = suspend_enter_low_power(state);
if (ret < 0) {
goto error_dpm;
}
// 8. 执行设备恢复
ret = dpm_resume(PMSG_RESUME);
if (ret < 0) {
goto error_dpm;
}
// 9. 解冻进程
thaw_processes();
// 10. 完成挂起
suspend_finish(state);
data.end_time = jiffies;
pm_print_times(&data);
return 0;
error_dpm:
dpm_resume(PMSG_RESUME);
error_freeze:
thaw_processes();
error_prepare:
suspend_finish(state);
error:
return ret;
}
/**
* @brief 挂起准备函数。
* @param state 挂起状态
* @return 0 成功,负数错误
*/
int suspend_prepare(suspend_state_t state)
{
int ret;
// 1. 冻结内核线程
ret = freeze_kernel_threads();
if (ret < 0) {
return ret;
}
// 2. 检查唤醒源
ret = pm_wakeup_prepare(state);
if (ret < 0) {
goto error;
}
// 3. 准备平台
ret = platform_suspend_prepare(state);
if (ret < 0) {
goto error;
}
return 0;
error:
thaw_kernel_threads();
return ret;
}
/**
* @brief 进入低功耗状态。
* @param state 挂起状态
* @return 0 成功,负数错误
*/
int suspend_enter_low_power(suspend_state_t state)
{
struct cpuidle_state *idle_state;
int ret;
// 1. 选择空闲状态
idle_state = cpuidle_select();
if (!idle_state) {
return -EINVAL;
}
// 2. 进入低功耗状态
ret = cpuidle_enter(idle_state);
if (ret < 0) {
return ret;
}
// 3. 等待唤醒事件
ret = wait_for_wakeup_event();
if (ret < 0) {
return ret;
}
return 0;
}
1.3.2 设备 PM 管理 (代码出处: drivers/base/power/main.c)
// 代码出处: drivers/base/power/main.c
/**
* @brief 设备挂起。
* @param msg PM 消息
* @return 0 成功,负数错误
*/
int dpm_suspend(pm_message_t msg)
{
struct device *dev;
int ret = 0;
// 1. 遍历所有设备
list_for_each_entry(dev, &device_list, power.list) {
if (dev->power.no_suspend) {
continue;
}
// 2. 检查设备是否支持唤醒
if (dev->power.can_wakeup) {
dev->power.wakeup = true;
}
// 3. 执行设备挂起
ret = device_suspend(dev, msg);
if (ret < 0) {
dev_err(dev, "Device suspend failed: %d\n", ret);
break;
}
}
return ret;
}
/**
* @brief 设备恢复。
* @param msg PM 消息
* @return 0 成功,负数错误
*/
int dpm_resume(pm_message_t msg)
{
struct device *dev;
int ret = 0;
// 1. 遍历所有设备 (反向顺序)
list_for_each_entry_reverse(dev, &device_list, power.list) {
if (dev->power.no_suspend) {
continue;
}
// 2. 执行设备恢复
ret = device_resume(dev, msg);
if (ret < 0) {
dev_err(dev, "Device resume failed: %d\n", ret);
break;
}
}
return ret;
}
/**
* @brief 设备 PM 操作注册。
* @param dev 设备指针
* @param ops 设备 PM 操作
* @return 0 成功,负数错误
*/
int device_pm_ops_register(struct device *dev, struct device_pm_ops *ops)
{
if (!dev || !ops) {
return -EINVAL;
}
dev->power.ops = ops;
dev->power.can_wakeup = ops->wakeup ? true : false;
dev->power.no_suspend = ops->no_suspend ? true : false;
return 0;
}
1.3.3 平台 PM 层 (代码出处: arch/arm64/kernel/suspend.c)
// 代码出处: arch/arm64/kernel/suspend.c
/**
* @brief 平台挂起准备。
* @param state 挂起状态
* @return 0 成功,负数错误
*/
int platform_suspend_prepare(suspend_state_t state)
{
int ret;
// 1. 保存 CPU 状态
ret = cpu_suspend_prepare();
if (ret < 0) {
return ret;
}
// 2. 保存 GIC 状态
ret = gic_suspend_prepare();
if (ret < 0) {
goto error_gic;
}
// 3. 保存电源域状态
ret = pm_domain_suspend_prepare();
if (ret < 0) {
goto error_pd;
}
return 0;
error_pd:
gic_suspend_finish();
error_gic:
cpu_suspend_finish();
return ret;
}
/**
* @brief 平台挂起进入。
* @param state 挂起状态
* @return 0 成功,负数错误
*/
int platform_suspend_enter(suspend_state_t state)
{
// 1. 执行 ARM64 特定挂起
cpu_suspend_enter(state);
// 2. 进入 WFI (Wait For Interrupt)
wfi();
return 0;
}
/**
* @brief 平台唤醒。
* @param state 挂起状态
* @return 0 成功,负数错误
*/
int platform_wake_up(suspend_state_t state)
{
int ret;
// 1. 唤醒 CPU
ret = cpu_wake_up();
if (ret < 0) {
return ret;
}
// 2. 唤醒 GIC
ret = gic_wake_up();
if (ret < 0) {
return ret;
}
// 3. 唤醒电源域
ret = pm_domain_wake_up();
if (ret < 0) {
return ret;
}
return 0;
}
1.3.4 唤醒源管理 (代码出处: drivers/base/power/wakeup.c)
// 代码出处: drivers/base/power/wakeup.c
/**
* @struct wakeup_source
* @brief 唤醒源结构。
*/
struct wakeup_source {
const char *name; /**< 唤醒源名称 */
struct list_head list; /**< 链表节点 */
struct device *dev; /**< 关联的设备 */
int irq; /**< 中断号 */
bool active; /**< 是否活跃 */
unsigned long wakeup_count; /**< 唤醒次数 */
unsigned long last_wakeup_time; /**< 上次唤醒时间 */
unsigned long total_wakeup_time; /**< 总唤醒时间 */
spinlock_t lock; /**< 自旋锁 */
};
/**
* @brief 注册唤醒源。
* @param name 唤醒源名称
* @param irq 中断号
* @return 0 成功,负数错误
*/
int wakeup_source_register(const char *name, int irq)
{
struct wakeup_source *ws;
int ret;
// 1. 分配唤醒源结构
ws = kzalloc(sizeof(*ws), GFP_KERNEL);
if (!ws) {
return -ENOMEM;
}
ws->name = name;
ws->irq = irq;
ws->active = false;
ws->wakeup_count = 0;
ws->last_wakeup_time = 0;
ws->total_wakeup_time = 0;
spin_lock_init(&ws->lock);
// 2. 添加到全局列表
list_add_tail(&ws->list, &wakeup_source_list);
// 3. 申请中断
ret = request_irq(irq, wakeup_irq_handler, IRQF_TRIGGER_RISING,
name, ws);
if (ret < 0) {
list_del(&ws->list);
kfree(ws);
return ret;
}
return 0;
}
/**
* @brief 唤醒中断处理函数。
* @param irq 中断号
* @param dev_id 设备私有数据
* @return IRQ_HANDLED
*/
static irqreturn_t wakeup_irq_handler(int irq, void *dev_id)
{
struct wakeup_source *ws = dev_id;
// 1. 更新唤醒统计
spin_lock(&ws->lock);
ws->wakeup_count++;
ws->last_wakeup_time = jiffies;
ws->active = true;
spin_unlock(&ws->lock);
// 2. 调度唤醒工作队列
schedule_work(&wakeup_work);
return IRQ_HANDLED;
}
/**
* @brief 唤醒工作队列处理。
*/
static void wakeup_work_handler(struct work_struct *work)
{
// 1. 触发系统唤醒
pm_wakeup_event();
// 2. 重置唤醒源
// ...
}
1.4 软件设计模式树形分析
Suspend/Resume 设计模式 ├── 策略模式 (Strategy Pattern) │ ├── 挂起策略:根据状态选择不同的挂起方式 │ └── 唤醒策略:根据唤醒源选择不同的唤醒方式 ├── 观察者模式 (Observer Pattern) │ ├── wakeup_irq_handler():观察唤醒中断事件 │ └── device_pm_ops_register():观察设备 PM 状态 ├── 适配器模式 (Adapter Pattern) │ ├── dpm_suspend():适配设备挂起操作 │ └── dpm_resume():适配设备恢复操作 ├── 状态模式 (State Pattern) │ └── 系统状态 (WORKING/SUSPENDING/SUSPENDED/RESUMING) └── 模板方法模式 (Template Method Pattern) └── suspend_enter():定义了 Suspend 的标准流程 (准备->冻结->挂起->恢复->解冻)
1.5 Suspend/Resume 调试核心难点
1.5.1 挂起失败
现象:Suspend 过程中卡住,系统无法进入低功耗状态。
原因:
-
设备挂起函数返回错误。
-
进程冻结超时。
-
唤醒源冲突。
调试方法:
-
使用
echo "power" > /sys/power/pm_trace跟踪挂起。 -
使用
dmesg | grep "suspend"查看挂起日志。 -
使用
cat /proc/pid/status | grep "State"检查进程状态。
1.5.2 唤醒失败
现象:系统挂起后无法唤醒,按电源键无响应。
原因:
-
唤醒中断未正确配置。
-
GIC 中断未使能。
-
电源域未正确上电。
调试方法:
-
检查
/proc/interrupts查看唤醒中断统计。 -
使用
cat /sys/kernel/debug/wakeup_sources查看唤醒源。 -
检查 GIC 和电源域配置。
1.5.3 挂起后设备无法恢复
现象:系统唤醒后,某些设备无法正常工作。
原因:
-
设备恢复函数未正确执行。
-
设备状态未保存。
-
电源域未完全恢复。
调试方法:
-
检查
dpm_resume返回值。 -
检查设备驱动的
resume函数。 -
检查电源域恢复状态。
1.6 与其他模块的协同
| 模块 | 协同方式 | 调试关键点 |
|---|---|---|
| 进程管理 | 冻结和解冻进程 | 进程状态、优先级 |
| 设备驱动 | 实现设备挂起/恢复 | 状态保存、电源管理 |
| 平台 PM | 提供平台级挂起/恢复 | CPU 状态、GIC 状态 |
| 中断控制器 | 处理唤醒中断 | 中断配置、唤醒源 |
| 电源域 | 管理设备电源 | 上下电序列、依赖关系 |
| Cpuidle | 管理 CPU 空闲状态 | 空闲状态选择、进入/退出 |
第二部分 电源管理全景:DVFS 与 温控
2.1 DVFS 与 温控 核心概念
DVFS (Dynamic Voltage and Frequency Scaling) 是嵌入式系统中最重要的电源管理技术之一,它根据系统负载动态调整 CPU/GPU 的工作电压和频率,在性能与功耗之间取得平衡。温控 (Thermal Management) 则是确保系统在安全温度范围内工作,防止过热损坏硬件。
2.1.1 DVFS 架构
[DVFS 系统] ├── [CPUFreq 核心层] (drivers/cpufreq/cpufreq.c) │ ├── 频率表管理 (freq_table) │ ├── 策略管理 (governor) │ ├── 通知链 (notifier chain) │ └── sysfs 接口 ├── [CPUFreq 驱动] (drivers/cpufreq/rockchip-cpufreq.c) │ ├── 频率调整 (set_freq) │ ├── 电压调整 (set_voltage) │ └── 硬件配置 (clk/regulator) ├── [DevFreq 核心] (drivers/devfreq/devfreq.c) │ ├── 设备频率管理 (device freq) │ ├── 策略管理 (governor) │ └── sysfs 接口 ├── [DevFreq 驱动] (drivers/devfreq/rockchip-dfreq.c) │ ├── GPU 频率/电压调整 │ ├── DDR 频率/电压调整 │ └── 硬件配置 (clk/regulator) └── [温控子系统] (drivers/thermal/thermal_core.c) ├── 温度传感器驱动 (tsadc) ├── 冷却设备 (cooling device) ├── 温控策略 (governor) └── 热区管理 (thermal zone)
2.1.2 DVFS 与 温控 工作流程
[系统负载变化] ↓ [CPUFreq governor 触发] ↓ [计算目标频率] ↓ [CPUFreq 驱动调整频率] ↓ [频率调整完成] ↓ [温度变化] ↓ [温度传感器触发中断] ↓ [Thermal governor 触发] ↓ [计算冷却需求] ↓ [调用冷却设备降频] ↓ [温度稳定]
2.2 核心数据结构
2.2.1 CPUFreq 核心结构 (代码出处: drivers/cpufreq/cpufreq.c)
// 代码出处: drivers/cpufreq/cpufreq.c
/**
* @struct cpufreq_policy
* @brief CPUFreq 策略结构,管理一个 CPU 的 DVFS 策略。
*/
struct cpufreq_policy {
unsigned int cpu; /**< CPU 编号 */
unsigned int min; /**< 最小频率 */
unsigned int max; /**< 最大频率 */
unsigned int cur; /**< 当前频率 */
unsigned int target; /**< 目标频率 */
unsigned int freq_table_size; /**< 频率表大小 */
struct cpufreq_frequency_table *freq_table; /**< 频率表 */
struct cpufreq_governor *governor; /**< 治理器 */
struct cpufreq_driver *driver; /**< 驱动 */
unsigned int policy; /**< 策略 (POLICY_MAX/POLICY_MIN) */
unsigned int flags; /**< 标志位 */
struct device *dev; /**< 设备指针 */
struct device_node *np; /**< 设备树节点 */
spinlock_t lock; /**< 自旋锁 */
struct work_struct update_work; /**< 更新工作队列 */
struct list_head list; /**< 链表节点 */
};
/**
* @struct cpufreq_frequency_table
* @brief CPUFreq 频率表结构。
*/
struct cpufreq_frequency_table {
unsigned int frequency; /**< 频率 (kHz) */
unsigned int voltage; /**< 电压 (µV) */
unsigned int flag; /**< 标志位 */
unsigned int index; /**< 索引 */
};
/**
* @struct cpufreq_governor
* @brief CPUFreq 治理器结构。
*/
struct cpufreq_governor {
char name[20]; /**< 治理器名称 (ondemand/interactive) */
int (*governor)(struct cpufreq_policy *policy, unsigned int *target);
int (*init)(struct cpufreq_policy *policy);
int (*exit)(struct cpufreq_policy *policy);
int (*start)(struct cpufreq_policy *policy);
int (*stop)(struct cpufreq_policy *policy);
struct list_head list; /**< 链表节点 */
void *private_data; /**< 私有数据 */
};
2.2.2 DevFreq 结构 (代码出处: drivers/devfreq/devfreq.c)
// 代码出处: drivers/devfreq/devfreq.c
/**
* @struct devfreq
* @brief DevFreq 设备结构,管理一个设备的频率。
*/
struct devfreq {
struct device *dev; /**< 设备指针 */
struct devfreq_dev_profile *profile; /**< 设备配置 */
struct devfreq_governor *governor; /**< 治理器 */
unsigned long min_freq; /**< 最小频率 */
unsigned long max_freq; /**< 最大频率 */
unsigned long cur_freq; /**< 当前频率 */
unsigned long target_freq; /**< 目标频率 */
struct devfreq_frequency_table *freq_table; /**< 频率表 */
struct devfreq_ops *ops; /**< 操作 */
spinlock_t lock; /**< 自旋锁 */
struct work_struct update_work; /**< 更新工作队列 */
struct list_head list; /**< 链表节点 */
};
/**
* @struct devfreq_dev_profile
* @brief DevFreq 设备配置结构。
*/
struct devfreq_dev_profile {
unsigned long initial_freq; /**< 初始频率 */
unsigned long min_freq; /**< 最小频率 */
unsigned long max_freq; /**< 最大频率 */
unsigned long polling_ms; /**< 轮询间隔 */
int (*target)(struct devfreq *devfreq, unsigned long *freq);
int (*get_dev_status)(struct devfreq *devfreq, struct devfreq_dev_status *status);
int (*get_cur_freq)(struct devfreq *devfreq, unsigned long *freq);
int (*set_cur_freq)(struct devfreq *devfreq, unsigned long freq);
int (*get_event)(struct devfreq *devfreq, int event);
int (*set_event)(struct devfreq *devfreq, int event);
};
2.2.3 温控结构 (代码出处: drivers/thermal/thermal_core.c)
// 代码出处: drivers/thermal/thermal_core.c
/**
* @struct thermal_zone_device
* @brief 热区设备结构,管理一个温度区域。
*/
struct thermal_zone_device {
char name[20]; /**< 热区名称 */
int id; /**< 热区 ID */
struct device *dev; /**< 设备指针 */
struct thermal_zone_ops *ops; /**< 热区操作 */
int trips; /**< 温度触发点数量 */
struct thermal_trip *trips; /**< 温度触发点列表 */
struct thermal_governor *governor; /**< 治理器 */
int temperature; /**< 当前温度 */
int target_temp; /**< 目标温度 */
int polling_delay; /**< 轮询延迟 */
struct list_head cooling_devices; /**< 冷却设备列表 */
spinlock_t lock; /**< 自旋锁 */
struct work_struct work; /**< 工作队列 */
int state; /**< 热区状态 */
};
/**
* @struct cooling_device
* @brief 冷却设备结构,用于降温。
*/
struct cooling_device {
char name[20]; /**< 冷却设备名称 */
int id; /**< 冷却设备 ID */
struct device *dev; /**< 设备指针 */
int max_state; /**< 最大状态 (最高降频等级) */
int cur_state; /**< 当前状态 */
int min_state; /**< 最小状态 */
struct cooling_device_ops *ops; /**< 冷却设备操作 */
struct list_head list; /**< 链表节点 */
void *private_data; /**< 私有数据 */
};
2.3 核心代码实现
2.3.1 CPUFreq 核心逻辑 (代码出处: drivers/cpufreq/cpufreq.c)
// 代码出处: drivers/cpufreq/cpufreq.c
/**
* @brief CPUFreq 频率调整。
* @param policy 策略指针
* @param target_freq 目标频率
* @return 0 成功,负数错误
*/
int cpufreq_target(struct cpufreq_policy *policy, unsigned int target_freq)
{
struct cpufreq_frequency_table *freq_table;
unsigned int freq = target_freq;
int ret;
if (!policy || !policy->freq_table) {
return -EINVAL;
}
// 1. 检查频率范围
if (freq < policy->min) {
freq = policy->min;
} else if (freq > policy->max) {
freq = policy->max;
}
// 2. 查找最近的可用频率
freq_table = policy->freq_table;
for (int i = 0; i < policy->freq_table_size; i++) {
if (freq_table[i].frequency == freq) {
break;
}
// 选择最接近的频率
if (freq_table[i].frequency < freq) {
// 向下取整
if (i > 0 && freq_table[i-1].frequency > freq) {
freq = freq_table[i-1].frequency;
}
}
}
// 3. 检查是否需要调整
if (freq == policy->cur) {
return 0;
}
// 4. 调用驱动调整频率
ret = policy->driver->target(policy, freq);
if (ret < 0) {
return ret;
}
// 5. 更新当前频率
policy->cur = freq;
// 6. 通知策略更新
cpufreq_notify_transition(policy, freq);
return 0;
}
/**
* @brief CPUFreq 治理器 (ondemand)。
* @param policy 策略指针
* @param target 目标频率
* @return 0 成功,负数错误
*/
int cpufreq_governor_ondemand(struct cpufreq_policy *policy, unsigned int *target)
{
unsigned long load;
unsigned int freq;
int ret;
// 1. 计算当前 CPU 负载
load = cpufreq_get_load(policy);
if (load < 0) {
return load;
}
// 2. 计算目标频率
freq = policy->cur;
if (load > 80) {
// 高负载,升频
freq = policy->max;
} else if (load < 20) {
// 低负载,降频
freq = policy->min;
} else {
// 中等负载,按比例调整
freq = policy->min + (policy->max - policy->min) * load / 100;
}
*target = freq;
return 0;
}
2.3.2 DevFreq 核心逻辑 (代码出处: drivers/devfreq/devfreq.c)
// 代码出处: drivers/devfreq/devfreq.c
/**
* @brief DevFreq 频率调整。
* @param devfreq DevFreq 设备指针
* @param target_freq 目标频率
* @return 0 成功,负数错误
*/
int devfreq_target(struct devfreq *devfreq, unsigned long target_freq)
{
unsigned long old_freq;
int ret;
if (!devfreq) {
return -EINVAL;
}
// 1. 检查频率范围
if (target_freq < devfreq->min_freq) {
target_freq = devfreq->min_freq;
} else if (target_freq > devfreq->max_freq) {
target_freq = devfreq->max_freq;
}
// 2. 获取当前频率
old_freq = devfreq->cur_freq;
// 3. 检查是否需要调整
if (target_freq == old_freq) {
return 0;
}
// 4. 调用驱动调整频率
ret = devfreq->ops->target(devfreq, &target_freq);
if (ret < 0) {
return ret;
}
// 5. 更新当前频率
devfreq->cur_freq = target_freq;
// 6. 通知策略更新
devfreq_notify_transition(devfreq, target_freq, old_freq);
return 0;
}
/**
* @brief DevFreq 治理器 (simple_ondemand)。
* @param devfreq DevFreq 设备指针
* @param target 目标频率
* @return 0 成功,负数错误
*/
int devfreq_governor_simple_ondemand(struct devfreq *devfreq, unsigned long *target)
{
unsigned long load;
unsigned long freq;
// 1. 获取负载
load = devfreq_get_load(devfreq);
if (load < 0) {
return load;
}
// 2. 计算目标频率
freq = devfreq->cur_freq;
if (load > 80) {
freq = devfreq->max_freq;
} else if (load < 20) {
freq = devfreq->min_freq;
} else {
freq = devfreq->min_freq + (devfreq->max_freq - devfreq->min_freq) * load / 100;
}
*target = freq;
return 0;
}
2.3.3 温控核心逻辑 (代码出处: drivers/thermal/thermal_core.c)
// 代码出处: drivers/thermal/thermal_core.c
/**
* @brief 温度检测与冷却处理。
* @param tz 热区设备指针
* @return 0 成功,负数错误
*/
int thermal_zone_update(struct thermal_zone_device *tz)
{
int temp;
int ret;
// 1. 读取当前温度
ret = tz->ops->get_temp(tz, &temp);
if (ret < 0) {
return ret;
}
// 2. 更新温度
tz->temperature = temp;
// 3. 检查温度阈值
for (int i = 0; i < tz->trips; i++) {
struct thermal_trip *trip = &tz->trips[i];
if (temp >= trip->temperature) {
// 触发冷却
ret = thermal_zone_trigger_cooling(tz, trip);
if (ret < 0) {
return ret;
}
}
}
return 0;
}
/**
* @brief 触发冷却。
* @param tz 热区设备指针
* @param trip 触发点
* @return 0 成功,负数错误
*/
int thermal_zone_trigger_cooling(struct thermal_zone_device *tz,
struct thermal_trip *trip)
{
struct cooling_device *cdev;
int state;
int ret = 0;
// 1. 计算冷却状态
state = (tz->temperature - trip->temperature) / 10;
if (state < 0) {
state = 0;
}
if (state > tz->cooling_devices[0].max_state) {
state = tz->cooling_devices[0].max_state;
}
// 2. 遍历冷却设备
list_for_each_entry(cdev, &tz->cooling_devices, list) {
// 3. 设置冷却设备状态
ret = cdev->ops->set_state(cdev, state);
if (ret < 0) {
return ret;
}
}
return 0;
}
2.3.4 温度传感器驱动 (代码出处: drivers/thermal/rockchip_thermal.c)
// 代码出处: drivers/thermal/rockchip_thermal.c
/**
* @brief 读取温度传感器。
* @param tz 热区设备指针
* @param temp 温度输出
* @return 0 成功,负数错误
*/
static int rockchip_thermal_get_temp(struct thermal_zone_device *tz, int *temp)
{
struct rockchip_thermal_data *data = thermal_zone_get_drvdata(tz);
u32 val;
int temperature;
// 1. 读取传感器寄存器
val = readl(data->base + TSADC_DATA_REG);
val &= 0xFFF;
// 2. 转换为摄氏温度 (芯片特定公式)
temperature = (val * 500) - 40000;
*temp = temperature;
return 0;
}
/**
* @brief 初始化温度传感器。
* @param pdev Platform 设备指针
* @param data 私有数据
* @return 0 成功,负数错误
*/
int rockchip_thermal_init(struct platform_device *pdev, struct rockchip_thermal_data *data)
{
struct device *dev = &pdev->dev;
// 1. 获取时钟
data->pclk = devm_clk_get(dev, "pclk");
if (IS_ERR(data->pclk)) {
return PTR_ERR(data->pclk);
}
clk_prepare_enable(data->pclk);
// 2. 获取中断
data->irq = platform_get_irq(pdev, 0);
if (data->irq < 0) {
return data->irq;
}
// 3. 初始化硬件
writel(0x1, data->base + TSADC_CON);
writel(0x1, data->base + TSADC_INT_EN);
// 4. 注册热区
data->tzd = thermal_zone_device_register("rockchip-thermal", 0, 0,
data, &rockchip_thermal_ops,
NULL, 0, 0);
if (IS_ERR(data->tzd)) {
return PTR_ERR(data->tzd);
}
return 0;
}
2.3.5 冷却设备实现 (代码出处: drivers/thermal/cpu_cooling.c)
// 代码出处: drivers/thermal/cpu_cooling.c
/**
* @brief CPU 冷却设备创建。
* @param cpufreq_policy CPUFreq 策略指针
* @return 冷却设备指针
*/
struct cooling_device *cpu_cooling_device_create(struct cpufreq_policy *policy)
{
struct cooling_device *cdev;
// 1. 分配冷却设备
cdev = kzalloc(sizeof(*cdev), GFP_KERNEL);
if (!cdev) {
return NULL;
}
cdev->name = "cpu-cooling";
cdev->id = policy->cpu;
cdev->max_state = policy->freq_table_size - 1;
cdev->cur_state = 0;
cdev->min_state = 0;
cdev->ops = &cpu_cooling_ops;
cdev->private_data = policy;
// 2. 注册冷却设备
thermal_cooling_device_register(cdev);
return cdev;
}
/**
* @brief 设置 CPU 冷却设备状态。
* @param cdev 冷却设备指针
* @param state 状态值
* @return 0 成功,负数错误
*/
int cpu_cooling_set_state(struct cooling_device *cdev, int state)
{
struct cpufreq_policy *policy = cdev->private_data;
unsigned int freq;
// 1. 检查状态范围
if (state < cdev->min_state) {
state = cdev->min_state;
}
if (state > cdev->max_state) {
state = cdev->max_state;
}
// 2. 计算目标频率
if (state == cdev->max_state) {
freq = policy->min;
} else {
freq = policy->freq_table[state].frequency;
}
// 3. 设置 CPU 频率
cpufreq_target(policy, freq);
cdev->cur_state = state;
return 0;
}
2.3.6 DVFS 与 温控 协同 (代码出处: drivers/thermal/thermal_core.c)
// 代码出处: drivers/thermal/thermal_core.c
/**
* @brief 温控策略: step_wise。
* @param tz 热区设备指针
* @param trip 触发点
* @return 0 成功,负数错误
*/
int thermal_governor_step_wise(struct thermal_zone_device *tz,
struct thermal_trip *trip)
{
struct cooling_device *cdev;
int state;
int ret;
// 1. 计算当前状态
if (tz->temperature < trip->temperature) {
// 温度已恢复,逐步降低冷却等级
state = tz->cooling_devices[0].cur_state - 1;
if (state < 0) {
state = 0;
}
} else if (tz->temperature > trip->temperature + 10) {
// 温度继续升高,提高冷却等级
state = tz->cooling_devices[0].cur_state + 1;
if (state > tz->cooling_devices[0].max_state) {
state = tz->cooling_devices[0].max_state;
}
} else {
// 温度稳定,保持当前等级
state = tz->cooling_devices[0].cur_state;
}
// 2. 应用冷却
list_for_each_entry(cdev, &tz->cooling_devices, list) {
ret = cdev->ops->set_state(cdev, state);
if (ret < 0) {
return ret;
}
}
return 0;
}
/**
* @brief 智能温控策略。
* @param tz 热区设备指针
* @param trip 触发点
* @return 0 成功,负数错误
*/
int thermal_governor_intelligent(struct thermal_zone_device *tz,
struct thermal_trip *trip)
{
struct cooling_device *cdev;
int state;
int ret;
// 1. 基于负载计算目标状态
unsigned long load = cpufreq_get_load(tz->cooling_devices[0].private_data);
int target_state = (load * tz->cooling_devices[0].max_state) / 100;
// 2. 调整冷却状态
if (tz->temperature > trip->temperature) {
state = target_state + 1;
if (state > tz->cooling_devices[0].max_state) {
state = tz->cooling_devices[0].max_state;
}
} else if (tz->temperature < trip->temperature - 10) {
state = target_state - 1;
if (state < 0) {
state = 0;
}
} else {
state = target_state;
}
// 3. 应用冷却
list_for_each_entry(cdev, &tz->cooling_devices, list) {
ret = cdev->ops->set_state(cdev, state);
if (ret < 0) {
return ret;
}
}
return 0;
}
2.4 软件设计模式树形分析
DVFS 与 温控 设计模式 ├── 策略模式 (Strategy Pattern) │ ├── cpufreq_governor_ondemand:ondemand 降频策略 │ ├── devfreq_governor_simple_ondemand:简单降频策略 │ ├── thermal_governor_step_wise:步进冷却策略 │ └── thermal_governor_intelligent:智能冷却策略 ├── 观察者模式 (Observer Pattern) │ ├── cpufreq_notify_transition():观察频率变化 │ └── thermal_zone_update():观察温度变化 ├── 适配器模式 (Adapter Pattern) │ ├── cpu_cooling_device_create():适配 CPUFreq 到冷却设备 │ └── devfreq_get_load():适配设备负载 ├── 工厂模式 (Factory Pattern) │ ├── cpufreq_register_driver():注册 CPUFreq 驱动 │ └── thermal_cooling_device_register():注册冷却设备 └── 模板方法模式 (Template Method Pattern) ├── cpufreq_target():定义了频率调整的标准流程 └── thermal_zone_update():定义了温度检测的标准流程
2.5 DVFS 与 温控 调试核心难点
2.5.1 频率切换延迟
现象:频率切换时系统响应慢,帧率不稳定。
原因:
-
频率切换时锁被持有。
-
电压调整时间长。
-
策略切换频繁。
调试方法:
-
使用
perf stat -e cpu_freq:*监控频率切换。 -
检查
cpufreq_target执行时间。 -
调整策略阈值。
2.5.2 温度读取错误
现象:温度读数与实际温度偏差大,导致错误触发冷却。
原因:
-
传感器校准不准确。
-
采样频率过高或过低。
-
中断处理延迟。
调试方法:
-
使用
cat /sys/class/thermal/thermal_zone*/temp查看温度。 -
检查传感器转换公式。
-
调整采样频率。
2.5.3 冷却设备不响应
现象:温度超标后 CPU 频率未降低,系统过热。
原因:
-
冷却设备未注册。
-
冷却设备状态设置失败。
-
策略配置错误。
调试方法:
-
检查
/sys/class/thermal/cooling_device*设备是否存在。 -
手动设置冷却状态测试。
-
调整策略配置。
2.6 与其他模块的协同
| 模块 | 协同方式 | 调试关键点 |
|---|---|---|
| CPUFreq | 提供 CPU 频率调整 | 频率表、策略选择 |
| DevFreq | 提供 GPU/DDR 频率调整 | 频率表、策略选择 |
| 温控 | 提供温度检测和冷却 | 传感器、冷却设备 |
| 时钟控制器 | 提供频率调整时钟 | 时钟频率、分频器 |
| 电压调节器 | 提供电压调整 | 电压范围、稳定性 |
| 设备树 | 配置 DVFS 参数 | 频率表、电压值 |
第三部分 Android 安全机制:SELinux
3.1 SELinux 核心概念
SELinux (Security-Enhanced Linux) 是 Linux 内核的安全模块,通过 强制访问控制 (MAC, Mandatory Access Control) 机制,对进程、文件、设备等系统资源进行细粒度的权限管理。Android 从 4.3 版本开始引入 SELinux,并在后续版本中逐步收紧安全策略,成为 Android 安全体系的基石。
3.1.1 SELinux 与 DAC 的对比
| 特性 | DAC (传统权限) | SELinux (MAC) |
|---|---|---|
| 权限控制 | 基于用户和组 | 基于安全上下文 |
| 控制粒度 | 粗粒度 (读/写/执行) | 细粒度 (具体操作) |
| 权限继承 | 子进程继承父进程权限 | 子进程有自己的上下文 |
| 恶意程序防护 | 弱 (可继承所有权限) | 强 (即使 root 也受限制) |
| 策略配置 | 简单 (chmod) | 复杂 (策略文件) |
| 生效范围 | 全局 | 可针对特定进程/文件 |
3.1.2 SELinux 核心架构
[用户空间策略] ├── 策略文件 (policy.conf) ├── 策略包 (policy.31) └── 策略加载工具 (load_policy) ↓ [SELinux 内核模块] ├── 访问控制服务器 (avc) ├── 策略引擎 (ss) ├── 上下文管理 (context) └── 审计日志 (audit) ↓ [系统调用] ├── 进程访问 ├── 文件访问 └── 设备访问 ↓ [LSM 钩子] ├── inode_* hooks ├── file_* hooks ├── task_* hooks └── socket_* hooks
3.1.3 SELinux 工作流程
[访问请求] (进程请求访问文件/设备) ↓ [SELinux 检查] (avc_has_perm) ↓ [查找 AVC 缓存] → [命中] → [返回允许/拒绝] ↓ [未命中] [查找策略] (security_compute_av) ↓ [计算权限] (selinux_has_perm) ↓ [记录审计日志] (avc_audit) ↓ [返回结果]
3.2 核心数据结构
3.2.1 SELinux 核心结构 (代码出处: security/selinux/avc.c)
// 代码出处: security/selinux/avc.c
/**
* @struct avc_entry
* @brief AVC (Access Vector Cache) 条目结构。
*/
struct avc_entry {
struct avc_node *node; /**< 关联节点 */
u32 ssid; /**< 源安全上下文 ID */
u32 tsid; /**< 目标安全上下文 ID */
u16 tclass; /**< 对象类 */
struct av_decision avd; /**< 访问决策 */
u16 seqno; /**< 序列号 */
};
/**
* @struct avc_node
* @brief AVC 节点结构。
*/
struct avc_node {
struct avc_entry ae; /**< 关联条目 */
struct rb_node rb; /**< 红黑树节点 */
struct list_head list; /**< 链表节点 */
atomic_t count; /**< 引用计数 */
u32 key; /**< 哈希键 */
};
/**
* @struct avc_cache
* @brief AVC 缓存结构。
*/
struct avc_cache {
struct rb_root root; /**< 红黑树根 */
struct list_head lru; /**< LRU 链表 */
u32 entries; /**< 条目数 */
u32 max_entries; /**< 最大条目数 */
spinlock_t lock; /**< 自旋锁 */
};
/**
* @struct selinux_state
* @brief SELinux 状态结构。
*/
struct selinux_state {
bool initialized; /**< 是否已初始化 */
bool enforcing; /**< 是否强制执行 */
struct avc_cache avc; /**< AVC 缓存 */
struct policydb *policy; /**< 策略数据库 */
struct sidtab *sidtab; /**< 安全上下文 ID 表 */
struct list_head policy_list; /**< 策略列表 */
struct work_struct reload_work; /**< 重载工作队列 */
spinlock_t lock; /**< 自旋锁 */
struct audit_controller audit; /**< 审计控制器 */
};
3.3 核心代码实现
3.3.1 SELinux 初始化 (代码出处: security/selinux/selinux.c)
// 代码出处: security/selinux/selinux.c
/**
* @brief SELinux 核心初始化。
* @return 0 成功,负数错误
*/
static int __init selinux_init(void)
{
int ret;
// 1. 注册 LSM
ret = register_security(&selinux_ops);
if (ret < 0) {
pr_err("SELinux: Failed to register LSM\n");
return ret;
}
// 2. 初始化 AVC 缓存
ret = avc_init();
if (ret < 0) {
pr_err("SELinux: Failed to init AVC\n");
return ret;
}
// 3. 初始化策略数据库
ret = policydb_init(&selinux_state.policy);
if (ret < 0) {
pr_err("SELinux: Failed to init policy\n");
return ret;
}
// 4. 初始化审计
ret = audit_init();
if (ret < 0) {
pr_err("SELinux: Failed to init audit\n");
return ret;
}
// 5. 注册 sysfs 接口
ret = securityfs_create_file("selinux", 0444, NULL, NULL, &selinux_fops);
if (ret < 0) {
pr_err("SELinux: Failed to create securityfs\n");
return ret;
}
pr_info("SELinux: Initialized\n");
return 0;
}
/**
* @brief 加载 SELinux 策略。
* @param policy_data 策略数据
* @param policy_len 策略长度
* @return 0 成功,负数错误
*/
int selinux_load_policy(const char *policy_data, size_t policy_len)
{
struct policydb *policydb = selinux_state.policy;
int ret;
// 1. 检查策略数据
if (!policy_data || policy_len == 0) {
return -EINVAL;
}
// 2. 解析策略
ret = policydb_read(policydb, policy_data, policy_len);
if (ret < 0) {
pr_err("SELinux: Failed to read policy\n");
return ret;
}
// 3. 构建 SID 表
ret = sidtab_init(&selinux_state.sidtab);
if (ret < 0) {
pr_err("SELinux: Failed to init sidtab\n");
return ret;
}
// 4. 设置强制执行模式
selinux_state.enforcing = true;
// 5. 触发策略更新
selinux_state.policy_updated = true;
return 0;
}
3.3.2 访问控制检查 (代码出处: security/selinux/avc.c)
// 代码出处: security/selinux/avc.c
/**
* @brief AVC 访问检查。
* @param ssid 源安全上下文 ID
* @param tsid 目标安全上下文 ID
* @param tclass 对象类
* @param requested 请求的权限
* @param avd 访问决策输出
* @return 0 允许,负数拒绝
*/
int avc_has_perm(u32 ssid, u32 tsid, u16 tclass, u32 requested, struct av_decision *avd)
{
struct avc_node *node;
int ret;
// 1. 检查缓存
node = avc_lookup(ssid, tsid, tclass);
if (node) {
*avd = node->ae.avd;
if (avd->allowed & requested) {
return 0;
} else {
return -EACCES;
}
}
// 2. 未命中缓存,查询策略
ret = security_compute_av(ssid, tsid, tclass, requested, avd);
if (ret < 0) {
return ret;
}
// 3. 插入缓存
avc_insert(ssid, tsid, tclass, avd);
// 4. 检查权限
if (avd->allowed & requested) {
return 0;
} else {
return -EACCES;
}
}
/**
* @brief 策略计算权限。
* @param ssid 源安全上下文 ID
* @param tsid 目标安全上下文 ID
* @param tclass 对象类
* @param requested 请求的权限
* @param avd 访问决策输出
* @return 0 成功,负数错误
*/
int security_compute_av(u32 ssid, u32 tsid, u16 tclass, u32 requested, struct av_decision *avd)
{
struct context *scontext = sidtab_search(ssid);
struct context *tcontext = sidtab_search(tsid);
struct policydb *policydb = selinux_state.policy;
struct avtab_key key;
struct avtab_node *node;
u32 perm_mask;
// 1. 检查上下文
if (!scontext || !tcontext) {
return -EINVAL;
}
// 2. 查找策略
key.source_type = scontext->type;
key.target_type = tcontext->type;
key.target_class = tclass;
key.perm_mask = requested;
node = avtab_search(&policydb->avtab, &key);
if (!node) {
return -EACCES;
}
// 3. 计算权限
avd->allowed = node->datum->allowed & requested;
avd->auditallow = node->datum->auditallow & requested;
avd->auditdeny = node->datum->auditdeny & requested;
return 0;
}
3.3.3 安全上下文管理 (代码出处: security/selinux/ss/context.c)
// 代码出处: security/selinux/ss/context.c
/**
* @struct context
* @brief 安全上下文结构。
*/
struct context {
u32 type; /**< 类型 */
u32 role; /**< 角色 */
u32 user; /**< 用户 */
char *str; /**< 字符串表示 */
struct {
u32 count; /**< 长度 */
u32 *values; /**< 值列表 */
} mls; /**< 多级安全 */
u32 flags; /**< 标志 */
};
/**
* @brief 解析安全上下文。
* @param str 字符串表示
* @param ctx 上下文输出
* @return 0 成功,负数错误
*/
int context_from_string(const char *str, struct context *ctx)
{
char *scontext = kstrdup(str, GFP_KERNEL);
char *tmp = scontext;
char *token;
int ret = 0;
if (!scontext) {
return -ENOMEM;
}
// 1. 解析用户
token = strsep(&tmp, ":");
if (!token) {
ret = -EINVAL;
goto out;
}
ctx->user = user_by_name(token);
if (!ctx->user) {
ret = -EINVAL;
goto out;
}
// 2. 解析角色
token = strsep(&tmp, ":");
if (!token) {
ret = -EINVAL;
goto out;
}
ctx->role = role_by_name(token);
if (!ctx->role) {
ret = -EINVAL;
goto out;
}
// 3. 解析类型
token = strsep(&tmp, ":");
if (!token) {
ret = -EINVAL;
goto out;
}
ctx->type = type_by_name(token);
if (!ctx->type) {
ret = -EINVAL;
goto out;
}
// 4. 解析 MLS (如果存在)
if (tmp && *tmp) {
ctx->mls.values = kcalloc(1, sizeof(u32), GFP_KERNEL);
if (!ctx->mls.values) {
ret = -ENOMEM;
goto out;
}
ctx->mls.count = 1;
ctx->mls.values[0] = mls_level_by_name(tmp);
if (!ctx->mls.values[0]) {
ret = -EINVAL;
goto out;
}
}
out:
kfree(scontext);
return ret;
}
3.3.4 策略加载与验证 (代码出处: security/selinux/ss/policydb.c)
// 代码出处: security/selinux/ss/policydb.c
/**
* @struct policydb
* @brief 策略数据库结构。
*/
struct policydb {
struct avtab avtab; /**< 权限表 */
struct hashtab *types; /**< 类型表 */
struct hashtab *roles; /**< 角色表 */
struct hashtab *users; /**< 用户表 */
struct hashtab *classes; /**< 类表 */
struct hashtab *perms; /**< 权限表 */
struct hashtab *mls_ops; /**< MLS 操作 */
u32 version; /**< 策略版本 */
int (*read)(struct policydb *p, void *data, size_t len);
int (*write)(struct policydb *p, void *data, size_t len);
void (*destroy)(struct policydb *p);
};
/**
* @brief 读取策略文件。
* @param p 策略数据库
* @param data 策略数据
* @param len 策略长度
* @return 0 成功,负数错误
*/
int policydb_read(struct policydb *p, const char *data, size_t len)
{
struct avtab_key key;
struct avtab_node *node;
int ret;
// 1. 检查版本
u32 version = *(u32 *)data;
if (version < POLICYDB_VERSION_MIN || version > POLICYDB_VERSION_MAX) {
return -EINVAL;
}
// 2. 初始化权限表
ret = avtab_init(&p->avtab);
if (ret < 0) {
return ret;
}
// 3. 读取权限表
ret = avtab_read(&p->avtab, data + 4, len - 4);
if (ret < 0) {
goto out;
}
// 4. 读取类型表
ret = hashtab_read(&p->types, data + 4 + p->avtab.size, len - 4 - p->avtab.size);
if (ret < 0) {
goto out;
}
return 0;
out:
avtab_destroy(&p->avtab);
return ret;
}
3.3.5 Android 特有 SELinux 策略 (代码出处: system/sepolicy/)
// 代码出处: system/sepolicy/te_macros
// 1. 定义 Android 特有权限
type foo_service, domain;
type foo_service_exec, exec_type, file_type, vendor_file_type;
init_daemon_domain(foo_service);
// 2. 授予权限
allow foo_service self:capability net_admin;
allow foo_service self:udp_socket { create bind connect };
allow foo_service self:tcp_socket { create bind connect };
allow foo_service sysfs:file rw_file_perms;
allow foo_service proc_net:file rw_file_perms;
// 3. 定义 Android 特定宏
# 宏定义: 为服务添加权限
define(`init_daemon_domain', `
type $1, domain;
type $1_exec, exec_type, file_type;
init_daemon_domain($1, $1_exec)
')
# 宏定义: 允许访问网络
define(`allow_network', `
allow $1 self:udp_socket { create bind connect };
allow $1 self:tcp_socket { create bind connect };
allow $1 net_iface:udp_socket { read write };
')
3.4 软件设计模式树形分析
SELinux 设计模式 ├── 策略模式 (Strategy Pattern) │ ├── avc_has_perm():访问检查策略 │ └── security_compute_av():权限计算策略 ├── 观察者模式 (Observer Pattern) │ ├── avc_lookup():观察 AVC 缓存 │ └── selinux_load_policy():观察策略更新 ├── 适配器模式 (Adapter Pattern) │ ├── context_from_string():适配字符串到上下文 │ └── policydb_read():适配二进制策略文件 ├── 工厂模式 (Factory Pattern) │ ├── selinux_init():创建 SELinux 状态 │ └── avc_init():创建 AVC 缓存 └── 模板方法模式 (Template Method Pattern) └── avc_has_perm():定义了访问检查的标准流程 (缓存查找->策略计算->审计)
3.5 SELinux 调试核心难点
3.5.1 权限拒绝
现象:avc: denied { read } for pid=1234 comm="app" 日志。
原因:
-
进程没有访问目标文件的权限。
-
文件上下文配置错误。
-
策略加载失败。
调试方法:
-
使用
audit2allow -i /var/log/audit.log生成允许规则。 -
检查文件上下文:
ls -Z /path/to/file。 -
临时禁用 SELinux:
setenforce 0。
3.5.2 策略加载失败
现象:load_policy 返回 -EINVAL。
原因:
-
策略文件损坏。
-
策略版本不匹配。
-
策略编译错误。
调试方法:
-
使用
checkpolicy -c policy.conf验证策略文件。 -
使用
sepolicy-inspect policy.31查看策略内容。 -
检查策略编译日志。
3.5.3 上下文配置错误
现象:进程或文件的上下文不是预期的。
原因:
-
file_contexts配置错误。 -
mac_permissions.xml配置错误。 -
进程未正确设置上下文。
调试方法:
-
检查
/sys/fs/selinux/context。 -
查看
file_contexts文件。 -
检查
mac_permissions.xml配置。
3.6 与其他模块的协同
| 模块 | 协同方式 | 调试关键点 |
|---|---|---|
| LSM 框架 | 提供安全钩子 | 钩子注册、调用顺序 |
| 审计系统 | 记录安全事件 | 审计日志、事件过滤 |
| 进程管理 | 进程上下文管理 | 上下文继承、创建 |
| 文件系统 | 文件上下文管理 | 上下文存储、xattr |
| 用户空间 | 通过 libselinux 访问 |
策略编译、上下文转换 |
| 设备驱动 | 设备上下文管理 | 设备节点、权限 |
第四部分 Android 安全机制:TrustZone
4.1 TrustZone 核心概念
TrustZone 是 ARM 架构提供的硬件安全扩展技术,它通过硬件隔离将处理器划分为 安全世界 (Secure World) 和 非安全世界 (Normal World)。安全世界运行可信执行环境 (TEE, Trusted Execution Environment),用于处理敏感数据和安全操作;非安全世界运行普通操作系统 (如 Android/Linux)。TrustZone 是 Android 安全体系的核心基础,广泛应用于指纹识别、支付、密钥管理等场景。
4.1.1 ARM64 异常级别与 TrustZone
[非安全世界 (Normal World)] [安全世界 (Secure World)] ↓ ↓ [EL0 (应用层)] [EL0 (TEE 应用)] ↓ ↓ [EL1 (内核层)] [EL1 (TEE 内核/OP-TEE)] ↓ ↓ [EL2 (Hypervisor)] [EL2 (Secure Hypervisor)] ↓ ↓ [EL3 (Monitor)] → [SMC (Secure Monitor Call)] ← [EL3 (Monitor)]
4.1.2 TrustZone 架构
[非安全世界] ├── Android/Linux (EL1) │ ├── 应用层 (EL0) │ ├── 内核层 (EL1) │ └── TEE 客户端 API (libteec) ↓ [EL3 Monitor (Monitor Mode)] ├── SMC 处理 (SMC handler) ├── 上下文切换 (context switch) └── 安全中断处理 (secure interrupt) ↓ [安全世界] ├── OP-TEE (EL1) │ ├── TEE 内核 (OP-TEE Core) │ ├── TEE 应用 (Trusted Application) │ └── TEE 客户端 API (libteec) └── TEE 驱动 (Linux 内核) └── drivers/tee/optee/
4.1.3 TEE 工作流程
[非安全世界应用] ↓ 1. 调用 TEE 客户端 API (libteec) ↓ 2. 通过 TEE 驱动 (optee) 发送请求 ↓ 3. 触发 SMC (Secure Monitor Call) ↓ 4. EL3 Monitor 切换到安全世界 ↓ 5. OP-TEE 内核接收请求 ↓ 6. 调用 Trusted Application (TA) ↓ 7. TA 处理安全操作 ↓ 8. 返回结果给 OP-TEE 内核 ↓ 9. 触发 SMC 返回非安全世界 ↓ 10. TEE 驱动返回结果给应用
4.2 核心数据结构
4.2.1 TEE 驱动核心结构 (代码出处: drivers/tee/optee/optee_core.c)
// 代码出处: drivers/tee/optee/optee_core.c
/**
* @struct optee
* @brief OP-TEE 核心结构,管理 TEE 设备。
*/
struct optee {
struct tee_device *teedev; /**< TEE 设备 */
struct tee_shm_pool *pool; /**< 共享内存池 */
void *secure_monitor; /**< 安全监视器 */
struct optee_ops *ops; /**< 操作函数 */
struct device *dev; /**< 设备指针 */
struct list_head teedev_list; /**< TEE 设备列表 */
spinlock_t lock; /**< 自旋锁 */
struct work_struct cmd_work; /**< 命令工作队列 */
struct work_struct msg_work; /**< 消息工作队列 */
};
/**
* @struct optee_session
* @brief TEE 会话结构,代表一个 TEE 会话。
*/
struct optee_session {
struct list_head list; /**< 会话列表节点 */
u32 session_id; /**< 会话 ID */
u32 client_id; /**< 客户端 ID */
struct tee_context *ctx; /**< TEE 上下文 */
void *private_data; /**< 私有数据 */
};
/**
* @struct optee_cmd
* @brief TEE 命令结构。
*/
struct optee_cmd {
u32 cmd; /**< 命令类型 */
u32 flags; /**< 命令标志 */
u32 ref_count; /**< 引用计数 */
struct tee_shm *shm; /**< 共享内存 */
struct optee_session *session; /**< 会话 */
struct optee_buf *params; /**< 参数列表 */
u32 num_params; /**< 参数数量 */
struct completion *done; /**< 完成信号 */
int ret; /**< 返回结果 */
};
4.2.2 共享内存管理 (代码出处: drivers/tee/tee_shm.c)
// 代码出处: drivers/tee/tee_shm.c
/**
* @struct tee_shm
* @brief TEE 共享内存结构。
*/
struct tee_shm {
struct kref ref; /**< 引用计数 */
struct tee_device *teedev; /**< 所属设备 */
struct tee_shm_pool *pool; /**< 所属内存池 */
void *kaddr; /**< 内核地址 */
dma_addr_t dma_addr; /**< DMA 地址 */
u32 flags; /**< 标志 */
size_t size; /**< 大小 */
struct page **pages; /**< 页面数组 */
struct list_head list; /**< 链表节点 */
spinlock_t lock; /**< 自旋锁 */
};
4.3 核心代码实现
4.3.1 OP-TEE 驱动初始化 (代码出处: drivers/tee/optee/optee_core.c)
// 代码出处: drivers/tee/optee/optee_core.c
/**
* @brief OP-TEE 驱动初始化。
* @param pdev Platform 设备指针
* @return 0 成功,负数错误
*/
static int optee_probe(struct platform_device *pdev)
{
struct optee *optee;
struct tee_device *teedev;
struct tee_shm_pool *pool;
int ret;
// 1. 分配 OP-TEE 结构
optee = devm_kzalloc(&pdev->dev, sizeof(*optee), GFP_KERNEL);
if (!optee) {
return -ENOMEM;
}
// 2. 初始化共享内存池
pool = tee_shm_pool_alloc(&pdev->dev, &optee_pool_ops);
if (IS_ERR(pool)) {
return PTR_ERR(pool);
}
optee->pool = pool;
// 3. 创建 TEE 设备
teedev = tee_device_alloc(tee_cdev, NULL, pool, &pdev->dev,
&optee_tee_ops, "optee");
if (IS_ERR(teedev)) {
ret = PTR_ERR(teedev);
goto error_pool;
}
optee->teedev = teedev;
optee->dev = &pdev->dev;
spin_lock_init(&optee->lock);
INIT_LIST_HEAD(&optee->teedev_list);
// 4. 注册 TEE 设备
ret = tee_device_register(teedev);
if (ret < 0) {
goto error_device;
}
// 5. 创建 SMC 接口
ret = tee_get_smc_abi(&optee->secure_monitor);
if (ret < 0) {
goto error_device;
}
// 6. 设置平台数据
platform_set_drvdata(pdev, optee);
pr_info("OP-TEE driver initialized\n");
return 0;
error_device:
tee_device_unregister(teedev);
error_pool:
tee_shm_pool_free(pool);
return ret;
}
4.3.2 SMC 调用处理 (代码出处: drivers/tee/optee/optee_smc.c)
// 代码出处: drivers/tee/optee/optee_smc.c
/**
* @brief 执行 SMC 调用。
* @param smc_fn 函数 ID
* @param arg0 参数 0
* @param arg1 参数 1
* @param arg2 参数 2
* @param arg3 参数 3
* @return SMC 结果
*/
u64 optee_smc_call(u64 smc_fn, u64 arg0, u64 arg1, u64 arg2, u64 arg3)
{
struct arm_smccc_res res;
u64 result;
// 1. 准备 SMC 参数
struct arm_smccc_args args = {
.a0 = smc_fn,
.a1 = arg0,
.a2 = arg1,
.a3 = arg2,
.a4 = arg3,
.a5 = 0,
.a6 = 0,
.a7 = 0,
};
// 2. 执行 SMC 调用
arm_smccc_smc(&args, &res);
// 3. 检查结果
if (res.a0 != SMC_OK) {
pr_err("SMC call failed: %lld\n", res.a0);
return -EIO;
}
result = res.a1;
return result;
}
/**
* @brief 处理 TEE 命令。
* @param optee OP-TEE 指针
* @param cmd 命令指针
* @return 0 成功,负数错误
*/
int optee_handle_cmd(struct optee *optee, struct optee_cmd *cmd)
{
u64 result;
int ret;
// 1. 检查命令类型
switch (cmd->cmd) {
case TEE_CMD_OPEN_SESSION:
result = optee_smc_call(SMC_CMD_OPEN_SESSION,
cmd->session->client_id,
cmd->session->session_id,
0, 0);
break;
case TEE_CMD_INVOKE_COMMAND:
result = optee_smc_call(SMC_CMD_INVOKE_COMMAND,
cmd->session->client_id,
cmd->session->session_id,
cmd->shm->dma_addr,
cmd->params[0].value);
break;
case TEE_CMD_CLOSE_SESSION:
result = optee_smc_call(SMC_CMD_CLOSE_SESSION,
cmd->session->client_id,
cmd->session->session_id,
0, 0);
break;
default:
return -EINVAL;
}
// 2. 检查结果
if (result == SMC_ERROR) {
return -EIO;
}
// 3. 完成命令
if (cmd->done) {
complete(cmd->done);
}
return 0;
}
4.3.3 共享内存管理 (代码出处: drivers/tee/tee_shm.c)
// 代码出处: drivers/tee/tee_shm.c
/**
* @brief 创建共享内存。
* @param teedev TEE 设备指针
* @param size 大小
* @param flags 标志
* @return 共享内存指针,失败返回 ERR_PTR
*/
struct tee_shm *tee_shm_alloc(struct tee_device *teedev, size_t size, u32 flags)
{
struct tee_shm *shm;
int ret;
// 1. 分配共享内存结构
shm = kzalloc(sizeof(*shm), GFP_KERNEL);
if (!shm) {
return ERR_PTR(-ENOMEM);
}
shm->teedev = teedev;
shm->size = size;
shm->flags = flags;
kref_init(&shm->ref);
spin_lock_init(&shm->lock);
INIT_LIST_HEAD(&shm->list);
// 2. 分配物理页面
shm->pages = kcalloc(size >> PAGE_SHIFT, sizeof(struct page *), GFP_KERNEL);
if (!shm->pages) {
kfree(shm);
return ERR_PTR(-ENOMEM);
}
for (int i = 0; i < (size >> PAGE_SHIFT); i++) {
shm->pages[i] = alloc_page(GFP_KERNEL);
if (!shm->pages[i]) {
ret = -ENOMEM;
goto error;
}
}
// 3. 获取 DMA 地址
shm->dma_addr = dma_map_page(teedev->dev, shm->pages[0], 0, size, DMA_BIDIRECTIONAL);
if (dma_mapping_error(teedev->dev, shm->dma_addr)) {
ret = -ENOMEM;
goto error;
}
// 4. 获取内核地址
shm->kaddr = page_address(shm->pages[0]);
return shm;
error:
for (int i = 0; i < (size >> PAGE_SHIFT); i++) {
if (shm->pages[i]) {
__free_page(shm->pages[i]);
}
}
kfree(shm->pages);
kfree(shm);
return ERR_PTR(ret);
}
/**
* @brief 释放共享内存。
* @param shm 共享内存指针
*/
void tee_shm_free(struct tee_shm *shm)
{
if (!shm) {
return;
}
// 1. 取消 DMA 映射
dma_unmap_page(shm->teedev->dev, shm->dma_addr, shm->size, DMA_BIDIRECTIONAL);
// 2. 释放物理页面
for (int i = 0; i < (shm->size >> PAGE_SHIFT); i++) {
if (shm->pages[i]) {
__free_page(shm->pages[i]);
}
}
kfree(shm->pages);
// 3. 释放结构
kfree(shm);
}
4.3.4 TEE 会话管理 (代码出处: drivers/tee/tee_core.c)
// 代码出处: drivers/tee/tee_core.c
/**
* @brief 创建 TEE 会话。
* @param ctx TEE 上下文
* @param ta_uuid 可信应用 UUID
* @return 会话 ID,负数错误
*/
int tee_session_create(struct tee_context *ctx, const u8 *ta_uuid)
{
struct optee_session *session;
struct optee *optee = ctx->teedev->priv;
int ret;
// 1. 分配会话结构
session = kzalloc(sizeof(*session), GFP_KERNEL);
if (!session) {
return -ENOMEM;
}
session->ctx = ctx;
session->client_id = ctx->client_id;
session->session_id = ++ctx->session_counter;
INIT_LIST_HEAD(&session->list);
// 2. 打开 TEE 会话
ret = optee_handle_cmd(optee, &(struct optee_cmd){
.cmd = TEE_CMD_OPEN_SESSION,
.session = session,
});
if (ret < 0) {
kfree(session);
return ret;
}
// 3. 添加到会话列表
spin_lock(&optee->lock);
list_add_tail(&session->list, &optee->teedev_list);
spin_unlock(&optee->lock);
return session->session_id;
}
/**
* @brief 关闭 TEE 会话。
* @param ctx TEE 上下文
* @param session_id 会话 ID
* @return 0 成功,负数错误
*/
int tee_session_close(struct tee_context *ctx, u32 session_id)
{
struct optee *optee = ctx->teedev->priv;
struct optee_session *session;
int ret = -ENOENT;
// 1. 查找会话
spin_lock(&optee->lock);
list_for_each_entry(session, &optee->teedev_list, list) {
if (session->session_id == session_id) {
list_del(&session->list);
spin_unlock(&optee->lock);
// 2. 关闭会话
ret = optee_handle_cmd(optee, &(struct optee_cmd){
.cmd = TEE_CMD_CLOSE_SESSION,
.session = session,
});
kfree(session);
return ret;
}
}
spin_unlock(&optee->lock);
return ret;
}
4.3.5 可信应用调用 (代码出处: drivers/tee/tee_core.c)
// 代码出处: drivers/tee/tee_core.c
/**
* @brief 调用可信应用。
* @param ctx TEE 上下文
* @param session_id 会话 ID
* @param cmd_id 命令 ID
* @param params 参数列表
* @param num_params 参数数量
* @return 0 成功,负数错误
*/
int tee_invoke_command(struct tee_context *ctx, u32 session_id,
u32 cmd_id, struct tee_param *params, u32 num_params)
{
struct optee *optee = ctx->teedev->priv;
struct optee_session *session;
struct optee_cmd cmd;
int ret = -ENOENT;
// 1. 查找会话
spin_lock(&optee->lock);
list_for_each_entry(session, &optee->teedev_list, list) {
if (session->session_id == session_id) {
spin_unlock(&optee->lock);
// 2. 构建命令
cmd.cmd = TEE_CMD_INVOKE_COMMAND;
cmd.session = session;
cmd.params = params;
cmd.num_params = num_params;
cmd.shm = params[0].shm;
cmd.done = &ctx->completion;
// 3. 执行命令
ret = optee_handle_cmd(optee, &cmd);
if (ret < 0) {
return ret;
}
// 4. 等待完成
if (cmd.done) {
wait_for_completion(cmd.done);
}
return ret;
}
}
spin_unlock(&optee->lock);
return ret;
}
4.3.6 用户空间接口 (代码出处: drivers/tee/tee_core.c)
// 代码出处: drivers/tee/tee_core.c
/**
* @brief TEE 设备文件操作。
*/
static const struct file_operations tee_fops = {
.owner = THIS_MODULE,
.open = tee_open,
.release = tee_release,
.unlocked_ioctl = tee_ioctl,
.compat_ioctl = tee_ioctl,
.mmap = tee_mmap,
};
/**
* @brief TEE ioctl 处理。
* @param file 文件指针
* @param cmd ioctl 命令
* @param arg 参数
* @return 0 成功,负数错误
*/
static long tee_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
struct tee_context *ctx = file->private_data;
struct optee *optee = ctx->teedev->priv;
long ret = 0;
switch (cmd) {
case TEE_IOC_OPEN_SESSION:
ret = tee_ioctl_open_session(ctx, arg);
break;
case TEE_IOC_CLOSE_SESSION:
ret = tee_ioctl_close_session(ctx, arg);
break;
case TEE_IOC_INVOKE_COMMAND:
ret = tee_ioctl_invoke_command(ctx, arg);
break;
case TEE_IOC_SHM_ALLOC:
ret = tee_ioctl_shm_alloc(ctx, arg);
break;
case TEE_IOC_SHM_FREE:
ret = tee_ioctl_shm_free(ctx, arg);
break;
default:
ret = -ENOTTY;
break;
}
return ret;
}
4.4 软件设计模式树形分析
TrustZone/TEE 设计模式 ├── 工厂模式 (Factory Pattern) │ ├── tee_device_alloc():创建 TEE 设备 │ └── tee_shm_alloc():创建共享内存 ├── 适配器模式 (Adapter Pattern) │ ├── optee_smc_call():适配 SMC 调用 │ └── tee_ioctl():适配 ioctl 命令 ├── 策略模式 (Strategy Pattern) │ ├── optee_handle_cmd():命令处理策略 │ └── tee_shm_alloc():共享内存分配策略 ├── 观察者模式 (Observer Pattern) │ ├── tee_invoke_command():观察命令完成事件 │ └── optee_probe():观察设备探测事件 ├── 状态模式 (State Pattern) │ └── 会话状态 (OPEN/CLOSED/INVOKING) └── 模板方法模式 (Template Method Pattern) └── tee_invoke_command():定义了 TEE 命令调用的标准流程
4.5 TrustZone 调试核心难点
4.5.1 SMC 调用失败
现象:optee_smc_call 返回 SMC_ERROR,TEE 操作失败。
原因:
-
SMC 参数错误。
-
安全世界未正常启动。
-
权限不足。
调试方法:
-
使用
trace-cmd record -e smc:*跟踪 SMC 调用。 -
检查
dmesg | grep tee查看 TEE 日志。 -
使用
xtest测试 TEE 功能。
4.5.2 共享内存分配失败
现象:tee_shm_alloc 返回 -ENOMEM,无法分配共享内存。
原因:
-
内存池耗尽。
-
页面分配失败。
-
DMA 映射失败。
调试方法:
-
检查
tee_shm_pool配置。 -
使用
cat /sys/kernel/debug/tee/shm查看共享内存统计。 -
检查 DMA 配置。
4.5.3 TEE 会话无法创建
现象:tee_session_create 返回 -EIO,无法创建 TEE 会话。
原因:
-
可信应用未加载。
-
会话 ID 冲突。
-
OP-TEE 内核未响应。
调试方法:
-
检查
tee_session日志。 -
使用
cat /proc/sys/tee/ta_ids查看已加载 TA。 -
重启 TEE 设备:
rmmod optee; modprobe optee。
4.6 与其他模块的协同
| 模块 | 协同方式 | 调试关键点 |
|---|---|---|
| SMC 调用 | 通过 SMC 与安全世界通信 | 参数传递、结果返回 |
| 共享内存 | TEE 与非安全世界交换数据 | 页面分配、DMA 映射 |
| 设备驱动 | 提供 TEE 设备接口 | 设备节点、ioctl |
| 进程管理 | 管理 TEE 会话 | 会话创建、关闭 |
| 内存管理 | 管理共享内存 | 页面池、DMA 地址 |
| 用户空间 | 通过 TEE 客户端 API 访问 | 信任应用、命令调用 |
第五部分 内核调试核心工具链
5.1 调试工具链全景
Linux 内核调试是系统开发中最具挑战性的任务之一。Ftrace、Perf、eBPF 和 Systrace 是四款最强大的内核调试工具,它们从不同维度提供了对内核行为的深入洞察。
5.1.1 调试工具对比
| 工具 | 核心功能 | 适用场景 | 性能开销 | 学习曲线 |
|---|---|---|---|---|
| Ftrace | 函数追踪、事件追踪、延迟分析 | 内核函数调用、中断延迟、调度延迟 | 低 | 中等 |
| Perf | 硬件性能计数器、采样分析、统计分析 | CPU 性能分析、缓存 miss 分析 | 低-中 | 较高 |
| eBPF | 动态追踪、过滤、统计、安全监控 | 自定义追踪、实时监控、安全策略 | 低 | 高 |
| Systrace | 系统级事件追踪、UI 性能分析 | Android 系统性能、UI 流畅度 | 低 | 中等 |
5.1.2 工具链架构
[Ftrace] ├── /sys/kernel/debug/tracing/ │ ├── trace → 追踪输出 │ ├── trace_marker → 用户空间标记 │ ├── current_tracer → 选择追踪器 │ └── trace_options → 追踪选项 ↓ [Perf] ├── perf record → 采样记录 ├── perf report → 报告生成 ├── perf stat → 性能统计 └── perf probe → 动态探测 ↓ [eBPF] ├── bpf_prog_load() → 加载 BPF 程序 ├── bpf_map_create() → 创建 BPF 映射 ├── bpf_attach_kprobe() → 挂载 kprobe └── bpf_trace_printk() → 打印追踪信息 ↓ [Systrace] ├── atrace → 抓取 Android 追踪 ├── systrace.py → 生成 HTML 报告 └── perfetto → 新一代追踪工具
5.2 核心代码实现
5.2.1 Ftrace 基本使用 (代码出处: kernel/trace/)
# 代码出处: kernel/trace/ # 1. 启用函数追踪 echo function > /sys/kernel/debug/tracing/current_tracer # 2. 设置追踪函数过滤 (只追踪特定函数) echo "__do_kern_addr" > /sys/kernel/debug/tracing/set_ftrace_filter # 3. 开始追踪 echo 1 > /sys/kernel/debug/tracing/tracing_on # 4. 执行测试操作 # ... 触发追踪事件 ... # 5. 停止追踪 echo 0 > /sys/kernel/debug/tracing/tracing_on # 6. 查看追踪结果 cat /sys/kernel/debug/tracing/trace
5.2.2 Perf 基本使用 (代码出处: tools/perf/)
# 代码出处: tools/perf/ # 1. 记录 CPU 性能事件 perf record -e cycles -e instructions -e cache-misses \ -p $(pgrep audioserver) -- sleep 5 # 2. 生成报告 perf report --stdio --sort=symbol # 3. 实时查看性能统计 perf stat -e cycles,instructions,branch-misses \ -p $(pgrep audioserver) -- sleep 2 # 4. 动态探测函数 perf probe --add 'do_sys_open filename=+0(%di):string' # 5. 追踪函数调用 perf record -e probe:do_sys_open -a -- sleep 5
5.2.3 eBPF 程序示例 (代码出处: samples/bpf/)
// 代码出处: samples/bpf/trace_event_kern.c
/**
* @brief eBPF 程序:跟踪 open 系统调用。
*/
SEC("kprobe/sys_open")
int bpf_trace_open(struct pt_regs *ctx)
{
char filename[256];
u32 pid = bpf_get_current_pid_tgid() >> 32;
u32 uid = bpf_get_current_uid_gid() & 0xFFFFFFFF;
// 1. 获取文件名
bpf_probe_read_user_str(filename, sizeof(filename),
(void *)ctx->di);
// 2. 输出追踪信息
bpf_trace_printk("open: pid=%d uid=%d file=%s\n", pid, uid, filename);
return 0;
}
/**
* @brief eBPF 程序:统计系统调用次数。
*/
SEC("tracepoint/syscalls/sys_enter_openat")
int bpf_trace_sys_openat(struct trace_event_raw_sys_enter *ctx)
{
u64 key = ctx->id;
u64 *value;
// 1. 查找统计映射
value = bpf_map_lookup_elem(&syscall_map, &key);
if (!value) {
// 首次调用,初始化计数
u64 init_val = 1;
bpf_map_update_elem(&syscall_map, &key, &init_val, BPF_ANY);
return 0;
}
// 2. 更新计数
(*value)++;
bpf_map_update_elem(&syscall_map, &key, value, BPF_ANY);
return 0;
}
// 定义 BPF 映射
struct bpf_map_def SEC("maps") syscall_map = {
.type = BPF_MAP_TYPE_HASH,
.key_size = sizeof(u64),
.value_size = sizeof(u64),
.max_entries = 1024,
};
char _license[] SEC("license") = "GPL";
5.2.4 eBPF 程序加载与运行 (代码出处: samples/bpf/trace_event_user.c)
// 代码出处: samples/bpf/trace_event_user.c
/**
* @brief 加载并运行 BPF 程序。
* @return 0 成功,负数错误
*/
static int bpf_trace_run(void)
{
struct bpf_object *obj;
struct bpf_program *prog;
struct bpf_map *map;
u64 key, value;
int prog_fd, map_fd, ret;
// 1. 加载 BPF 目标文件
obj = bpf_object__open_file("trace_event_kern.o", NULL);
if (IS_ERR(obj)) {
return PTR_ERR(obj);
}
// 2. 加载 BPF 程序
ret = bpf_object__load(obj);
if (ret < 0) {
bpf_object__close(obj);
return ret;
}
// 3. 获取程序文件描述符
prog = bpf_object__find_program_by_name(obj, "bpf_trace_open");
if (!prog) {
ret = -ENOENT;
goto out;
}
prog_fd = bpf_program__fd(prog);
// 4. 获取映射文件描述符
map = bpf_object__find_map_by_name(obj, "syscall_map");
if (!map) {
ret = -ENOENT;
goto out;
}
map_fd = bpf_map__fd(map);
// 5. 挂载 kprobe
ret = bpf_prog_attach(prog_fd, get_kprobe_events(), 0);
if (ret < 0) {
goto out;
}
// 6. 读取统计信息
printf("System call statistics:\n");
for (int i = 0; i < 1024; i++) {
key = i;
if (bpf_map_lookup_elem(map_fd, &key, &value) == 0) {
printf(" syscall %llu: %llu\n", key, value);
}
}
out:
bpf_object__close(obj);
return ret;
}
5.2.5 Systrace 使用示例 (代码出处: tools/atrace/)
# 代码出处: tools/atrace/ # 1. 启用 Systrace (Android 8.0+) adb shell atrace --async-start -t 5 -b 40960 \ gfx input view wm am binder kernel sched freq # 2. 等待 5 秒 # 3. 停止并获取追踪数据 adb shell atrace --async-stop adb pull /sys/kernel/debug/tracing/trace # 4. 生成 HTML 报告 python systrace.py --from-file trace # 5. 使用 Perfetto (新一代工具) adb shell perfetto -c -f -t 5 \ -o /data/misc/perfetto/trace \ --txt -s power/regulators adb pull /data/misc/perfetto/trace
5.2.6 Ftrace 高级使用 (代码出处: kernel/trace/trace.c)
// 代码出处: kernel/trace/trace.c
/**
* @brief 设置 Ftrace 追踪过滤器。
* @param tr 追踪器指针
* @param filter_str 过滤器字符串
* @return 0 成功,负数错误
*/
static int trace_set_filter(struct trace_ops *tr, const char *filter_str)
{
struct trace_filter *filter;
struct trace_filter_ops *filter_ops;
int ret;
// 1. 分配过滤器
filter = kmalloc(sizeof(*filter), GFP_KERNEL);
if (!filter) {
return -ENOMEM;
}
// 2. 解析过滤器
ret = trace_filter_parse(filter, filter_str);
if (ret < 0) {
kfree(filter);
return ret;
}
// 3. 设置过滤器
filter_ops = tr->filter_ops;
ret = filter_ops->set_filter(tr, filter);
if (ret < 0) {
kfree(filter);
return ret;
}
return 0;
}
/**
* @brief 用户空间标记事件。
* @param fmt 格式字符串
* @return 0 成功,负数错误
*/
int trace_mark(const char *fmt, ...)
{
va_list args;
char buf[256];
int len;
int ret;
// 1. 格式化字符串
va_start(args, fmt);
len = vsnprintf(buf, sizeof(buf), fmt, args);
va_end(args);
// 2. 写入 trace_marker
ret = __trace_puts(buf, len);
if (ret < 0) {
return ret;
}
return 0;
}
EXPORT_SYMBOL(trace_mark);
5.3 软件设计模式树形分析
调试工具链设计模式 ├── 工厂模式 (Factory Pattern) │ ├── bpf_object__open_file():创建 BPF 对象 │ └── trace_set_filter():创建追踪过滤器 ├── 适配器模式 (Adapter Pattern) │ ├── bpf_prog_attach():适配 BPF 程序到 kprobe │ └── perf_event_open():适配 perf 事件 ├── 观察者模式 (Observer Pattern) │ ├── bpf_trace_open():观察 open 系统调用 │ └── perf_event_reader():观察 perf 事件 ├── 策略模式 (Strategy Pattern) │ ├── ftrace_ops:追踪策略 │ └── bpf_map_lookup_elem():映射查找策略 └── 模板方法模式 (Template Method Pattern) └── bpf_trace_run():定义了 BPF 程序加载的标准流程
5.4 调试工具链调试核心难点
5.4.1 Ftrace 输出过多
现象:cat /sys/kernel/debug/tracing/trace 输出过多信息。
原因:
-
未设置过滤器。
-
追踪函数过多。
-
追踪持续时间过长。
调试方法:
-
设置过滤器:
echo "do_sys_open" > set_ftrace_filter。 -
限制追踪时间:
echo 1 > tracing_on; sleep 1; echo 0 > tracing_on。 -
使用
set_ftrace_notrace排除不需要的函数。
5.4.2 eBPF 验证失败
现象:加载 eBPF 程序时返回 -EINVAL。
原因:
-
程序违反 eBPF 验证器规则。
-
循环或分支过多。
-
内存访问越界。
调试方法:
-
使用
bpf_object__load的日志输出。 -
简化程序逻辑。
-
使用
bpf_get_stack等安全函数。
5.4.3 Perf 采样过高
现象:perf record 导致系统负载飙升。
原因:
-
采样频率过高。
-
追踪事件过多。
-
缓冲区太小。
调试方法:
-
降低采样频率:
perf record -F 100。 -
减少追踪事件。
-
增大缓冲区:
perf record -m 64。
5.5 与其他模块的协同
| 模块 | 协同方式 | 调试关键点 |
|---|---|---|
| 内核函数 | 通过 ftrace 追踪函数调用 | 函数入口/出口、调用栈 |
| 硬件性能 | 通过 perf 监控硬件事件 | 缓存 miss、分支预测 |
| 系统调用 | 通过 eBPF 追踪系统调用 | 系统调用参数、返回值 |
| 进程管理 | 追踪进程调度事件 | 调度延迟、负载均衡 |
| 内存管理 | 追踪内存分配事件 | 内存泄漏、页面分配 |
| 设备驱动 | 通过 kprobe 跟踪驱动函数 | 设备操作、中断处理 |
第六部分 内核崩溃分析
6.1 内核崩溃核心概念
内核崩溃 (Kernel Panic) 是 Linux 内核遇到无法恢复的错误时触发的紧急状态。当内核崩溃时,系统会停止所有活动并打印诊断信息。对于 ARM64 平台和 Android 系统,正确分析和定位内核崩溃是 BSP 开发中最关键的技能之一。
6.1.1 Oops 与 Panic 的对比
| 特性 | Oops | Panic |
|---|---|---|
| 严重程度 | 可恢复 (但可能不稳定) | 不可恢复 |
| 触发条件 | 非法内存访问、空指针引用 | 严重错误、内核死锁 |
| 系统状态 | 可能继续运行 | 立即停止 |
| 日志输出 | 部分寄存器/栈信息 | 完整寄存器/栈信息 |
| 调试难度 | 中等 | 较高 |
| 处理方式 | 可加载模块卸载 | 需要系统重启 |
6.1.2 崩溃信息结构
[Kernel Panic 日志] ├── [CPU 状态] │ ├── 异常类型 (Synchronous/Asynchronous) │ ├── 异常级别 (EL0/EL1/EL2/EL3) │ ├── 异常地址 (Fault Address) │ └── 异常指令 (Instruction Fault) ├── [寄存器状态] │ ├── 通用寄存器 (X0-X30) │ ├── 栈指针 (SP) │ ├── 程序计数器 (PC) │ └── 状态寄存器 (PSTATE) ├── [栈回溯] │ ├── 栈帧 (Stack Frame) │ ├── 函数调用链 (Call Chain) │ └── 符号解析 (Symbol Resolution) └── [内存状态] ├── 页面映射 (Page Mapping) ├── SLAB 缓存信息 └── 内存统计
6.2 核心数据结构
6.2.1 崩溃记录结构 (代码出处: kernel/panic.c)
// 代码出处: kernel/panic.c
/**
* @struct panic_info
* @brief 内核崩溃信息结构。
*/
struct panic_info {
unsigned long panic_flags; /**< 崩溃标志 */
unsigned long panic_cpu; /**< 发生崩溃的 CPU */
unsigned long panic_time; /**< 崩溃时间 (秒) */
const char *panic_message; /**< 崩溃消息 */
void *caller; /**< 调用者地址 */
struct pt_regs *regs; /**< 寄存器状态 */
struct task_struct *task; /**< 当前任务 */
struct list_head *stack_trace; /**< 栈回溯列表 */
struct list_head *mm; /**< 内存描述符 */
struct list_head *regions; /**< 内存区域 */
};
/**
* @struct crash_info
* @brief 崩溃信息结构。
*/
struct crash_info {
struct panic_info panic; /**< 崩溃信息 */
struct timeval timestamp; /**< 时间戳 */
char module_name[64]; /**< 模块名称 */
char function_name[64]; /**< 函数名称 */
unsigned long instruction_pointer; /**< 指令指针 */
unsigned long stack_pointer; /**< 栈指针 */
unsigned long link_register; /**< 链接寄存器 */
unsigned long fault_address; /**< 错误地址 */
unsigned long error_code; /**< 错误码 */
struct list_head *stack_frames; /**< 栈帧列表 */
struct list_head *symbols; /**< 符号列表 */
};
6.3 核心代码实现
6.3.1 崩溃触发与处理 (代码出处: kernel/panic.c)
// 代码出处: kernel/panic.c
/**
* @brief 触发内核崩溃。
* @param fmt 格式字符串
* @param ... 参数
*/
void panic(const char *fmt, ...)
{
static char buf[1024];
va_list args;
int ret;
// 1. 格式化消息
va_start(args, fmt);
ret = vsnprintf(buf, sizeof(buf), fmt, args);
va_end(args);
// 2. 打印崩溃信息
pr_emerg("Kernel panic - not syncing: %s\n", buf);
// 3. 禁止抢占
preempt_disable();
// 4. 记录崩溃信息
panic_record(&panic_info);
// 5. 触发 kdump
crash_kexec(NULL);
// 6. 进入无限循环
while (1) {
// 等待重启
if (panic_timeout > 0) {
schedule_timeout(panic_timeout * HZ);
// 触发重启
restart_handler();
}
cpu_relax();
}
}
EXPORT_SYMBOL(panic);
/**
* @brief 处理 Oops。
* @param regs 寄存器状态
* @param error_code 错误码
* @param address 错误地址
*/
void __weak show_stack(struct task_struct *task, struct list_head *stack)
{
struct stack_frame *frame;
unsigned long pc, lr;
struct list_head *stack_list;
// 1. 检查是否有栈
if (!task) {
task = current;
}
// 2. 获取栈指针
stack_list = task->stack;
if (!stack_list) {
return;
}
// 3. 遍历栈帧
list_for_each_entry(frame, stack_list, list) {
pc = frame->pc;
lr = frame->lr;
// 4. 打印栈帧信息
pr_emerg(" %*pS\n", 12, (void *)pc);
if (frame->lr != 0) {
pr_emerg(" LR = %pS\n", (void *)lr);
}
// 5. 检查是否到达栈顶
if (frame->prev == NULL) {
break;
}
}
}
6.3.2 Kdump 配置与使用 (代码出处: arch/arm64/kernel/crash_dump.c)
// 代码出处: arch/arm64/kernel/crash_dump.c
/**
* @brief 初始化 kdump。
* @return 0 成功,负数错误
*/
static int crash_dump_init(void)
{
struct crash_info *info;
int ret;
// 1. 分配崩溃信息结构
info = kzalloc(sizeof(*info), GFP_KERNEL);
if (!info) {
return -ENOMEM;
}
// 2. 初始化崩溃信息
info->panic.panic_flags = 0;
info->panic.panic_cpu = 0;
info->panic.panic_time = 0;
info->panic.panic_message = NULL;
info->panic.caller = NULL;
info->panic.regs = NULL;
info->panic.task = NULL;
info->panic.stack_trace = NULL;
info->panic.mm = NULL;
info->panic.regions = NULL;
// 3. 注册崩溃钩子
ret = register_crash_handler(crash_handler, info);
if (ret < 0) {
kfree(info);
return ret;
}
// 4. 创建崩溃文件
crash_file = debugfs_create_file("crash", 0444, NULL, info, &crash_fops);
return 0;
}
/**
* @brief 内核崩溃处理函数。
* @param info 崩溃信息
* @param regs 寄存器状态
* @param error_code 错误码
* @param address 错误地址
*/
static void crash_handler(struct crash_info *info, struct pt_regs *regs,
unsigned long error_code, unsigned long address)
{
// 1. 保存寄存器
info->regs = regs;
info->error_code = error_code;
info->fault_address = address;
// 2. 保存当前任务
info->task = current;
// 3. 保存栈回溯
info->stack_frames = get_stack_frames(regs);
// 4. 保存内存信息
info->mm = current->mm;
info->regions = get_memory_regions();
// 5. 触发 kdump
crash_kexec(NULL);
}
/**
* @brief 触发 kdump。
* @param image 内核镜像指针
* @return 0 成功,负数错误
*/
int crash_kexec(struct kimage *image)
{
struct kexec_control_block *cb;
int ret;
// 1. 获取控制块
cb = kexec_get_control_block();
if (!cb) {
return -ENOMEM;
}
// 2. 设置崩溃信息
cb->info = &panic_info;
// 3. 启动 kdump
ret = kexec_load(image, 0, 0);
if (ret < 0) {
return ret;
}
// 4. 执行 kdump
ret = kexec_start(image);
if (ret < 0) {
return ret;
}
return 0;
}
6.3.3 Crash 工具基本使用
# 1. 安装 crash 工具 sudo apt-get install crash # 2. 获取 vmcore # 启用 kdump: 修改 /etc/default/grub # GRUB_CMDLINE_LINUX="crashkernel=512M" # update-grub # 3. 触发崩溃 (测试) # echo c > /proc/sysrq-trigger # 4. 分析 vmcore crash /usr/lib/debug/vmlinux /var/crash/2025-01-01/vmcore # 5. Crash 常用命令 crash> bt # 栈回溯 crash> log # 查看内核日志 crash> ps # 查看进程列表 crash> task # 查看任务结构 crash> kmem # 内存统计 crash> files # 查看打开的文件
6.3.4 Crash 工具高级分析
# 1. 定位崩溃点 crash> bt PID: 1234 TASK: ffff800012345678 CPU: 0 COMMAND: "app_process" #0 [ffff800012345678] do_sys_open+0x14/0x20 #1 [ffff800012345678] sys_open+0x18/0x28 #2 [ffff800012345678] el0_svc_naked+0x38/0x50 # 2. 检查寄存器 crash> regs x0: 0x0000000000000001 x1: 0xffff800012345678 x2: 0x0000000000000000 x3: 0x0000000000000000 x4: 0x0000000000000000 x5: 0x0000000000000000 x6: 0x0000000000000000 x7: 0x0000000000000000 x8: 0xffff800012345678 x9: 0x0000000000000000 x10: 0x0000000000000000 x11: 0x0000000000000000 x12: 0x0000000000000000 x13: 0x0000000000000000 x14: 0x0000000000000000 x15: 0x0000000000000000 x16: 0x0000000000000000 x17: 0x0000000000000000 x18: 0x0000000000000000 x19: 0xffff800012345678 x20: 0x0000000000000000 x21: 0x0000000000000000 x22: 0x0000000000000000 x23: 0x0000000000000000 x24: 0x0000000000000000 x25: 0x0000000000000000 x26: 0x0000000000000000 x27: 0x0000000000000000 x28: 0x0000000000000000 x29: 0x0000000000000000 x30: 0x0000000000000000 sp: 0xffff800012345678 pc: 0xffff800012345678 # 3. 分析内存 crash> kmem -i total used free shared buffers cached Mem: 2048M 1024M 1024M 512M 256M 512M -/+ buffers/cache: 256M 1792M Swap: 0M 0M 0M # 4. 查看堆栈 crash> stack SP: 0xffff800012345678 PC: 0xffff800012345678 #0: 0xffff800012345678 #1: 0xffff800012345678 #2: 0xffff800012345678 # 5. 分析某个进程 crash> task 1234 PID: 1234 TASK: ffff800012345678 CPU: 0 COMMAND: "app_process" STATE: RUNNING PPID: 123 PRI: 120 VM: 0xffff800012345678 MM: 0xffff800012345678 FS: 0xffff800012345678
6.3.5 崩溃日志分析示例
# 1. 获取崩溃日志 dmesg | grep "Kernel panic" [ 1234.567890] Kernel panic - not syncing: Fatal exception [ 1234.567890] CPU: 0 PID: 1234 Comm: app_process Not tainted 5.10.0-rc1+ [ 1234.567890] Hardware name: Rockchip RK3399 (DT) [ 1234.567890] pc : do_sys_open+0x14/0x20 [ 1234.567890] lr : sys_open+0x18/0x28 [ 1234.567890] sp : ffff800012345678 [ 1234.567890] x29: ffff800012345678 x28: ffff800012345678 [ 1234.567890] x27: ffff800012345678 x26: ffff800012345678 [ 1234.567890] x25: ffff800012345678 x24: ffff800012345678 [ 1234.567890] x23: ffff800012345678 x22: ffff800012345678 [ 1234.567890] x21: ffff800012345678 x20: ffff800012345678 [ 1234.567890] x19: ffff800012345678 x18: ffff800012345678 [ 1234.567890] x17: ffff800012345678 x16: ffff800012345678 [ 1234.567890] x15: ffff800012345678 x14: ffff800012345678 [ 1234.567890] x13: ffff800012345678 x12: ffff800012345678 [ 1234.567890] x11: ffff800012345678 x10: ffff800012345678 [ 1234.567890] x9 : ffff800012345678 x8 : ffff800012345678 [ 1234.567890] x7 : ffff800012345678 x6 : ffff800012345678 [ 1234.567890] x5 : ffff800012345678 x4 : ffff800012345678 [ 1234.567890] x3 : ffff800012345678 x2 : ffff800012345678 [ 1234.567890] x1 : ffff800012345678 x0 : ffff800012345678 # 2. 分析栈回溯 [ 1234.567890] Call trace: [ 1234.567890] do_sys_open+0x14/0x20 [ 1234.567890] sys_open+0x18/0x28 [ 1234.567890] el0_svc_naked+0x38/0x50 [ 1234.567890] el0_svc_handler+0x30/0x40 [ 1234.567890] el0_svc_common+0x18/0x20 # 3. 分析错误地址 [ 1234.567890] fault_address: 0xffff800012345678 [ 1234.567890] error_code: 0x1 (Translation fault) # 4. 分析内存信息 [ 1234.567890] Page: 0xffff800012345678 [ 1234.567890] PTE: 0xffff800012345678 [ 1234.567890] Phys: 0xffff800012345678 # 5. 定位问题 # 错误地址 0xffff800012345678 在内核态,指向一个无效的内存区域 # 翻译错误 (Translation fault) 表示页表无法解析该地址 # 需要检查 do_sys_open 函数的参数,是否传递了无效的文件名指针
6.4 软件设计模式树形分析
内核崩溃分析设计模式 ├── 观察者模式 (Observer Pattern) │ ├── panic():观察系统崩溃事件 │ └── crash_handler():观察崩溃信号 ├── 工厂模式 (Factory Pattern) │ ├── crash_dump_init():创建 kdump 实例 │ └── crash_info_alloc():创建崩溃信息结构 ├── 策略模式 (Strategy Pattern) │ ├── crash_kexec():崩溃处理策略 │ └── show_stack():栈回溯策略 ├── 适配器模式 (Adapter Pattern) │ ├── crash_file_fops:适配 debugfs 接口 │ └── crash_vmcore_ops:适配 vmcore 接口 └── 模板方法模式 (Template Method Pattern) └── panic():定义了内核崩溃的标准处理流程
6.5 内核崩溃调试核心难点
6.5.1 空指针引用
现象:内核崩溃日志显示 Translation fault,pc 指向某个函数,lr 指向上层调用。
原因:
-
对空指针进行解引用。
-
释放后使用。
-
指针未初始化。
调试方法:
-
检查
x0寄存器的值。 -
使用
crash查看调用栈。 -
查看
do_sys_open函数参数。
6.5.2 栈溢出
现象:内核崩溃日志显示 stack overflow,sp 指向栈底。
原因:
-
递归调用过深。
-
栈缓冲区过大。
-
栈大小配置不足。
调试方法:
-
检查栈指针。
-
查看栈帧数量。
-
增加栈大小。
6.5.3 内存踩踏
现象:内核崩溃随机发生,错误地址不稳定。
原因:
-
越界写。
-
缓冲区溢出。
-
内存破坏。
调试方法:
-
使用
kmemleak检测内存泄漏。 -
使用
slub_debug检测内存破坏。 -
使用
kasan动态检测。
6.6 与其他模块的协同
| 模块 | 协同方式 | 调试关键点 |
|---|---|---|
| 内存管理 | 提供内存信息 | 页面状态、SLAB 缓存 |
| 进程管理 | 提供任务信息 | 进程状态、栈信息 |
| 调度器 | 提供调度信息 | 任务上下文、CPU 状态 |
| 中断控制器 | 提供中断信息 | 中断号、中断状态 |
| 设备驱动 | 提供设备信息 | 设备状态、寄存器信息 |
第七部分 汇编/反汇编基础
7.1 ARM64 汇编基础
ARM64 (AArch64) 是 ARM 架构的 64 位指令集,广泛应用于移动设备、服务器和嵌入式系统。理解 ARM64 汇编是定位内核崩溃、分析性能瓶颈和进行底层调试的关键技能。
7.1.1 ARM64 寄存器
| 寄存器 | 用途 | 调用约定 | 保留 |
|---|---|---|---|
| X0-X7 | 参数传递、返回值 | 参数寄存器 | 否 |
| X8 | 间接结果位置 | 返回地址 | 否 |
| X9-X15 | 临时变量 | 调用者保存 | 否 |
| X16-X17 | 临时变量 (IP0/IP1) | 调用者保存 | 否 |
| X18 | 平台保留 | 平台保留 | 是 |
| X19-X28 | 临时变量 | 被调用者保存 | 是 |
| X29 | 帧指针 (FP) | 被调用者保存 | 是 |
| X30 | 链接寄存器 (LR) | 被调用者保存 | 是 |
| SP | 栈指针 | 被调用者保存 | 是 |
| PC | 程序计数器 | 无 | 无 |
| PSTATE | 处理器状态 | 无 | 无 |
7.1.2 ARM64 指令格式
| 指令类型 | 格式 | 示例 |
|---|---|---|
| 数据处理 | op dst, src1, src2 |
add x0, x1, x2 |
| 加载/存储 | op dst, [base, offset] |
ldr x0, [x1, #8] |
| 跳转 | op target |
bl do_sys_open |
| 比较 | op src1, src2 |
cmp x0, x1 |
| 条件跳转 | op target |
b.eq 0xffff800012345678 |
7.1.3 ARM64 函数调用约定
[函数调用流程] ↓ 1. 保存 LR 到栈 (如果调用其他函数) 2. 保存 FP 到栈 3. 分配栈空间 (sp -= size) 4. 将参数从 X0-X7 移动到局部变量 5. 执行函数体 6. 将返回值放在 X0 7. 恢复栈空间 (sp += size) 8. 恢复 FP 9. 跳转到 LR (ret)
7.2 反汇编基础
7.2.1 反汇编工具
| 工具 | 用途 | 命令示例 |
|---|---|---|
| objdump | 反汇编二进制文件 | objdump -d vmlinux |
| gdb | 动态反汇编 | gdb vmlinux; disassemble do_sys_open |
| perf | 实时反汇编 | perf record -e cycles -a -- sleep 5; perf report |
| crash | 内核崩溃反汇编 | crash vmlinux vmcore; disassemble 0xffff800012345678 |
| llvm-objdump | LLVM 反汇编器 | llvm-objdump -d vmlinux |
7.2.2 栈帧分析
// 代码出处: arch/arm64/kernel/process.c
/**
* @brief 栈帧结构。
*/
struct stack_frame {
struct stack_frame *prev; /**< 上一个栈帧 */
unsigned long lr; /**< 链接寄存器 */
unsigned long pc; /**< 程序计数器 */
unsigned long fp; /**< 帧指针 */
unsigned long sp; /**< 栈指针 */
};
/**
* @brief 打印栈回溯。
* @param regs 寄存器状态
* @param task 任务指针
*/
void show_stack(struct task_struct *task, unsigned long *sp)
{
struct stack_frame *frame;
unsigned long pc, lr;
int i = 0;
// 1. 检查是否有栈
if (!task) {
task = current;
}
if (!sp) {
sp = task->stack;
}
// 2. 遍历栈帧
while (sp) {
frame = (struct stack_frame *)sp;
pc = frame->pc;
lr = frame->lr;
// 3. 打印栈帧
pr_emerg(" %*pS\n", 12, (void *)pc);
if (lr != 0) {
pr_emerg(" LR = %pS\n", (void *)lr);
}
// 4. 移动到下一个栈帧
sp = frame->prev;
i++;
if (i > 100) {
break;
}
}
}
7.3 核心代码实现
7.3.1 反汇编内核函数
# 1. 获取内核符号表 nm vmlinux | grep do_sys_open ffff800012345678 T do_sys_open # 2. 反汇编 do_sys_open objdump -d vmlinux | grep -A 20 "do_sys_open" ffff800012345678 <do_sys_open>: ffff800012345678: stp x29, x30, [sp, #-32]! ffff80001234567c: mov x29, sp ffff800012345680: str x19, [sp, #16] ffff800012345684: mov w19, w0 ffff800012345688: mov w0, w1 ffff80001234568c: bl ffff800012345678 <do_sys_open> ffff800012345690: mov w0, w19 ffff800012345694: ldp x19, x29, [sp, #16] ffff800012345698: ldp x29, x30, [sp], #32 ffff80001234569c: ret # 3. 使用 gdb 反汇编 gdb vmlinux (gdb) disassemble do_sys_open Dump of assembler code for function do_sys_open: 0xffff800012345678 <+0>: stp x29, x30, [sp, #-32]! 0xffff80001234567c <+4>: mov x29, sp 0xffff800012345680 <+8>: str x19, [sp, #16] 0xffff800012345684 <+12>: mov w19, w0 0xffff800012345688 <+16>: mov w0, w1 0xffff80001234568c <+20>: bl 0xffff800012345678 <do_sys_open> 0xffff800012345690 <+24>: mov w0, w19 0xffff800012345694 <+28>: ldp x19, x29, [sp, #16] 0xffff800012345698 <+32>: ldp x29, x30, [sp], #32 0xffff80001234569c <+36>: ret End of assembler dump.
7.3.2 崩溃点定位
# 1. 假设崩溃点在 0xffff80001234568c crash> bt PID: 1234 TASK: ffff800012345678 CPU: 0 COMMAND: "app_process" #0 [ffff800012345678] do_sys_open+0x14/0x20 #1 [ffff800012345678] sys_open+0x18/0x28 #2 [ffff800012345678] el0_svc_naked+0x38/0x50 # 2. 反汇编 do_sys_open crash> disassemble do_sys_open 0xffff800012345678 <do_sys_open>: stp x29, x30, [sp, #-32]! mov x29, sp str x19, [sp, #16] mov w19, w0 mov w0, w1 bl 0xffff800012345678 <do_sys_open> mov w0, w19 ldp x19, x29, [sp, #16] ldp x29, x30, [sp], #32 ret # 3. 计算偏移 # 崩溃点在 0xffff80001234568c,相对于 do_sys_open 的偏移 = 0x14 # 对应指令: bl 0xffff800012345678 <do_sys_open> # 4. 分析指令 # bl 0xffff800012345678 <do_sys_open> 是调用 do_sys_open 自身 # 这是一个递归调用,可能导致栈溢出
7.3.3 寄存器分析
# 1. 查看崩溃时的寄存器 crash> regs x0: 0x0000000000000001 x1: 0x0000000000000002 x2: 0x0000000000000003 x3: 0x0000000000000004 x4: 0x0000000000000005 x5: 0x0000000000000006 x6: 0x0000000000000007 x7: 0x0000000000000008 x8: 0x0000000000000009 x9: 0x000000000000000a x10: 0x000000000000000b x11: 0x000000000000000c x12: 0x000000000000000d x13: 0x000000000000000e x14: 0x000000000000000f x15: 0x0000000000000010 x16: 0x0000000000000011 x17: 0x0000000000000012 x18: 0x0000000000000013 x19: 0x0000000000000014 x20: 0x0000000000000015 x21: 0x0000000000000016 x22: 0x0000000000000017 x23: 0x0000000000000018 x24: 0x0000000000000019 x25: 0x000000000000001a x26: 0x000000000000001b x27: 0x000000000000001c x28: 0x000000000000001d x29: 0x000000000000001e x30: 0x000000000000001f sp: 0x0000000000000020 pc: 0x0000000000000021 # 2. 分析函数参数 # x0-w0 是第一个参数 (filename) # x1-w1 是第二个参数 (flags) # 检查 x0 是否为空指针 # 如果 x0 为 0,则可能是空指针引用
7.3.4 栈回溯分析
# 1. 查看栈回溯 crash> bt PID: 1234 TASK: ffff800012345678 CPU: 0 COMMAND: "app_process" #0 [ffff800012345678] do_sys_open+0x14/0x20 #1 [ffff800012345678] sys_open+0x18/0x28 #2 [ffff800012345678] el0_svc_naked+0x38/0x50 # 2. 查看栈内容 crash> stack SP: 0xffff800012345678 PC: 0xffff800012345678 #0: 0xffff800012345678 #1: 0xffff800012345678 #2: 0xffff800012345678 # 3. 分析栈帧 # 栈帧大小: 32 字节 (stp x29, x30, [sp, #-32]!) # 栈帧布局: # sp+0: x29 (fp) # sp+8: x30 (lr) # sp+16: x19 (saved register) # 崩溃点在 0xffff80001234568c,对应第 4 条指令 # 指令: bl 0xffff800012345678 <do_sys_open> # 这是一个递归调用,导致栈溢出
7.4 软件设计模式树形分析
汇编/反汇编分析设计模式 ├── 观察者模式 (Observer Pattern) │ ├── show_stack():观察栈帧信息 │ └── regs_dump():观察寄存器状态 ├── 工厂模式 (Factory Pattern) │ ├── stack_frame_alloc():创建栈帧 │ └── crash_info_alloc():创建崩溃信息结构 ├── 策略模式 (Strategy Pattern) │ ├── stack_trace_strategy():栈回溯策略 │ └── crash_dump_strategy():崩溃转储策略 ├── 适配器模式 (Adapter Pattern) │ ├── regs_to_frame():适配寄存器到栈帧 │ └── crash_to_file():适配崩溃信息到文件 └── 模板方法模式 (Template Method Pattern) ├── show_stack():定义了栈回溯的标准流程 └── crash_handler():定义了崩溃处理的标准流程
7.5 反汇编调试核心难点
7.5.1 栈帧对齐问题
现象:栈回溯中 fp 指针不对齐,导致栈帧解析错误。
原因:
-
编译器优化。
-
汇编中未正确保存
fp。 -
栈空间分配错误。
调试方法:
-
检查
fp是否指向正确位置。 -
检查
stp x29, x30, [sp, #-32]!指令。 -
使用
-fno-omit-frame-pointer重新编译。
7.5.2 符号解析失败
现象:崩溃日志中函数名无法解析,显示 [<0xffff800012345678>]。
原因:
-
内核符号表未加载。
-
函数被内联。
-
调试信息缺失。
调试方法:
-
使用
nm vmlinux | grep 0xffff800012345678查找符号。 -
使用
objdump -d vmlinux查看反汇编。 -
使用
gdb vmlinux加载调试信息。
7.6 与其他模块的协同
| 模块 | 协同方式 | 调试关键点 |
|---|---|---|
| 内存管理 | 提供内存信息 | 页面状态、堆栈布局 |
| 进程管理 | 提供进程信息 | 栈指针、任务结构 |
| 调试工具 | 提供反汇编接口 | objdump、gdb、crash |
| 编译器 | 生成调试信息 | -g、-fno-omit-frame-pointer |
| 内核符号表 | 提供符号解析 | kallsyms、System.map |
第八部分 内核死锁、内存踩踏与野指针问题排查
8.1 三类复杂问题的核心概念
在内核开发中,死锁 (Deadlock)、内存踩踏 (Memory Corruption) 和 野指针 (Dangling Pointer) 是最具挑战性的三类问题。它们往往难以复现,且一旦触发可能导致系统崩溃、数据损坏或安全漏洞。
8.1.1 问题对比
| 问题类型 | 根本原因 | 典型症状 | 检测工具 | 修复难度 |
|---|---|---|---|---|
| 死锁 | 锁依赖循环、锁顺序错误 | 系统卡死、CPU 占用 100%、进程阻塞 | Lockdep, lock_stat, ftrace | 中等 |
| 内存踩踏 | 越界写、缓冲区溢出、use-after-free | 数据损坏、随机崩溃、OOM | KASAN, SLUB Debug, kmemleak | 高 |
| 野指针 | 指针未初始化、use-after-free | 非法内存访问、NULL 指针引用 | KASAN, UBSAN, kasan | 高 |
8.1.2 三类问题的关系图
[内核并发问题] ├── [死锁 (Deadlock)] │ ├── 互斥锁死锁 (Mutex Deadlock) │ ├── 读写锁死锁 (RWSem Deadlock) │ ├── 自旋锁死锁 (Spinlock Deadlock) │ └── 递归锁死锁 (Recursive Lock Deadlock) ├── [内存踩踏 (Memory Corruption)] │ ├── 缓冲区溢出 (Buffer Overflow) │ ├── 越界写 (Out-of-bounds Write) │ ├── Use-after-free │ └── 双重释放 (Double Free) └── [野指针 (Dangling Pointer)] ├── 指针未初始化 (Uninitialized Pointer) ├── 释放后使用 (Use-after-free) └── 空指针引用 (NULL Pointer Dereference)
8.2 死锁检测与排查
8.2.1 死锁检测工具 (代码出处: lib/locking/lockdep.c)
// 代码出处: lib/locking/lockdep.c
/**
* @struct lockdep_map
* @brief Lockdep 锁映射结构。
*/
struct lockdep_map {
struct lock_class_key *key; /**< 锁类键 */
struct lock_class *class; /**< 锁类 */
const char *name; /**< 锁名称 */
int wait_type_outer; /**< 外部等待类型 */
int wait_type_inner; /**< 内部等待类型 */
};
/**
* @brief Lockdep 锁依赖检测。
* @param lock 锁映射
* @param ip 调用地址
* @param nest 嵌套层级
* @param flags 标志
*/
void lockdep_acquire(struct lockdep_map *lock, unsigned long ip, int nest, int flags)
{
struct task_struct *curr = current;
struct lock_class *class = lock->class;
int i;
// 1. 检查锁是否已持有
for (i = 0; i < curr->lockdep_depth; i++) {
if (curr->held_locks[i].class == class) {
// 重复锁,检查是否允许递归
if (!class->recursive) {
lockdep_print_held_locks(curr);
WARN_ON(1);
}
return;
}
}
// 2. 检查锁依赖顺序
if (curr->lockdep_depth > 0) {
struct lock_class *prev = curr->held_locks[curr->lockdep_depth - 1].class;
if (lockdep_check_prev(prev, class) < 0) {
lockdep_print_held_locks(curr);
WARN_ON(1);
}
}
// 3. 记录锁依赖
curr->held_locks[curr->lockdep_depth].class = class;
curr->held_locks[curr->lockdep_depth].ip = ip;
curr->lockdep_depth++;
}
/**
* @brief Lockdep 锁依赖图打印。
*/
void lockdep_print_dependency_graph(void)
{
struct lock_class *class;
struct lock_class *dep;
int i;
pr_info("Lock dependency graph:\n");
list_for_each_entry(class, &all_lock_classes, lock_entry) {
pr_info(" %s:\n", class->name);
for (i = 0; i < class->dep_count; i++) {
dep = class->deps[i];
pr_info(" -> %s\n", dep->name);
}
}
}
8.2.2 死锁排查流程
# 1. 启用 Lockdep
echo 1 > /proc/sys/kernel/lockdep
echo 1 > /proc/sys/kernel/lockdep_verbose
# 2. 触发死锁 (模拟)
# 编写代码造成锁顺序颠倒
# 3. 查看 Lockdep 报告
dmesg | grep "possible circular locking dependency detected"
# 4. 分析死锁报告
[ 123.456789] ======================================================
[ 123.456789] [ INFO: possible circular locking dependency detected ]
[ 123.456789] 5.10.0-rc1+ #1 Not tainted
[ 123.456789] ------------------------------------------------------
[ 123.456789] app_process/1234 is trying to acquire lock:
[ 123.456789] (lock_a){+.+.}-{3:3}, at: mutex_lock+0x14/0x20
[ 123.456789]
[ 123.456789] but task is already holding lock:
[ 123.456789] (lock_b){+.+.}-{3:3}, at: mutex_lock+0x14/0x20
[ 123.456789]
[ 123.456789] which lock already depends on the new lock.
[ 123.456789]
[ 123.456789] the existing dependency chain (in reverse order) is:
[ 123.456789]
[ 123.456789] -> #1 (lock_b){+.+.}-{3:3}:
[ 123.456789] lock_acquire+0x30/0x40
[ 123.456789] mutex_lock+0x14/0x20
[ 123.456789] do_sys_open+0x14/0x20
[ 123.456789] sys_open+0x18/0x28
[ 123.456789] el0_svc_naked+0x38/0x50
[ 123.456789]
[ 123.456789] -> #0 (lock_a){+.+.}-{3:3}:
[ 123.456789] lock_acquire+0x30/0x40
[ 123.456789] mutex_lock+0x14/0x20
[ 123.456789] do_sys_open+0x14/0x20
[ 123.456789] sys_open+0x18/0x28
[ 123.456789] el0_svc_naked+0x38/0x50
[ 123.456789]
[ 123.456789] other info that might help us debug this:
[ 123.456789]
[ 123.456789] 2 locks held by app_process/1234:
[ 123.456789] #0: (lock_b){+.+.}-{3:3}, at: mutex_lock+0x14/0x20
[ 123.456789] #1: (lock_a){+.+.}-{3:3}, at: mutex_lock+0x14/0x20
# 5. 修复建议
# 修改锁顺序,确保 lock_a 在 lock_b 之前获取
8.2.3 死锁避免策略
// 代码出处: kernel/locking/mutex.c
/**
* @brief 死锁避免:获取锁时保持顺序。
* @param lock_a 第一个锁
* @param lock_b 第二个锁
*/
void safe_lock_order(struct mutex *lock_a, struct mutex *lock_b)
{
// 1. 确保 lock_a 在 lock_b 之前获取
mutex_lock(lock_a);
mutex_lock(lock_b);
// ... 临界区代码 ...
mutex_unlock(lock_b);
mutex_unlock(lock_a);
}
/**
* @brief 死锁避免:使用锁超时。
* @param lock 锁指针
* @param timeout_ms 超时时间 (毫秒)
* @return 0 成功,-ETIMEDOUT 超时
*/
int try_lock_with_timeout(struct mutex *lock, int timeout_ms)
{
unsigned long timeout = msecs_to_jiffies(timeout_ms);
unsigned long start = jiffies;
while (time_before(jiffies, start + timeout)) {
if (mutex_trylock(lock)) {
return 0;
}
schedule_timeout_uninterruptible(1);
}
return -ETIMEDOUT;
}
8.3 内存踩踏检测与排查
8.3.1 KASAN 检测 (代码出处: mm/kasan/kasan.c)
// 代码出处: mm/kasan/kasan.c
/**
* @brief KASAN 内存访问检测。
* @param addr 访问地址
* @param size 访问大小
* @param is_write 是否写操作
* @param ip 调用地址
*/
void kasan_report(unsigned long addr, size_t size, int is_write, unsigned long ip)
{
struct kasan_access_info info;
const char *access_type = is_write ? "write" : "read";
// 1. 检查是否已报告
if (kasan_suppress_report()) {
return;
}
// 2. 构建错误信息
info.addr = addr;
info.size = size;
info.is_write = is_write;
info.ip = ip;
// 3. 打印错误信息
pr_err("==================================================================\n");
pr_err("BUG: KASAN: %s in %pS\n", access_type, (void *)ip);
pr_err("Address: %pK\n", (void *)addr);
pr_err("Size: %zu\n", size);
pr_err("Memory state around the buggy address:\n");
// 4. 打印内存状态
for (int i = -32; i < 32; i += 8) {
pr_err(" %pK: %02x\n", (void *)(addr + i), kasan_read_memory_byte(addr + i));
}
pr_err("==================================================================\n");
// 5. 触发崩溃
panic("KASAN: memory corruption detected");
}
/**
* @brief KASAN 越界写检测。
* @param addr 访问地址
* @param size 访问大小
* @param is_write 是否写操作
*/
void kasan_check_memory(unsigned long addr, size_t size, int is_write)
{
unsigned long end = addr + size;
unsigned long *shadow = kasan_mem_to_shadow(addr);
unsigned long shadow_end = kasan_mem_to_shadow(end);
// 1. 检查阴影区域
while (shadow < shadow_end) {
u8 shadow_val = kasan_read_memory_byte(shadow);
if (shadow_val != 0) {
kasan_report(addr, size, is_write, __builtin_return_address(0));
return;
}
shadow++;
}
}
8.3.2 SLUB Debug 检测 (代码出处: mm/slub.c)
// 代码出处: mm/slub.c
/**
* @brief SLUB 对象生命周期检测。
* @param object 对象指针
* @param cache 缓存指针
*/
void slub_debug_object(struct page *page, void *object)
{
struct kmem_cache *cache = page->slab_cache;
u8 *page_object = page->slab_cache->object;
u8 *red_zone = page_object + page->slab_cache->size;
// 1. 检查红色区域 (Red Zone)
if (*(u8 *)(object + cache->size) != SLUB_RED_ZONE) {
pr_err("SLUB: Red zone corruption in %s\n", cache->name);
kasan_report((unsigned long)object, cache->size, 0, __builtin_return_address(0));
}
// 2. 检查对象状态
if (*(u8 *)object == SLUB_FREE) {
// 释放后使用
pr_err("SLUB: Use-after-free detected in %s\n", cache->name);
kasan_report((unsigned long)object, cache->size, 0, __builtin_return_address(0));
}
}
/**
* @brief SLUB 对象分配跟踪。
* @param cache 缓存指针
* @param object 对象指针
* @param caller 调用地址
*/
void slub_alloc_trace(struct kmem_cache *cache, void *object, unsigned long caller)
{
struct trace_slub *trace = slub_alloc_trace_create(cache, object, caller);
if (!trace) {
return;
}
// 1. 记录分配信息
trace->cache = cache;
trace->object = object;
trace->caller = caller;
trace->timestamp = jiffies;
trace->size = cache->size;
// 2. 添加到跟踪列表
list_add_tail(&trace->list, &slub_trace_list);
}
8.3.3 内存踩踏排查流程
# 1. 启用 KASAN echo 1 > /proc/sys/kernel/kasan echo 1 > /proc/sys/kernel/kasan_verbose # 2. 启用 SLUB Debug echo "slub_debug=P" > /proc/sys/kernel/slub_debug # 3. 执行测试操作 # 触发内存踩踏 # 4. 查看 KASAN 报告 dmesg | grep "KASAN" # 5. 分析 KASAN 报告 [ 123.456789] ================================================================== [ 123.456789] BUG: KASAN: use-after-free in do_sys_open+0x14/0x20 [ 123.456789] Address: ffff800012345678 [ 123.456789] Size: 32 [ 123.456789] Memory state around the buggy address: [ 123.456789] ffff800012345678: 0b 0b 0b 0b 0b 0b 0b 0b [ 123.456789] ffff800012345680: 0b 0b 0b 0b 0b 0b 0b 0b [ 123.456789] ================================================================== # 6. 修复建议 # 检查 do_sys_open 函数的对象生命周期 # 确保使用 kfree 后不再访问对象
8.4 野指针检测与排查
8.4.1 野指针检测工具
# 1. 使用 KASAN 检测野指针 echo 1 > /proc/sys/kernel/kasan # 2. 使用 UBSAN 检测未初始化指针 echo 1 > /proc/sys/kernel/ubsan # 3. 触发野指针 (模拟) # 编写代码造成 use-after-free # 4. 查看报告 dmesg | grep "UBSAN" [ 123.456789] ================================================================== [ 123.456789] UBSAN: Undefined behavior in do_sys_open+0x14/0x20 [ 123.456789] Use of uninitialized pointer in do_sys_open [ 123.456789] ================================================================== # 5. 修复建议 # 检查 do_sys_open 函数中的指针初始化 # 确保指针在使用前已经初始化
8.4.2 野指针排查流程
// 代码出处: lib/ubsan/ubsan.c
/**
* @brief UBSAN 未初始化检测。
* @param data 未初始化数据
* @param size 数据大小
* @param ip 调用地址
*/
void __ubsan_handle_uninitialized(void *data, size_t size, unsigned long ip)
{
ubsan_report_uninitialized(data, size, ip);
// 1. 打印未初始化的内存内容
u8 *bytes = (u8 *)data;
for (int i = 0; i < size; i++) {
if (bytes[i] != 0x00) {
pr_err("UBSAN: Uninitialized memory content: 0x%02x\n", bytes[i]);
}
}
// 2. 触发崩溃
panic("UBSAN: uninitialized pointer detected");
}
/**
* @brief UBSAN 空指针引用检测。
* @param addr 引用地址
* @param size 引用大小
* @param ip 调用地址
*/
void __ubsan_handle_null_pointer(void *addr, size_t size, unsigned long ip)
{
ubsan_report_null_pointer(addr, size, ip);
// 1. 打印空指针信息
pr_err("UBSAN: Null pointer dereference at %p\n", addr);
// 2. 触发崩溃
panic("UBSAN: null pointer dereference detected");
}
8.5 综合排查流程
# 1. 启用所有调试工具 echo 1 > /proc/sys/kernel/lockdep echo 1 > /proc/sys/kernel/kasan echo 1 > /proc/sys/kernel/ubsan echo "slub_debug=PF" > /proc/sys/kernel/slub_debug # 2. 执行压力测试 stress --cpu 4 --vm 2 --vm-bytes 512M --timeout 60 # 3. 收集所有报告 dmesg | grep -E "(lockdep|KASAN|UBSAN|SLUB)" > /tmp/kernel_issues.log # 4. 分析报告 cat /tmp/kernel_issues.log # 5. 逐项修复 # - 死锁: 调整锁顺序 # - 内存踩踏: 检查缓冲区边界 # - 野指针: 初始化指针
8.6 与其他模块的协同
| 模块 | 协同方式 | 调试关键点 |
|---|---|---|
| 锁管理 | 提供锁依赖检测 | 锁顺序、递归锁 |
| 内存管理 | 提供内存分配跟踪 | 缓冲区边界、对象生命周期 |
| 进程管理 | 提供进程状态信息 | 进程栈、任务结构 |
| 调度器 | 提供调度信息 | 任务上下文、CPU 状态 |
| 调试工具 | 提供检测接口 | Lockdep、KASAN、UBSAN |
第九部分 eBPF 模块编程
9.1 eBPF 核心概念
eBPF (extended Berkeley Packet Filter) 是 Linux 内核中一项革命性的技术,允许在不修改内核代码的情况下,动态加载和执行沙箱化的程序。eBPF 程序可以挂载到内核的各个 hook 点(如 kprobe、tracepoint、系统调用、网络包处理等),实现高性能的数据收集、过滤和监控。
9.1.1 eBPF 架构
[用户空间] ├── eBPF 程序源 (.c) ├── 编译器 (clang/LLVM) → 生成 BPF 字节码 ├── libbpf 库 → 加载 BPF 程序 └── BPF 映射 (Maps) → 内核与用户空间交换数据 ↓ [内核空间] ├── eBPF 验证器 (Verifier) │ ├── 检查程序安全性 │ ├── 检查指令数限制 │ └── 检查内存访问边界 ├── eBPF 执行引擎 (JIT) │ ├── 将字节码编译为本地指令 │ └── 提高执行效率 ├── eBPF 运行时 (Runtime) │ ├── kprobe/kretprobe │ ├── tracepoint │ ├── 系统调用 (syscall) │ └── 网络包处理 (XDP/TC) └── BPF 映射 (Maps) ├── Hash Map ├── Array Map ├── Per-CPU Map └── Ring Buffer
9.1.2 eBPF hook 点
| Hook 类型 | 触发时机 | 典型用途 |
|---|---|---|
| kprobe | 函数入口 | 追踪内核函数调用、参数检查 |
| kretprobe | 函数返回 | 追踪返回值、性能分析 |
| tracepoint | 内核事件 | 系统调用追踪、调度事件 |
| syscall | 系统调用 | 安全监控、行为分析 |
| XDP | 网卡驱动层 | 高性能包过滤、DDoS 防护 |
| TC | 网络栈层 | 流量控制、QoS |
| cgroup | 控制组事件 | 资源限制、容器监控 |
| perf_event | 性能事件 | 性能采样、热点分析 |
9.2 核心数据结构
9.2.1 eBPF 程序结构 (代码出处: include/linux/bpf.h)
// 代码出处: include/linux/bpf.h
/**
* @struct bpf_prog
* @brief eBPF 程序结构。
*/
struct bpf_prog {
struct bpf_prog_aux *aux; /**< 辅助数据 */
struct bpf_insn *insnsi; /**< 指令数组 */
u32 len; /**< 指令数量 */
u32 jited_len; /**< JIT 后的长度 */
u8 *jited; /**< JIT 后的指令 */
struct bpf_prog_stats *stats; /**< 性能统计 */
unsigned int pages; /**< 占用页面数 */
struct rcu_head rcu; /**< RCU 头 */
struct work_struct work; /**< 工作队列 */
};
/**
* @struct bpf_map
* @brief eBPF 映射结构。
*/
struct bpf_map {
const struct bpf_map_ops *ops; /**< 映射操作 */
void *key_size; /**< 键大小 */
void *value_size; /**< 值大小 */
u32 max_entries; /**< 最大条目数 */
u32 map_type; /**< 映射类型 */
u32 flags; /**< 标志 */
u64 *memory; /**< 内存区域 */
struct bpf_map *inner_map; /**< 内层映射 */
struct list_head list; /**< 链表节点 */
struct rcu_head rcu; /**< RCU 头 */
};
/**
* @struct bpf_prog_aux
* @brief eBPF 程序辅助结构。
*/
struct bpf_prog_aux {
struct bpf_prog *prog; /**< 关联的程序 */
struct bpf_map **used_maps; /**< 使用的映射 */
u32 used_map_cnt; /**< 映射数量 */
u32 stack_depth; /**< 栈深度 */
u32 id; /**< 程序 ID */
const struct bpf_func_proto *func_proto; /**< 函数原型 */
struct dentry *dentry; /**< debugfs 条目 */
struct work_struct work; /**< 工作队列 */
struct rcu_head rcu; /**< RCU 头 */
};
9.3 核心代码实现
9.3.1 kprobe 挂载示例 (代码出处: samples/bpf/kprobe_example.c)
// 代码出处: samples/bpf/kprobe_example.c
/**
* @brief eBPF kprobe 程序:追踪 open 系统调用。
*/
SEC("kprobe/sys_open")
int bpf_kprobe_open(struct pt_regs *ctx)
{
// 1. 获取进程 ID 和用户 ID
u32 pid = bpf_get_current_pid_tgid() >> 32;
u32 uid = bpf_get_current_uid_gid() & 0xFFFFFFFF;
u64 key = pid;
u64 *value;
// 2. 获取文件名 (从寄存器中读取)
char filename[256];
bpf_probe_read_user_str(filename, sizeof(filename),
(void *)PT_REGS_PARM1(ctx));
// 3. 输出追踪信息
bpf_trace_printk("open: pid=%d uid=%d file=%s\n", pid, uid, filename);
// 4. 更新统计信息
value = bpf_map_lookup_elem(&open_stats_map, &key);
if (!value) {
u64 init_val = 1;
bpf_map_update_elem(&open_stats_map, &key, &init_val, BPF_ANY);
return 0;
}
(*value)++;
bpf_map_update_elem(&open_stats_map, &key, value, BPF_ANY);
return 0;
}
// 定义 BPF 映射
struct bpf_map_def SEC("maps") open_stats_map = {
.type = BPF_MAP_TYPE_HASH,
.key_size = sizeof(u64),
.value_size = sizeof(u64),
.max_entries = 1024,
};
char _license[] SEC("license") = "GPL";
9.3.2 tracepoint 挂载示例 (代码出处: samples/bpf/tracepoint_example.c)
// 代码出处: samples/bpf/tracepoint_example.c
/**
* @brief eBPF tracepoint 程序:追踪系统调用。
*/
SEC("tracepoint/syscalls/sys_enter_openat")
int bpf_tracepoint_sys_enter_openat(struct trace_event_raw_sys_enter *ctx)
{
u32 pid = bpf_get_current_pid_tgid() >> 32;
char filename[256];
int ret;
// 1. 获取文件名参数
ret = bpf_probe_read_user_str(filename, sizeof(filename),
(void *)ctx->args[1]);
if (ret < 0) {
return -EIO;
}
// 2. 输出追踪信息
bpf_trace_printk("openat: pid=%d file=%s\n", pid, filename);
return 0;
}
char _license[] SEC("license") = "GPL";
9.3.3 加载 eBPF 程序 (用户空间) (代码出处: samples/bpf/trace_event_user.c)
// 代码出处: samples/bpf/trace_event_user.c
/**
* @brief 加载并运行 BPF 程序。
* @param obj_path BPF 目标文件路径
* @return 0 成功,负数错误
*/
int bpf_load_program(const char *obj_path)
{
struct bpf_object *obj;
struct bpf_program *prog;
struct bpf_map *map;
int prog_fd, map_fd, ret;
// 1. 打开 BPF 目标文件
obj = bpf_object__open_file(obj_path, NULL);
if (IS_ERR(obj)) {
return PTR_ERR(obj);
}
// 2. 加载 BPF 程序
ret = bpf_object__load(obj);
if (ret < 0) {
bpf_object__close(obj);
return ret;
}
// 3. 获取程序文件描述符
prog = bpf_object__find_program_by_name(obj, "bpf_kprobe_open");
if (!prog) {
ret = -ENOENT;
goto out;
}
prog_fd = bpf_program__fd(prog);
// 4. 获取映射文件描述符
map = bpf_object__find_map_by_name(obj, "open_stats_map");
if (!map) {
ret = -ENOENT;
goto out;
}
map_fd = bpf_map__fd(map);
// 5. 挂载 kprobe
ret = bpf_prog_attach(prog_fd, get_kprobe_events(), 0);
if (ret < 0) {
goto out;
}
// 6. 读取统计信息
u64 key, value;
for (int i = 0; i < 10; i++) {
key = i;
if (bpf_map_lookup_elem(map_fd, &key, &value) == 0) {
printf("open: pid=%lld count=%lld\n", key, value);
}
}
out:
bpf_object__close(obj);
return ret;
}
9.3.4 使用 ring buffer 传递数据 (代码出处: samples/bpf/ring_buffer_example.c)
// 代码出处: samples/bpf/ring_buffer_example.c
/**
* @brief eBPF ring buffer 程序。
*/
struct data_event {
u32 pid;
u32 uid;
char comm[16];
char filename[256];
};
SEC("kprobe/sys_open")
int bpf_ring_buffer_open(struct pt_regs *ctx)
{
struct data_event *event;
u32 pid = bpf_get_current_pid_tgid() >> 32;
u32 uid = bpf_get_current_uid_gid() & 0xFFFFFFFF;
// 1. 从 ring buffer 中申请内存
event = bpf_ringbuf_reserve(&ring_buffer_map, sizeof(*event), 0);
if (!event) {
return -ENOMEM;
}
// 2. 填充事件数据
event->pid = pid;
event->uid = uid;
bpf_probe_read_str(event->comm, sizeof(event->comm), current->comm);
bpf_probe_read_user_str(event->filename, sizeof(event->filename),
(void *)PT_REGS_PARM1(ctx));
// 3. 提交事件到 ring buffer
bpf_ringbuf_submit(event, 0);
return 0;
}
struct bpf_map_def SEC("maps") ring_buffer_map = {
.type = BPF_MAP_TYPE_RINGBUF,
.max_entries = 256 * 1024, // 256 KB
};
char _license[] SEC("license") = "GPL";
9.3.5 用户空间读取 ring buffer 数据
// 代码出处: samples/bpf/ring_buffer_user.c
/**
* @brief ring buffer 事件处理回调。
* @param ctx 上下文
* @param data 事件数据
* @param size 事件大小
* @return 0 成功,负数错误
*/
static int ring_buffer_event_handler(void *ctx, void *data, size_t size)
{
struct data_event *event = (struct data_event *)data;
// 1. 处理事件
printf("open: pid=%d uid=%d comm=%s file=%s\n",
event->pid, event->uid, event->comm, event->filename);
return 0;
}
/**
* @brief 读取 ring buffer 数据。
* @param map_fd ring buffer 映射文件描述符
* @return 0 成功,负数错误
*/
int bpf_ring_buffer_read(int map_fd)
{
struct ring_buffer *rb;
int ret;
// 1. 创建 ring buffer 管理器
rb = ring_buffer__new(map_fd, ring_buffer_event_handler, NULL);
if (!rb) {
return -ENOMEM;
}
// 2. 轮询 ring buffer
while (1) {
ret = ring_buffer__poll(rb, 100);
if (ret < 0) {
break;
}
}
// 3. 清理
ring_buffer__free(rb);
return ret;
}
9.3.6 BPF Map 操作
// 代码出处: kernel/bpf/map.c
/**
* @brief 创建 BPF 映射。
* @param map_type 映射类型
* @param key_size 键大小
* @param value_size 值大小
* @param max_entries 最大条目数
* @param flags 标志
* @return 映射文件描述符,负数错误
*/
int bpf_map_create(enum bpf_map_type map_type, u32 key_size,
u32 value_size, u32 max_entries, u32 flags)
{
struct bpf_map *map;
int ret;
// 1. 检查权限
if (!capable(CAP_BPF)) {
return -EPERM;
}
// 2. 分配映射结构
map = kmalloc(sizeof(*map), GFP_KERNEL);
if (!map) {
return -ENOMEM;
}
map->map_type = map_type;
map->key_size = key_size;
map->value_size = value_size;
map->max_entries = max_entries;
map->flags = flags;
INIT_LIST_HEAD(&map->list);
map->ops = bpf_map_ops;
// 3. 分配内存
map->memory = kmalloc(max_entries * value_size, GFP_KERNEL);
if (!map->memory) {
kfree(map);
return -ENOMEM;
}
// 4. 注册映射
ret = bpf_map_register(map);
if (ret < 0) {
kfree(map->memory);
kfree(map);
return ret;
}
// 5. 返回文件描述符
return bpf_map_get_fd(map);
}
/**
* @brief 从 BPF 映射中查找条目。
* @param map_fd 映射文件描述符
* @param key 键指针
* @param value 值指针
* @return 0 成功,负数错误
*/
int bpf_map_lookup_elem(int map_fd, void *key, void *value)
{
struct bpf_map *map = bpf_map_fd_get(map_fd);
u32 key_offset = (u32)key - (u32)map->memory;
u32 value_offset = (u32)value - (u32)map->memory;
// 1. 检查偏移量
if (key_offset >= map->max_entries * map->key_size) {
return -EINVAL;
}
if (value_offset >= map->max_entries * map->value_size) {
return -EINVAL;
}
// 2. 复制值
memcpy(value, map->memory + value_offset, map->value_size);
return 0;
}
9.4 软件设计模式树形分析
eBPF 编程设计模式 ├── 工厂模式 (Factory Pattern) │ ├── bpf_object__open_file():创建 BPF 对象 │ └── bpf_map_create():创建 BPF 映射 ├── 适配器模式 (Adapter Pattern) │ ├── bpf_prog_attach():适配 BPF 程序到 kprobe │ └── ring_buffer__new():适配 ring buffer 到用户空间 ├── 策略模式 (Strategy Pattern) │ ├── bpf_kprobe_open():kprobe 追踪策略 │ └── bpf_tracepoint_sys_enter_openat():tracepoint 追踪策略 ├── 观察者模式 (Observer Pattern) │ ├── bpf_ring_buffer_read():观察 ring buffer 事件 │ └── ring_buffer_event_handler():观察事件回调 ├── 状态模式 (State Pattern) │ └── BPF 程序状态 (LOADED/ATTACHED/RUNNING/UNLOADED) └── 模板方法模式 (Template Method Pattern) └── bpf_load_program():定义了 BPF 程序加载的标准流程
9.5 eBPF 调试核心难点
9.5.1 验证器错误
现象:加载 BPF 程序时返回 -EINVAL,验证器报告 "invalid bpf program"。
原因:
-
BPF 程序循环次数超出限制。
-
BPF 栈大小超过 512 字节。
-
BPF 映射访问越界。
调试方法:
-
使用
bpftool prog dump xlated查看指令。 -
检查程序是否包含循环。
-
减少栈使用或使用映射。
9.5.2 kprobe 挂载失败
现象:bpf_prog_attach 返回 -ENOENT,无法挂载 kprobe。
原因:
-
函数名错误或函数不存在。
-
内核未启用 CONFIG_KPROBE。
-
权限不足。
调试方法:
-
检查函数是否存在:
cat /proc/kallsyms | grep do_sys_open。 -
检查内核配置:
zgrep CONFIG_KPROBE /proc/config.gz。 -
使用
sudo执行。
9.5.3 Ring buffer 溢出
现象:bpf_ringbuf_reserve 返回 NULL,ring buffer 溢出。
原因:
-
ring buffer 太小。
-
事件产生速度过快。
-
用户空间处理速度慢。
调试方法:
-
增加 ring buffer 大小。
-
在 BPF 程序中增加丢包策略。
-
使用
bpftool map dump查看 ring buffer 状态。
9.6 与其他模块的协同
| 模块 | 协同方式 | 调试关键点 |
|---|---|---|
| kprobe | 挂载 BPF 程序到函数入口 | 函数参数、返回值 |
| tracepoint | 挂载 BPF 程序到内核事件 | 事件数据、上下文 |
| perf_event | 挂载 BPF 程序到性能事件 | 性能采样、热点分析 |
| XDP | 挂载 BPF 程序到网卡驱动层 | 包处理、过滤策略 |
| TC | 挂载 BPF 程序到网络栈层 | 流量控制、QoS |
| cgroup | 挂载 BPF 程序到控制组 | 资源限制、容器监控 |
第十部分 Android 内核特性分析与优化
10.1 Android 内核特性概述
Android 内核基于 Linux 主线内核,但包含大量 Android 特有的增强和优化。这些特性包括 Binder IPC 优化、Low Memory Killer (LMK) 增强、Ashmem 共享内存、ION 内存分配器、F2FS 文件系统优化、SELinux 策略等。理解这些特性并进行针对性优化,是提升 Android 系统性能和稳定性的关键。
10.1.1 Android 内核特性全景
[Android 内核特性] ├── [IPC 优化] │ ├── Binder 优化 (oneway 事务、线程池管理) │ ├── Ashmem 共享内存 (优化 TLB 刷新) │ └── ION 内存分配器 (连续内存分配、DMA-BUF) ├── [内存优化] │ ├── Low Memory Killer (进程优先级、杀进程策略) │ ├── ZRAM 压缩 (压缩算法选择、交换策略) │ └── KSM (Kernel Samepage Merging) ├── [文件系统优化] │ ├── F2FS (Flash-Friendly File System) │ ├── EROFS (Enhanced Read-Only File System) │ └── FUSE 优化 ├── [电源优化] │ ├── Suspend/Resume 优化 │ ├── 唤醒源管理 │ └── CPU 热插拔 └── [安全优化] ├── SELinux 策略优化 ├── TrustZone 集成 └── 安全启动 (dm-verity)
10.1.2 Android 内核优化目标
| 优化目标 | 核心指标 | 优化手段 |
|---|---|---|
| 响应速度 | 应用启动时间、触控响应 | Binder 优先队列、LMK 策略 |
| 内存效率 | 内存占用、进程生命周期 | ZRAM 压缩、KSM、ION 池 |
| 存储性能 | 读写速度、I/O 延迟 | F2FS 优化、EROFS 压缩 |
| 电源效率 | 待机功耗、唤醒次数 | Suspend/Resume 优化、唤醒源管理 |
| 安全性 | 安全漏洞数量 | SELinux 策略、内核加固 |
| 稳定性 | 崩溃次数、内核 panic | 内存腐蚀检测、死锁检测 |
10.2 核心优化实现
10.2.1 Binder 优化 (代码出处: drivers/android/binder.c)
// 代码出处: drivers/android/binder.c
/**
* @brief Binder 事务池优化:预分配事务池。
* @param proc Binder 进程指针
* @param pool_size 池大小
* @return 0 成功,负数错误
*/
int binder_prealloc_transaction_pool(struct binder_proc *proc, int pool_size)
{
struct binder_transaction *t;
int i;
// 1. 预分配事务池
proc->transaction_pool = kcalloc(pool_size, sizeof(*t), GFP_KERNEL);
if (!proc->transaction_pool) {
return -ENOMEM;
}
// 2. 初始化事务池
for (i = 0; i < pool_size; i++) {
t = &proc->transaction_pool[i];
t->from_proc = proc;
t->to_proc = proc;
t->buffer = binder_alloc_new(&proc->alloc, 1024);
INIT_LIST_HEAD(&t->list);
}
return 0;
}
/**
* @brief Binder 线程池优化:动态调整线程数量。
* @param proc Binder 进程指针
* @param delta 变化量
* @return 0 成功,负数错误
*/
int binder_adjust_thread_pool(struct binder_proc *proc, int delta)
{
int target = proc->thread_count + delta;
// 1. 检查线程数量范围
if (target < proc->min_threads) {
target = proc->min_threads;
}
if (target > proc->max_threads) {
target = proc->max_threads;
}
// 2. 调整线程数量
while (proc->thread_count < target) {
int tid = binder_create_thread(proc);
if (tid < 0) {
return tid;
}
proc->thread_count++;
}
while (proc->thread_count > target) {
binder_stop_thread(proc);
proc->thread_count--;
}
return 0;
}
/**
* @brief Binder oneway 事务优化:批量处理。
* @param proc Binder 进程指针
* @param t 事务指针
* @return 0 成功,负数错误
*/
int binder_oneway_batch(struct binder_proc *proc, struct binder_transaction *t)
{
struct binder_transaction *next;
// 1. 检查是否支持批量处理
if (!t->flags & BINDER_TRANSACTION_FLAG_ONEWAY) {
return -EINVAL;
}
// 2. 批量处理事务
list_for_each_entry(next, &proc->oneway_queue, list) {
if (next->to_proc == t->to_proc) {
// 合并事务
binder_transaction_merge(t, next);
return 0;
}
}
// 3. 添加到队列
list_add_tail(&t->list, &proc->oneway_queue);
return 0;
}
10.2.2 Low Memory Killer 优化 (代码出处: drivers/staging/android/lowmemorykiller.c)
// 代码出处: drivers/staging/android/lowmemorykiller.c
/**
* @brief LMK 杀进程策略优化:优先杀后台进程。
* @param task 任务指针
* @return 0 不杀,>0 杀
*/
int lowmem_kill_priority(struct task_struct *task)
{
int oom_score_adj = task->signal->oom_score_adj;
int priority = task->prio;
int flags = task->flags;
// 1. 检查是否为前台进程
if (oom_score_adj < OOM_SCORE_ADJ_FOREGROUND) {
return 0; // 前台进程不杀
}
// 2. 检查是否为系统服务
if (flags & PF_SYSTEM) {
return 0; // 系统服务不杀
}
// 3. 检查是否为内核线程
if (flags & PF_KTHREAD) {
return 0; // 内核线程不杀
}
// 4. 根据优先级计算杀进程分数
int score = oom_score_adj * 100 / OOM_SCORE_ADJ_MAX;
if (priority > 100) {
score += 50; // 高优先级进程加分
}
if (flags & PF_IO_WORKER) {
score += 30; // I/O 工作进程加分
}
return score;
}
/**
* @brief LMK 杀进程策略优化:批量杀进程。
* @param minfree 最小空闲页面数
* @param kill_count 杀进程数量
* @return 释放的页面数
*/
int lowmem_batch_kill(unsigned long minfree, int kill_count)
{
struct task_struct *task;
struct mm_struct *mm;
unsigned long freed = 0;
int killed = 0;
// 1. 扫描所有进程
rcu_read_lock();
for_each_process(task) {
if (killed >= kill_count) {
break;
}
mm = task->mm;
if (!mm) {
continue;
}
// 2. 计算杀进程分数
int score = lowmem_kill_priority(task);
if (score <= 0) {
continue;
}
// 3. 发送 SIGKILL
send_sig(SIGKILL, task, 0);
killed++;
freed += get_mm_rss(mm);
// 4. 记录日志
pr_info("LMK: batch kill pid=%d comm=%s score=%d\n",
task->pid, task->comm, score);
}
rcu_read_unlock();
return freed;
}
10.2.3 ZRAM 压缩优化 (代码出处: drivers/block/zram/zram_drv.c)
// 代码出处: drivers/block/zram/zram_drv.c
/**
* @brief ZRAM 压缩算法选择优化。
* @param zram ZRAM 设备指针
* @param algo 算法名称
* @return 0 成功,负数错误
*/
int zram_select_best_algorithm(struct zram *zram, const char *algo)
{
struct zram_compressor *comp;
size_t max_size = 0;
const char *best_algo = NULL;
// 1. 计算最佳压缩算法
if (strcmp(algo, "lz4") == 0) {
// LZ4 速度快,但压缩率低
comp = zram_get_compressor(zram, "lz4");
if (comp->compress(zram->comp, "test", "test", 4, &max_size) == 0) {
best_algo = "lz4";
}
} else if (strcmp(algo, "lz4hc") == 0) {
// LZ4HC 压缩率高,但速度慢
comp = zram_get_compressor(zram, "lz4hc");
if (comp->compress(zram->comp, "test", "test", 4, &max_size) == 0) {
best_algo = "lz4hc";
}
} else if (strcmp(algo, "zstd") == 0) {
// ZSTD 压缩率高,速度中等
comp = zram_get_compressor(zram, "zstd");
if (comp->compress(zram->comp, "test", "test", 4, &max_size) == 0) {
best_algo = "zstd";
}
}
// 2. 设置最佳算法
if (best_algo) {
zram_set_algo(zram, best_algo);
return 0;
}
// 3. 默认使用 LZO
zram_set_algo(zram, "lzo");
return 0;
}
/**
* @brief ZRAM 自适应压缩策略。
* @param zram ZRAM 设备指针
* @param size 压缩大小
* @return 压缩策略
*/
int zram_adaptive_compression(struct zram *zram, size_t size)
{
// 1. 根据大小选择压缩策略
if (size < 256) {
// 小数据块,使用 LZ4 快速压缩
zram_set_algo(zram, "lz4");
return 0;
} else if (size < 4096) {
// 中等数据块,使用 ZSTD
zram_set_algo(zram, "zstd");
return 0;
} else {
// 大数据块,使用 LZ4HC 高压缩
zram_set_algo(zram, "lz4hc");
return 0;
}
}
10.2.4 F2FS 文件系统优化 (代码出处: fs/f2fs/)
// 代码出处: fs/f2fs/sysfs.c
/**
* @brief F2FS 自适应 I/O 调度。
* @param sb 超级块指针
* @param readahead 预读大小
* @param sync 是否同步
* @return 0 成功,负数错误
*/
int f2fs_adaptive_io_schedule(struct super_block *sb, size_t readahead, int sync)
{
struct f2fs_sb_info *sbi = F2FS_SB(sb);
int ret = 0;
// 1. 检查 I/O 负载
if (sbi->io_load > 50) {
// 高负载时,减少预读
readahead = readahead / 2;
if (readahead < 32) {
readahead = 32;
}
}
// 2. 根据同步/异步调整 I/O 策略
if (sync) {
// 同步 I/O 优先级高
sbi->io_priority = 100;
ret = f2fs_submit_io(sbi, F2FS_WRITE, readahead, sync);
} else {
// 异步 I/O 优先级低
sbi->io_priority = 50;
ret = f2fs_submit_io(sbi, F2FS_READ, readahead, sync);
}
return ret;
}
/**
* @brief F2FS 垃圾回收优化。
* @param sb 超级块指针
* @return 0 成功,负数错误
*/
int f2fs_optimize_gc(struct super_block *sb)
{
struct f2fs_sb_info *sbi = F2FS_SB(sb);
// 1. 检查空闲空间
if (sbi->free_segments < 10) {
// 空闲空间不足,启动紧急 GC
return f2fs_urgent_gc(sbi);
}
// 2. 检查 GC 触发阈值
if (sbi->need_gc > 100) {
// 触发 GC
return f2fs_trigger_gc(sbi);
}
// 3. 定期 GC
if (sbi->gc_interval > 1000) {
// 启动定期 GC
return f2fs_periodic_gc(sbi);
}
return 0;
}
10.2.5 唤醒源管理优化 (代码出处: drivers/base/power/wakeup.c)
// 代码出处: drivers/base/power/wakeup.c
/**
* @brief 唤醒源聚合优化。
* @param ws 唤醒源指针
* @return 0 成功,负数错误
*/
int wakeup_source_aggregate(struct wakeup_source *ws)
{
struct wakeup_source *tmp;
unsigned long last_wakeup = 0;
// 1. 查找相近的唤醒源
list_for_each_entry(tmp, &wakeup_source_list, list) {
if (tmp == ws) {
continue;
}
if (abs(tmp->last_wakeup_time - ws->last_wakeup_time) < 1000) {
// 时间相近,合并
last_wakeup = max(tmp->last_wakeup_time, ws->last_wakeup_time);
ws->last_wakeup_time = last_wakeup;
ws->wakeup_count += tmp->wakeup_count;
// 删除重复的唤醒源
list_del(&tmp->list);
kfree(tmp);
}
}
return 0;
}
/**
* @brief 唤醒源优先级优化。
* @param ws 唤醒源指针
* @return 优先级值
*/
int wakeup_source_priority(struct wakeup_source *ws)
{
int priority = 0;
// 1. 根据唤醒源类型计算优先级
if (strcmp(ws->name, "power_button") == 0) {
priority = 100; // 电源键最高优先级
} else if (strcmp(ws->name, "touchscreen") == 0) {
priority = 90; // 触摸屏高优先级
} else if (strcmp(ws->name, "rtc") == 0) {
priority = 50; // RTC 中等优先级
} else if (strcmp(ws->name, "usb") == 0) {
priority = 30; // USB 低优先级
} else {
priority = 10; // 其他唤醒源
}
// 2. 根据唤醒次数调整优先级
if (ws->wakeup_count > 100) {
priority += 20;
}
return priority;
}
10.3 软件设计模式树形分析
Android 内核优化设计模式 ├── 工厂模式 (Factory Pattern) │ ├── binder_prealloc_transaction_pool():预分配事务池 │ └── zram_select_best_algorithm():创建压缩算法实例 ├── 适配器模式 (Adapter Pattern) │ ├── lowmem_batch_kill():适配杀进程策略 │ └── wakeup_source_aggregate():适配唤醒源管理 ├── 策略模式 (Strategy Pattern) │ ├── lowmem_kill_priority():杀进程优先级策略 │ ├── zram_adaptive_compression():自适应压缩策略 │ └── f2fs_adaptive_io_schedule():自适应 I/O 调度策略 ├── 观察者模式 (Observer Pattern) │ ├── f2fs_optimize_gc():观察 GC 触发条件 │ └── wakeup_source_priority():观察唤醒优先级 └── 模板方法模式 (Template Method Pattern) └── binder_adjust_thread_pool():定义了线程池调整的标准流程
10.4 Android 内核优化调试核心难点
10.4.1 Binder 事务队列阻塞
现象:Binder 事务队列过长,系统响应缓慢。
原因:
-
事务池太小。
-
线程池不足。
-
oneway 事务过多。
调试方法:
-
使用
dumpsys binder查看事务队列状态。 -
调整
binder_prealloc_transaction_pool池大小。 -
优化 oneway 事务批处理。
10.4.2 LMK 杀进程不当
现象:LMK 杀死重要进程,导致系统不稳定。
原因:
-
oom_score_adj配置错误。 -
杀进程策略过于激进。
-
进程优先级未正确设置。
调试方法:
-
检查
/proc/pid/oom_score_adj查看进程优先级。 -
调整
lowmem_kill_priority评分策略。 -
增加杀进程延迟。
10.4.3 ZRAM 压缩率过低
现象:ZRAM 压缩率低,内存利用率不足。
原因:
-
压缩算法选择不当。
-
数据块大小不匹配。
-
压缩策略配置错误。
调试方法:
-
使用
cat /sys/block/zram0/comp_algorithm查看压缩算法。 -
调整
zram_adaptive_compression策略。 -
增加 ZRAM 大小。
10.5 与其他模块的协同
| 模块 | 协同方式 | 调试关键点 |
|---|---|---|
| Binder | 提供 IPC 优化 | 事务池大小、线程池管理 |
| LMK | 提供内存回收优化 | 杀进程策略、进程优先级 |
| ZRAM | 提供压缩优化 | 算法选择、压缩策略 |
| F2FS | 提供文件系统优化 | I/O 调度、GC 策略 |
| 唤醒源管理 | 提供电源优化 | 唤醒聚合、优先级管理 |
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐

所有评论(0)