目前的cpu频率越来越高,但实际上大部分场景并不需要cpu一直工作在最高频率。
因为cpu工作频率越高,功耗越大,尤其是对手机等移动设备,更需要降低功耗,
延长手机电池使用时间。
在linux中,通过cpufreq来实现频率的动态调节。
1. 先直观看下cpufreq提供的功能。
在/sys/devices/system/cpu/中定义了相关的接口,
root:/sys/devices/system/cpu # ll
drwxr-xr-x root root 1970-01-22 03:05 cpu0
drwxr-xr-x root root 2017-01-21 10:26 cpu1
drwxr-xr-x root root 2017-01-21 10:26 cpu2
drwxr-xr-x root root 2017-01-21 10:26 cpu3
drwxr-xr-x root root 2017-01-21 10:26 cpu4
drwxr-xr-x root root 2017-01-21 10:26 cpu5
drwxr-xr-x root root 2017-01-21 10:26 cpu6
drwxr-xr-x root root 2017-01-21 10:26 cpu7
drwxr-xr-x root root 1970-01-22 03:05 cpufreq
drwxr-xr-x root root 1970-01-22 03:05 cpuidle
-r--r--r-- root root 4096 1970-01-22 03:05 kernel_max
-r--r--r-- root root 4096 1970-01-22 03:05 modalias
-r--r--r-- root root 4096 1970-01-22 03:05 offline
-r--r--r-- root root 4096 1970-01-22 03:05 online
-r--r--r-- root root 4096 1970-01-22 03:05 possible
drwxr-xr-x root root 1970-01-22 03:05 power
-r--r--r-- root root 4096 1970-01-22 03:05 present
-rw-r--r-- root root 4096 1970-01-22 03:05 uevent
其中cpu0-cpu7 几个目录单独定义了每个cpu core调频信息。
cpu0下面的cpufreq里面cpuinfo_XX表示cpu0硬件最大,最小和当前频率,
interactive表示调节器用的是interactive模式参数信息,scaling_XX表示调频时
软件可以设定的信息。
root:/sys/devices/system/cpu/cpu0/cpufreq # ll
-r--r--r-- root root 4096 1970-01-22 03:05 affected_cpus
-r-------- root root 4096 1970-01-22 03:05 cpuinfo_cur_freq
-r--r--r-- root root 4096 1970-01-22 03:05 cpuinfo_max_freq
-r--r--r-- root root 4096 1970-01-22 03:05 cpuinfo_min_freq
-r--r--r-- root root 4096 1970-01-22 03:05 cpuinfo_transition_latency
drwxr-xr-x root root 2017-01-20 12:40 interactive
-r--r--r-- root root 4096 1970-01-22 03:05 related_cpus
-r--r--r-- root root 4096 1970-01-22 03:05 scaling_available_frequencies
-r--r--r-- root root 4096 1970-01-22 03:05 scaling_available_governors
-r--r--r-- root root 4096 1970-01-22 03:05 scaling_cur_freq
-r--r--r-- root root 4096 1970-01-22 03:05 scaling_driver
-rw-r--r-- root root 4096 2017-01-20 12:40 scaling_governor
-rw-rw---- system system 4096 1970-01-22 03:05 scaling_max_freq
-rw-rw-r-- system system 4096 2017-01-20 12:40 scaling_min_freq
-rw-r--r-- root root 4096 1970-01-22 03:05 scaling_setspeed
drwxr-xr-x root root 1970-01-22 03:05 stats
2. 内核代码实现
代码涉及的数据结构:
//调频策略,定义调频时频率信息,具体调节器的选择等信息
//每个cpu都有自己的policy,因为不同的cpu能力不同,调频时限制条件也会有差别
struct cpufreq_policy {
/* CPUs sharing clock, require sw coordination */
cpumask_var_t cpus; /* Online CPUs only 该policy下online cpus*/
cpumask_var_t related_cpus; /* Online + Offline CPUs 该policy所管理的所有cpus*/
unsigned int shared_type; /* ACPI: ANY or ALL affected CPUs
should set cpufreq */
unsigned int cpu; /* cpu nr of CPU managing this policy 管理该policy的cpu编号 */
unsigned int last_cpu; /* cpu nr of previous CPU that managed
* this policy 上次管理该policy的cpu编号,cpu 热插拔时使用*/
struct cpufreq_cpuinfo cpuinfo;/* cpu硬件信息 */
//最大、最小、当前频率,软件调频参数信息
unsigned int min; /* in kHz */
unsigned int max; /* in kHz */
unsigned int cur; /* in kHz, only needed if cpufreq
* governors are used */
/*
该变量可以取以下两个值:CPUFREQ_POLICY_POWERSAVE和CPUFREQ_POLICY_PERFORMANCE,
该变量只有当调频驱动支持setpolicy回调函数的时候有效,
这时候由驱动根据policy变量的值来决定系统的工作频率或状态。
如果调频驱动(cpufreq_driver)支持target回调,则频率由相应的governor来决定
*/
unsigned int policy; /* see above */
/*
指向该policy当前使用的cpufreq_governor结构和它的上下文数据,governor是实现该policy的关键所在,调频策略的逻辑由governor实现
*/
struct cpufreq_governor *governor; /* see below */
void *governor_data;//其实是每种governor的tunables
bool governor_enabled; /* governor start/stop flag */
/*
有时在中断上下文中需要更新policy,需要利用该工作队列把实际的工作移到稍后的进程上下文中执行。
*/
struct work_struct update; /* if update_policy() needs to be
* called, but you're in IRQ context */
/*
有时候因为特殊的原因需要修改policy的参数,比如溫度过高时,最大可允许的运行频率可能会被降低,为了在适当的时候恢复原有的运行参数,需要使用user_policy保存原始的参数(min,max,policy,governor)
*/
struct cpufreq_real_policy user_policy;
struct list_head policy_list;//系统使用的policy 列表
struct kobject kobj;
struct completion kobj_unregister;
/*
* The rules for this semaphore:
* - Any routine that wants to read from the policy structure will
* do a down_read on this semaphore.
* - Any routine that will write to the policy structure and/or may take away
* the policy altogether (eg. CPU hotplug), will hold this lock in write
* mode before doing so.
*
* Additional rules:
* - Lock should not be held across
* __cpufreq_governor(data, CPUFREQ_GOV_POLICY_EXIT);
*/
struct rw_semaphore rwsem;
};
//governor:调节器,调节策略的执行者,主要有函数governor实现具体调频工作
struct cpufreq_governor {
char name[CPUFREQ_NAME_LEN];
int initialized;//初始化标志
/*
指向一个回调函数,CPUFreq Core会在不同的阶段调用该回调函数,用于该governor的启动、停止、初始化、退出动作
*/
int (*governor) (struct cpufreq_policy *policy,
unsigned int event);
ssize_t (*show_setspeed) (struct cpufreq_policy *policy,
char *buf);
int (*store_setspeed) (struct cpufreq_policy *policy,
unsigned int freq);
unsigned int max_transition_latency; /* HW must be able to switch to
next freq faster than this value in nano secs or we
will fallback to performance governor */
struct list_head governor_list;//所有注册的governor都会利用该字段链接在一个全局链表中,以供系统查询和使用
struct module *owner;
};
关于governor,目前提供了几种不同的实现,
performance:cpu一直运行在最高频率;
powersave:cpu一直运行在最低频率;
ondemand:按需快速动态调整CPU频率,一有cpu计算量的任务,就会立即达到最大频率运行,等执行完毕就立即回到最低频率(阙值为 95%)
conservative:按需快速动态调整CPU频率,一有cpu计算量的任务,就会立即达到最大频率运行,等执行完毕就立即回到最低频率(阙值为 75%)
userspace:运行于用户指定的频率
interactive:目前Android都是采用此方式,定时计算cpu负载,进行调频处理
//调频驱动数据结构,根据不同cpu厂商具体实现,主要用来设定硬件cpu工作频率
struct cpufreq_driver {
char name[CPUFREQ_NAME_LEN];//驱动名
u8 flags;//
/* needed by all drivers */
int (*init) (struct cpufreq_policy *policy);//驱动初始化
int (*verify) (struct cpufreq_policy *policy);//检查policy的参数是否被驱动支持
/*
回调函数,驱动必须实现这两个函数中的其中一个,
如果不支持通过governor选择合适的运行频率,则实现setpolicy回调函数,
这样系统只能支持CPUFREQ_POLICY_POWERSAVE和CPUFREQ_POLICY_PERFORMANCE这两种工作策略。
反之,实现target回调函数,通过target回调设定governor所需要的频率
*/
int (*setpolicy) (struct cpufreq_policy *policy);//
int (*target) (struct cpufreq_policy *policy, /* Deprecated */
unsigned int target_freq,
unsigned int relation);
//target或者target_index设定目标频率
int (*target_index) (struct cpufreq_policy *policy,
unsigned int index);
/*获取当前工作频率 */
unsigned int (*get) (unsigned int cpu);
/* optional */
int (*bios_limit) (int cpu, unsigned int *limit);
int (*exit) (struct cpufreq_policy *policy);
int (*suspend) (struct cpufreq_policy *policy);
int (*resume) (struct cpufreq_policy *policy);
struct freq_attr **attr;
};
代码中cpufreq.c实现了调频主体框架,cpufreq_governer.c 抽象出来所有governor公共接口,
具体实现由每种governor实现,cpufreq_driver 根据不同芯片实现调频相关的硬件操作。
后续我们以Android采用的interactive来讲解代码流程。
因为cpu工作频率越高,功耗越大,尤其是对手机等移动设备,更需要降低功耗,
延长手机电池使用时间。
在linux中,通过cpufreq来实现频率的动态调节。
1. 先直观看下cpufreq提供的功能。
在/sys/devices/system/cpu/中定义了相关的接口,
root:/sys/devices/system/cpu # ll
drwxr-xr-x root root 1970-01-22 03:05 cpu0
drwxr-xr-x root root 2017-01-21 10:26 cpu1
drwxr-xr-x root root 2017-01-21 10:26 cpu2
drwxr-xr-x root root 2017-01-21 10:26 cpu3
drwxr-xr-x root root 2017-01-21 10:26 cpu4
drwxr-xr-x root root 2017-01-21 10:26 cpu5
drwxr-xr-x root root 2017-01-21 10:26 cpu6
drwxr-xr-x root root 2017-01-21 10:26 cpu7
drwxr-xr-x root root 1970-01-22 03:05 cpufreq
drwxr-xr-x root root 1970-01-22 03:05 cpuidle
-r--r--r-- root root 4096 1970-01-22 03:05 kernel_max
-r--r--r-- root root 4096 1970-01-22 03:05 modalias
-r--r--r-- root root 4096 1970-01-22 03:05 offline
-r--r--r-- root root 4096 1970-01-22 03:05 online
-r--r--r-- root root 4096 1970-01-22 03:05 possible
drwxr-xr-x root root 1970-01-22 03:05 power
-r--r--r-- root root 4096 1970-01-22 03:05 present
-rw-r--r-- root root 4096 1970-01-22 03:05 uevent
其中cpu0-cpu7 几个目录单独定义了每个cpu core调频信息。
cpu0下面的cpufreq里面cpuinfo_XX表示cpu0硬件最大,最小和当前频率,
interactive表示调节器用的是interactive模式参数信息,scaling_XX表示调频时
软件可以设定的信息。
root:/sys/devices/system/cpu/cpu0/cpufreq # ll
-r--r--r-- root root 4096 1970-01-22 03:05 affected_cpus
-r-------- root root 4096 1970-01-22 03:05 cpuinfo_cur_freq
-r--r--r-- root root 4096 1970-01-22 03:05 cpuinfo_max_freq
-r--r--r-- root root 4096 1970-01-22 03:05 cpuinfo_min_freq
-r--r--r-- root root 4096 1970-01-22 03:05 cpuinfo_transition_latency
drwxr-xr-x root root 2017-01-20 12:40 interactive
-r--r--r-- root root 4096 1970-01-22 03:05 related_cpus
-r--r--r-- root root 4096 1970-01-22 03:05 scaling_available_frequencies
-r--r--r-- root root 4096 1970-01-22 03:05 scaling_available_governors
-r--r--r-- root root 4096 1970-01-22 03:05 scaling_cur_freq
-r--r--r-- root root 4096 1970-01-22 03:05 scaling_driver
-rw-r--r-- root root 4096 2017-01-20 12:40 scaling_governor
-rw-rw---- system system 4096 1970-01-22 03:05 scaling_max_freq
-rw-rw-r-- system system 4096 2017-01-20 12:40 scaling_min_freq
-rw-r--r-- root root 4096 1970-01-22 03:05 scaling_setspeed
drwxr-xr-x root root 1970-01-22 03:05 stats
2. 内核代码实现
代码涉及的数据结构:
//调频策略,定义调频时频率信息,具体调节器的选择等信息
//每个cpu都有自己的policy,因为不同的cpu能力不同,调频时限制条件也会有差别
struct cpufreq_policy {
/* CPUs sharing clock, require sw coordination */
cpumask_var_t cpus; /* Online CPUs only 该policy下online cpus*/
cpumask_var_t related_cpus; /* Online + Offline CPUs 该policy所管理的所有cpus*/
unsigned int shared_type; /* ACPI: ANY or ALL affected CPUs
should set cpufreq */
unsigned int cpu; /* cpu nr of CPU managing this policy 管理该policy的cpu编号 */
unsigned int last_cpu; /* cpu nr of previous CPU that managed
* this policy 上次管理该policy的cpu编号,cpu 热插拔时使用*/
struct cpufreq_cpuinfo cpuinfo;/* cpu硬件信息 */
//最大、最小、当前频率,软件调频参数信息
unsigned int min; /* in kHz */
unsigned int max; /* in kHz */
unsigned int cur; /* in kHz, only needed if cpufreq
* governors are used */
/*
该变量可以取以下两个值:CPUFREQ_POLICY_POWERSAVE和CPUFREQ_POLICY_PERFORMANCE,
该变量只有当调频驱动支持setpolicy回调函数的时候有效,
这时候由驱动根据policy变量的值来决定系统的工作频率或状态。
如果调频驱动(cpufreq_driver)支持target回调,则频率由相应的governor来决定
*/
unsigned int policy; /* see above */
/*
指向该policy当前使用的cpufreq_governor结构和它的上下文数据,governor是实现该policy的关键所在,调频策略的逻辑由governor实现
*/
struct cpufreq_governor *governor; /* see below */
void *governor_data;//其实是每种governor的tunables
bool governor_enabled; /* governor start/stop flag */
/*
有时在中断上下文中需要更新policy,需要利用该工作队列把实际的工作移到稍后的进程上下文中执行。
*/
struct work_struct update; /* if update_policy() needs to be
* called, but you're in IRQ context */
/*
有时候因为特殊的原因需要修改policy的参数,比如溫度过高时,最大可允许的运行频率可能会被降低,为了在适当的时候恢复原有的运行参数,需要使用user_policy保存原始的参数(min,max,policy,governor)
*/
struct cpufreq_real_policy user_policy;
struct list_head policy_list;//系统使用的policy 列表
struct kobject kobj;
struct completion kobj_unregister;
/*
* The rules for this semaphore:
* - Any routine that wants to read from the policy structure will
* do a down_read on this semaphore.
* - Any routine that will write to the policy structure and/or may take away
* the policy altogether (eg. CPU hotplug), will hold this lock in write
* mode before doing so.
*
* Additional rules:
* - Lock should not be held across
* __cpufreq_governor(data, CPUFREQ_GOV_POLICY_EXIT);
*/
struct rw_semaphore rwsem;
};
//governor:调节器,调节策略的执行者,主要有函数governor实现具体调频工作
struct cpufreq_governor {
char name[CPUFREQ_NAME_LEN];
int initialized;//初始化标志
/*
指向一个回调函数,CPUFreq Core会在不同的阶段调用该回调函数,用于该governor的启动、停止、初始化、退出动作
*/
int (*governor) (struct cpufreq_policy *policy,
unsigned int event);
ssize_t (*show_setspeed) (struct cpufreq_policy *policy,
char *buf);
int (*store_setspeed) (struct cpufreq_policy *policy,
unsigned int freq);
unsigned int max_transition_latency; /* HW must be able to switch to
next freq faster than this value in nano secs or we
will fallback to performance governor */
struct list_head governor_list;//所有注册的governor都会利用该字段链接在一个全局链表中,以供系统查询和使用
struct module *owner;
};
关于governor,目前提供了几种不同的实现,
performance:cpu一直运行在最高频率;
powersave:cpu一直运行在最低频率;
ondemand:按需快速动态调整CPU频率,一有cpu计算量的任务,就会立即达到最大频率运行,等执行完毕就立即回到最低频率(阙值为 95%)
conservative:按需快速动态调整CPU频率,一有cpu计算量的任务,就会立即达到最大频率运行,等执行完毕就立即回到最低频率(阙值为 75%)
userspace:运行于用户指定的频率
interactive:目前Android都是采用此方式,定时计算cpu负载,进行调频处理
//调频驱动数据结构,根据不同cpu厂商具体实现,主要用来设定硬件cpu工作频率
struct cpufreq_driver {
char name[CPUFREQ_NAME_LEN];//驱动名
u8 flags;//
/* needed by all drivers */
int (*init) (struct cpufreq_policy *policy);//驱动初始化
int (*verify) (struct cpufreq_policy *policy);//检查policy的参数是否被驱动支持
/*
回调函数,驱动必须实现这两个函数中的其中一个,
如果不支持通过governor选择合适的运行频率,则实现setpolicy回调函数,
这样系统只能支持CPUFREQ_POLICY_POWERSAVE和CPUFREQ_POLICY_PERFORMANCE这两种工作策略。
反之,实现target回调函数,通过target回调设定governor所需要的频率
*/
int (*setpolicy) (struct cpufreq_policy *policy);//
int (*target) (struct cpufreq_policy *policy, /* Deprecated */
unsigned int target_freq,
unsigned int relation);
//target或者target_index设定目标频率
int (*target_index) (struct cpufreq_policy *policy,
unsigned int index);
/*获取当前工作频率 */
unsigned int (*get) (unsigned int cpu);
/* optional */
int (*bios_limit) (int cpu, unsigned int *limit);
int (*exit) (struct cpufreq_policy *policy);
int (*suspend) (struct cpufreq_policy *policy);
int (*resume) (struct cpufreq_policy *policy);
struct freq_attr **attr;
};
代码中cpufreq.c实现了调频主体框架,cpufreq_governer.c 抽象出来所有governor公共接口,
具体实现由每种governor实现,cpufreq_driver 根据不同芯片实现调频相关的硬件操作。
后续我们以Android采用的interactive来讲解代码流程。