C语言 了解一下回调函数(钩子函数)的使用
回忆下回调函数使用
 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)进行频率调整。
函数原型
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 编号 | 
| 
 | 输出参数:用于存储注册后的回调函数指针(通常指向 | 
| 
 | 输入参数:调度器提供的util update 回调函数(如 | 
✅ 调用后,
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
//
// 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
//
// Created by wyd on 2025/10/30.
//#include "test.h"#define NR_CPUS 4  // 假设 4 核 CPUstruct 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
#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 修改后就 会触发回调函数起作用
