回忆下回调函数使用
cs
static int sugov_start(struct cpufreq_policy *policy)
871 {
872 struct sugov_policy *sg_policy = policy->governor_data;
873 void (*uu)(struct update_util_data *data, u64 time, unsigned int flags);
874 unsigned int cpu;
875
876 sg_policy->freq_update_delay_ns = sg_policy->tunables->rate_limit_us * NSEC_PER_USEC;
877 sg_policy->last_freq_update_time = 0;
878 sg_policy->next_freq = 0;
879 sg_policy->work_in_progress = false;
880 sg_policy->limits_changed = false;
881 sg_policy->cached_raw_freq = 0;
882
883 sg_policy->need_freq_update = cpufreq_driver_test_flags(CPUFREQ_NEED_UPDATE_LIMITS);
884
885 if (policy_is_shared(policy))
886 uu = sugov_update_shared;
887 else if (policy->fast_switch_enabled && cpufreq_driver_has_adjust_perf())
888 uu = sugov_update_single_perf;
889 else
890 uu = sugov_update_single_freq;
891
892 for_each_cpu(cpu, policy->cpus) {
893 struct sugov_cpu *sg_cpu = &per_cpu(sugov_cpu, cpu);
894
895 memset(sg_cpu, 0, sizeof(*sg_cpu));
896 sg_cpu->cpu = cpu;
897 sg_cpu->sg_policy = sg_policy;
898 cpufreq_add_update_util_hook(cpu, &sg_cpu->update_util, uu);
899 }
900 return 0;
901 }
cpufreq_add_update_util_hook(cpu,&sg_cpu->update_util, uu)是 Linux 内核中 连接调度器(scheduler)与 CPUFreq 调频子系统 的关键机制。为指定 CPU 注册一个"利用率更新回调函数",使得每当调度器感知到任务负载变化时,能立即通知 CPUFreq governor(如 schedutil)进行频率调整。
函数原型
cs
void cpufreq_add_update_util_hook(int cpu,
struct update_util_data *data,
void (*func)(struct update_util_data *, u64, unsigned int));
在你的调用中:
cpufreq_add_update_util_hook(cpu, &sg_cpu->update_util, uu);
&sg_cpu->update_util:类型是struct update_util_data *,作为输出参数;- uu:是一个 函数指针 ,类型为 void (*)(struct update_util_data *, u64, unsigned int),这才是真正的回调函数 。
🔍 一、函数参数解析
cpufreq_add_update_util_hook(cpu, &sg_cpu->update_util, uu);
cpu |
目标 CPU 编号 |
&sg_cpu->update_util |
输出参数 :用于存储注册后的回调函数指针(通常指向sugov_update_util()) |
uu |
输入参数 :调度器提供的util update 回调函数 (如sugov_update_util()的地址) |
✅ 调用后,
sg_cpu->update_util会被赋值为一个可调用的函数指针。
内核中 cpufreq_add_update_util_hook() 做了什么?
void cpufreq_add_update_util_hook(int cpu, struct update_util_data *data, 31 void (*func)(struct update_util_data *data, u64 time, 32 unsigned int flags)) 33 { 34 if (WARN_ON(!data || !func)) 35 return; 36 37 if (WARN_ON(per_cpu(cpufreq_update_util_data, cpu))) 38 return; 39 40 data->func = func; // ← 把 uu 赋值给 data->func 41 rcu_assign_pointer(per_cpu(cpufreq_update_util_data, cpu), data); 42 } 43 EXPORT_SYMBOL_GPL(cpufreq_add_update_util_hook);
调度器如何触发回调?
// 在 kernel/sched/core.c 中
if (rq->update_util)
rq->update_util(rq->update_util_data, time, flags);
二. 自己写一个钩子函数看下具体使用
2.1 test.h
cs
//
// Created by wyd on 2025/10/30.
//
#ifndef TEST1019_TEST_H
#define TEST1019_TEST_H
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
/* 利用率更新标志 */
#define UTIL_UPDATE_IOWAIT (1U << 0) // I/O 唤醒
#define UTIL_UPDATE_MIGRATION (1U << 1) // 任务迁移
#define UTIL_UPDATE_IDLE (1U << 2) // CPU 空闲
/* 钩子回调函数类型 */
typedef void (*util_update_fn_t)(void *data, uint64_t time, unsigned int flags);
/* 钩子数据结构(类似 struct update_util_data) */
struct util_update_data {
util_update_fn_t func; // 回调函数指针
void *private; // 私有数据(如 sugov_cpu*)
};
/* 每个 CPU 的运行队列模拟 */
struct runqueue {
int cpu;
struct util_update_data *update_util; // 钩子指针
};
/* 全局 CPU 运行队列数组(简化版) */
extern struct runqueue rq_table[];
/* 注册钩子 */
void util_hook_add(int cpu, struct util_update_data *data,
util_update_fn_t fn, void *private);
/* 注销钩子 */
void util_hook_remove(int cpu);
/* 触发钩子(由调度器调用) */
void util_hook_trigger(int cpu, uint64_t time, unsigned int flags);
#endif //TEST1019_TEST_H
2.2 test.c
cs
//
// Created by wyd on 2025/10/30.
//
#include "test.h"
#define NR_CPUS 4 // 假设 4 核 CPU
struct runqueue rq_table[NR_CPUS];
void util_hook_add(int cpu, struct util_update_data *data,
util_update_fn_t fn, void *private)
{
if (cpu < 0 || cpu >= NR_CPUS) {
fprintf(stderr, "Invalid CPU %d\n", cpu);
return;
}
data->func = fn;
data->private = private;
rq_table[cpu].update_util = data;
rq_table[cpu].cpu = cpu;
printf("Hook registered on CPU %d\n", cpu);
}
void util_hook_trigger(int cpu, uint64_t time, unsigned int flags)
{
struct runqueue *rq = &rq_table[cpu];
if (rq->update_util && rq->update_util->func) {
rq->update_util->func(rq->update_util->private, time, flags);
}
}
void util_hook_remove(int cpu)
{
if (cpu < 0 || cpu >= NR_CPUS) return;
rq_table[cpu].update_util = NULL;
printf("Hook removed from CPU %d\n", cpu);
}
2.3 main.c
cs
#include "test.h"
/* 模拟一个 governor 的 per-CPU 数据 */
struct my_gov_cpu {
int cpu;
unsigned long util;
bool iowait_pending;
};
/* 回调函数:当调度器通知利用率更新时调用 */
static void my_gov_update_util(void *data, uint64_t time, unsigned int flags)
{
struct my_gov_cpu *gov_cpu = (struct my_gov_cpu *)data;
printf("[CPU %d] Update at time %llu, flags=0x%x\n",
gov_cpu->cpu, (unsigned long long)time, flags);
if (flags & UTIL_UPDATE_IOWAIT) {
printf(" → I/O wake-up detected! Boosting frequency.\n");
gov_cpu->iowait_pending = true;
}
// 这里可以调用频率调整逻辑
// set_cpu_frequency(gov_cpu->cpu, new_freq);
}
int main() {
const int cpu = 0;
struct my_gov_cpu gov_cpu = {.cpu = cpu};
struct util_update_data hook_data;
/* 1. 注册钩子 */
util_hook_add(cpu, &hook_data, my_gov_update_util, &gov_cpu);
/* 2. 模拟调度器事件 */
uint64_t now = time(NULL) * 1000000000ULL; // 纳秒级时间
// 普通任务唤醒
util_hook_trigger(cpu, now, 0);
// I/O 唤醒
util_hook_trigger(cpu, now + 1000000, UTIL_UPDATE_IOWAIT);
// 任务迁移
util_hook_trigger(cpu, now + 2000000, UTIL_UPDATE_MIGRATION);
/* 3. 注销钩子 */
util_hook_remove(cpu);
return 0;
}
2.4 输出结果
Hook registered on CPU 0
CPU 0\] Update at time 1761835217000000000, flags=0x0 \[CPU 0\] Update at time 1761835217001000000, flags=0x1 → I/O wake-up detected! Boosting frequency. \[CPU 0\] Update at time 1761835217002000000, flags=0x2 Hook removed from CPU 0
flag 修改后就 会触发回调函数起作用