在上一篇文章中,介绍了cpufreq的core层,core提供了cpufreq系统的初始化,公共数据结构的建立以及对cpufreq中其它子部件提供注册功能。core的最核心功能是对policy的管理,一个policy通过cpufreq_policy结构中的governor字段,和某个governor相关联,本章的内容正是要对governor进行讨论。

/*****************************************************************************************************/

原文链接:https://blog.csdn.net/DroidPhone/article/details/9532999

/*****************************************************************************************************/

通过前面两篇文章的介绍,我们知道,governor的作用是:检测系统的负载状况,然后根据当前的负载,选择出某个可供使用的工作频率,然后把该工作频率传递给cpufreq_driver,完成频率的动态调节。内核默认提供了5种governor供我们使用,在之前的内核版本中,每种governor几乎是独立的代码,它们各自用自己的方式实现对系统的负载进行监测,很多时候,检测的逻辑其实是很相似的,各个governor最大的不同之处其实是根据检测的结果,选择合适频率的策略。所以,为了减少代码的重复,在我现在分析的内核版本中(3.10.0),一些公共的逻辑代码被单独抽象出来,单独用一个文件来实现:/drivers/cpufreq/cpufreq_governor.c,而各个具体的governor则分别有自己的代码文件,如:cpufreq_ondemand.c,cpufreq_performance.c。下面我们先从公共部分讨论。

1. 数据结构


cpu_dbs_common_info 该结构把对计算cpu负载需要使用到的一些辅助变量整合在了一起,通常,每个cpu都需要一个cpu_dbs_common_info结构体,该结构体中的成员会在governor的生命周期期间进行传递,以用于统计当前cpu的负载,它的定义如下:

/* Per cpu structures */
struct cpu_dbs_common_info {int cpu;u64 prev_cpu_idle;u64 prev_cpu_wall;u64 prev_cpu_nice;struct cpufreq_policy *cur_policy;struct delayed_work work;struct mutex timer_mutex;ktime_t time_stamp;
};
  • cpu 与该结构体相关联的cpu编号。
  • prev_cpu_idle 上一次统计时刻该cpu停留在idle状态的总时间。
  • prev_cpu_wall 上一次统计时刻对应的总工作时间。
  • cur_policy 指向该cpu所使用的cpufreq_policy结构。
  • work 工作队列,该工作队列会被定期地触发,然后定期地进行负载的更新和统计工作。

dbs缩写,实际是:demand based switching,通常,因为cpu_dbs_common_info只包含了经过抽象后的公共部分,所以,各个governor会自己定义的一个包含cpu_dbs_common_info的自定义结构,例如对于ondemand,他会定义:

struct od_cpu_dbs_info_s {struct cpu_dbs_common_info cdbs;struct cpufreq_frequency_table *freq_table;unsigned int freq_lo;unsigned int freq_lo_jiffies;unsigned int freq_hi_jiffies;unsigned int rate_mult;unsigned int sample_type:1;
};

而对于Conservative,他的定义如下:

struct cs_cpu_dbs_info_s {struct cpu_dbs_common_info cdbs;unsigned int down_skip;unsigned int requested_freq;unsigned int enable:1;
};

把它理解为类似于C++语言的基类和子类的概念就是了。

common_dbs_data 各个独立的governor,需要和governor的公共层交互,需要实现一套公共的接口,这个接口由common_dbs_data结构来提供:

struct common_dbs_data {/* Common across governors */#define GOV_ONDEMAND            0#define GOV_CONSERVATIVE        1int governor;struct attribute_group *attr_group_gov_sys; /* one governor - system */struct attribute_group *attr_group_gov_pol; /* one governor - policy *//* Common data for platforms that don't set have_governor_per_policy */struct dbs_data *gdbs_data;struct cpu_dbs_common_info *(*get_cpu_cdbs)(int cpu);void *(*get_cpu_dbs_info_s)(int cpu);void (*gov_dbs_timer)(struct work_struct *work);void (*gov_check_cpu)(int cpu, unsigned int load);int (*init)(struct dbs_data *dbs_data);void (*exit)(struct dbs_data *dbs_data);/* Governor specific ops, see below */void *gov_ops;
};

主要的字段意义如下:

  • governor 因为ondemand和conservative的实现部分有很多相似的地方,用该字段做一区分,可以设置为GOV_ONDEMAND或GOV_CONSERVATIVE的其中之一。

  • attr_group_gov_sys 该公共的sysfs属性组。

  • attr_group_gov_pol 各policy使用的属性组,有时候多个policy会使用同一个governor算法。

  • gdbs_data 通常,当没有设置have_governor_per_policy时,表示所有的policy使用了同一种governor,该字段指向该governor的dbs_data结构。

  • get_cpu_cdbs 回调函数,公共层用它取得对应cpu的cpu_dbs_common_info结构指针。

  • get_cpu_dbs_info_s 回调函数,公共层用它取得对应cpu的cpu_dbs_common_info_s的派生结构指针,例如:od_cpu_dbs_info_s,cs_cpu_dbs_info_s。

  • gov_dbs_timer 前面说过,cpu_dbs_common_info_s结构中有一个工作队列,该回调通常用作工作队列的工作函数。

  • gov_check_cpu 计算cpu负载的回调函数,通常会直接调用公共层提供的dbs_check_cpu函数完成实际的计算工作。

  • init 初始化回调,用于完成该governor的一些额外的初始化工作。

  • exit 回调函数,governor被移除时调用。

  • gov_ops 各个governor可以用该指针定义各自特有的一些操作接口。

dbs_data 该结构体通常由governor的公共层代码在governor的初始化阶段动态创建,该结构的一个最重要的字段就是cdata:一个common_dbs_data结构指针,另外,该结构还包含一些定义governor工作方式的一些调节参数。该结构的详细定义如下:

struct dbs_data {struct common_dbs_data *cdata;unsigned int min_sampling_rate;int usage_count;void *tuners;/* dbs_mutex protects dbs_enable in governor start/stop */struct mutex mutex;
};

几个主要的字段:

  • cdata 一个common_dbs_data结构指针,通常由具体governor的实现部分定义好,然后作为参数,通过公共层的API:cpufreq_governor_dbs,传递到公共层,cpufreq_governor_dbs函数在创建好dbs_data结构后,把该指针赋值给该字段。

  • min_sampling_rate 用于记录统计cpu负载的采样周期。

  • usage_count 当没有设置have_governor_per_policy时,意味着所有的policy采用同一个governor,该字段就是用来统计目前该governor被多少个policy引用。

  • tuners 指向governor的调节参数结构,不同的governor可以定义自己的tuner结构,公共层代码会在governor的初始化阶段调用common_dbs_data结构的init回调函数,governor的实现可以在init回调中初始化tuners字段。

如果设置了have_governor_per_policy,每个policy拥有各自独立的governor,也就是说,拥有独立的dbs_data结构,它会记录在cpufreq_policy结构的governor_data字段中,否则,如果没有设置have_governor_per_policy,多个policy共享一个governor,和同一个dbs_data结构关联,此时,dbs_data被赋值在common_dbs_data结构的gdbs_data字段中。

cpufreq_governor 这个结构在本系列文章的第一篇已经介绍过了,请参看Linux动态频率调节系统CPUFreq之一:概述。几个数据结构的关系如下图所示:

下面我们以ondemand这个系统已经实现的governor为例,说明一下如何实现一个governor。具体的代码请参看:/drivers/cpufreq/cpufreq_ondemand.c。

2. 定义一个governor


要实现一个governor,首先要定义一个cpufreq_governor结构,对于ondeman来说,它的定义如下:

struct cpufreq_governor cpufreq_gov_ondemand = {.name                   = "ondemand",.governor               = od_cpufreq_governor_dbs,.max_transition_latency = TRANSITION_LATENCY_LIMIT,.owner                  = THIS_MODULE,
};

其中,governor是这个结构的核心字段,cpufreq_governor注册后,cpufreq的核心层通过该字段操纵这个governor的行为,包括:初始化、启动、退出等工作。现在,该字段被设置为od_cpufreq_governor_dbs,我们看看它的实现:

static int od_cpufreq_governor_dbs(struct cpufreq_policy *policy,unsigned int event)
{return cpufreq_governor_dbs(policy, &od_dbs_cdata, event);
}

只是简单地调用了governor的公共层提供的API:cpufreq_governor_dbs,关于这个API,我们在后面会逐一进行展开,这里我们注意到参数:&od_dbs_cdata,正是我们前面讨论过得common_dbs_data结构,作为和governor公共层的接口,在这里它的定义如下:

static struct common_dbs_data od_dbs_cdata = {.governor = GOV_ONDEMAND,.attr_group_gov_sys = &od_attr_group_gov_sys,.attr_group_gov_pol = &od_attr_group_gov_pol,.get_cpu_cdbs = get_cpu_cdbs,.get_cpu_dbs_info_s = get_cpu_dbs_info_s,.gov_dbs_timer = od_dbs_timer,.gov_check_cpu = od_check_cpu,.gov_ops = &od_ops,.init = od_init,.exit = od_exit,
};

这里先介绍一下get_cpu_cdbs和get_cpu_dbs_info_s这两个回调,前面介绍cpu_dbs_common_info_s结构的时候已经说过,各个governor需要定义一个cpu_dbs_common_info_s结构的派生结构,对于ondemand来说,这个派生结构是:od_cpu_dbs_info_s。两个回调函数分别用来获得基类和派生类这两个结构的指针。我们先看看od_cpu_dbs_info_s是如何定义的:

static DEFINE_PER_CPU(struct od_cpu_dbs_info_s, od_cpu_dbs_info);

没错,它被定义为了一个per_cpu变量,也就是说,每个cpu拥有各自独立的od_cpu_dbs_info_s,这很正常,因为每个cpu需要的实时负载是不一样的,需要独立的上下文变量来进行负载的统计。前面也已经列出了od_cpu_dbs_info_s的声明,他的第一个字段cdbs就是一个cpu_dbs_common_info_s结构。内核为我们提供了一个辅助宏来帮助我们定义get_cpu_cdbs和get_cpu_dbs_info_s这两个回调函数:

#define define_get_cpu_dbs_routines(_dbs_info)                          \
static struct cpu_dbs_common_info *get_cpu_cdbs(int cpu)                \
{                                                                       \return &per_cpu(_dbs_info, cpu).cdbs;                           \
}                                                                       \\
static void *get_cpu_dbs_info_s(int cpu)                                \
{                                                                       \return &per_cpu(_dbs_info, cpu);                                \
}

经过上述这一系列的定义以后,governor的公共层即可通过这两个回调获取各个cpu所对应的cpu_dbs_common_info_s和od_cpu_dbs_info_s的结构指针,用来记录本次统计周期的一些上下文参数(idle时间和运行时间等等)。

3. 初始化一个governor


当一个governor被policy选定后,核心层会通过__cpufreq_set_policy函数对该cpu的policy进行设定,参看 Linux动态频率调节系统CPUFreq之二:核心(core)架构与API中的第4节和图4.1。如果policy认为这是一个新的governor(和原来使用的旧的governor不相同),policy会通过__cpufreq_governor函数,并传递CPUFREQ_GOV_POLICY_INIT参数,而__cpufreq_governor函数实际上是调用cpufreq_governor结构中的governor回调函数,在第2节中我们已经知道,这个回调最后会进入governor公共API:cpufreq_governor_dbs,下面是它收到CPUFREQ_GOV_POLICY_INIT参数时,经过简化后的代码片段:

        case CPUFREQ_GOV_POLICY_INIT:......dbs_data = kzalloc(sizeof(*dbs_data), GFP_KERNEL);......dbs_data->cdata = cdata;dbs_data->usage_count = 1;rc = cdata->init(dbs_data);......rc = sysfs_create_group(get_governor_parent_kobj(policy),get_sysfs_attr(dbs_data));......policy->governor_data = dbs_data;....../* Bring kernel and HW constraints together */dbs_data->min_sampling_rate = max(dbs_data->min_sampling_rate,MIN_LATENCY_MULTIPLIER * latency);set_sampling_rate(dbs_data, max(dbs_data->min_sampling_rate,latency * LATENCY_MULTIPLIER));if ((cdata->governor == GOV_CONSERVATIVE) &&(!policy->governor->initialized)) {struct cs_ops *cs_ops = dbs_data->cdata->gov_ops;cpufreq_register_notifier(cs_ops->notifier_block,CPUFREQ_TRANSITION_NOTIFIER);}if (!have_governor_per_policy())cdata->gdbs_data = dbs_data;return 0;

首先,它会给这个policy分配一个dbs_data实例,然后把通过参数cdata传入的common_dbs_data指针,赋值给它的cdata字段,这样,policy就可以通过该字段获得governor的操作接口(通过cdata的一系列回调函数)。然后,调用cdata的init回调函数,对这个governor做进一步的初始化工作,对于ondemand来说,init回调的实际执行函数是:od_init,主要是完成和governor相关的一些调节参数的初始化,然后把初始化好的od_dbs_tuners结构指针赋值到dbs_data的tuners字段中,它的详细代码这里就不贴出了。接着,通过sysfs_create_group函数,建立该governor在sysfs中的节点,以后我们就可以通过这些节点对该governor的算法逻辑进行微调,ondemand在我的电脑中,建立了以下这些节点(sys/devices/system/cpu/cpufreq/ondemand):

sampling_rate;
io_is_busy;
up_threshold;
sampling_down_factor;
ignore_nice;
powersave_bias;
sampling_rate_min;

继续,把初始化好的dbs_data结构赋值给policy的governor_data字段,以方便以后的访问。最后是通过set_sampling_rate设置governor的采样周期,如果还有设置have_governor_per_policy,把dbs_data结构指针赋值给cdata结构的gdbs_data字段,至此,governor的初始化工作完成,下面是整个过程的序列图:

4. 启动一个governor


核心层会通过__cpufreq_set_policy函数,通过CPUFREQ_GOV_POLICY_INIT参数,在公共层的API:cpufreq_governor_dbs中,完成了对governor的初始化工作,紧接着,__cpufreq_set_policy会通过CPUFREQ_GOV_START参数,和初始化governor的流程一样,最终会到达cpufreq_governor_dbs函数中,我们看看它是如何启动一个governor的:

        case CPUFREQ_GOV_START:if (!policy->cur)return -EINVAL;mutex_lock(&dbs_data->mutex);for_each_cpu(j, policy->cpus) {struct cpu_dbs_common_info *j_cdbs =dbs_data->cdata->get_cpu_cdbs(j);j_cdbs->cpu = j;j_cdbs->cur_policy = policy;j_cdbs->prev_cpu_idle = get_cpu_idle_time(j,&j_cdbs->prev_cpu_wall, io_busy);if (ignore_nice)j_cdbs->prev_cpu_nice =kcpustat_cpu(j).cpustat[CPUTIME_NICE];mutex_init(&j_cdbs->timer_mutex);INIT_DEFERRABLE_WORK(&j_cdbs->work,dbs_data->cdata->gov_dbs_timer);}

首先,遍历使用该policy的所有的处于online状态的cpu,针对每一个cpu,做以下动作:

  • 取出该cpu相关联的cpu_dbs_common_info结构指针,之前已经讨论过,governor定义了一个per_cpu变量来定义各个cpu所对应的cpu_dbs_common_info结构,通过common_dbs_data结构的回调函数可以获取该结构的指针。
  • 初始化cpu_dbs_common_info结构的cpu,cur_policy,prev_cpu_idle,prev_cpu_wall,prev_cpu_nice字段,其中,prev_cpu_idle,prev_cpu_wall这两个字段会被以后的负载计算所使用。
  • 为每个cpu初始化一个工作队列,工作队列的执行函数是common_dbs_data结构中的gov_dbs_timer字段所指向的回调函数,对于ondemand来说,该函数是:od_dbs_timer。这个工作队列会被按照设定好的采样率定期地被唤醒,进行cpu负载的统计工作。

然后,记录目前的时间戳,调度初始化好的工作队列在稍后某个时间点运行:

               /* Initiate timer time stamp */cpu_cdbs->time_stamp = ktime_get();gov_queue_work(dbs_data, policy,delay_for_sampling_rate(sampling_rate), true);

下图表达了启动一个governor的过程:

工作队列被调度执行后,会在工作队列的执行函数中进行cpu负载的统计工作,这个我们在下一节中讨论。

5. 系统负载的检测


上一节我们提到,核心层启动一个governor后,会在每个使用该governor的cpu上建立一个工作队列,工作队列的执行函数是在common_dbs_data中gov_dbs_timer字段所指向的函数,理所当然,该函数由各个governor的具体代码来实现,对于ondemand governor,它的实现函数是od_dbs_timer。governor的公共层代码为我们提供了一个API:dbs_check_cpu,该API用来计算两个统计周期期间某个cpu的负载情况,我们先分析一下dbs_check_cpu:

void dbs_check_cpu(struct dbs_data *dbs_data, int cpu)
{struct cpu_dbs_common_info *cdbs = dbs_data->cdata->get_cpu_cdbs(cpu);......policy = cdbs->cur_policy;/* Get Absolute Load (in terms of freq for ondemand gov) */for_each_cpu(j, policy->cpus) {struct cpu_dbs_common_info *j_cdbs;......j_cdbs = dbs_data->cdata->get_cpu_cdbs(j);......cur_idle_time = get_cpu_idle_time(j, &cur_wall_time, io_busy);wall_time = (unsigned int)(cur_wall_time - j_cdbs->prev_cpu_wall);j_cdbs->prev_cpu_wall = cur_wall_time;idle_time = (unsigned int)(cur_idle_time - j_cdbs->prev_cpu_idle);j_cdbs->prev_cpu_idle = cur_idle_time;......load = 100 * (wall_time - idle_time) / wall_time;......load *= cur_freq;    /* 实际的代码不是这样,为了简化讨论,精简为实际的计算逻辑*/if (load > max_load)max_load = load;}dbs_data->cdata->gov_check_cpu(cpu, max_load);
}

由代码可以看出,遍历该policy下每个online的cpu,取出该cpu对应的cpu_dbs_common_info结构,该结构中的prev_cpu_idle和prev_cpu_wall保存有上一次采样周期时记录的idle时间和运行时间,负载的计算其实很简单:

  • idle_time = 本次idle时间 - 上次idle时间;
  • wall_time = 本次总运行时间 - 上次总运行时间;
  • 负载load = 100 * (wall_time - idle_time)/ wall_time;
  • 把所有cpu中,负载最大值记入max_load中,作为选择频率的依据;

计算出最大负载max_load后,调用具体governor实现的gov_check_cpu回调函数,对于ondemand来说,该回调函数是:od_check_cpu,我们跟进去看看:

static void od_check_cpu(int cpu, unsigned int load_freq)
{struct od_cpu_dbs_info_s *dbs_info = &per_cpu(od_cpu_dbs_info, cpu);struct cpufreq_policy *policy = dbs_info->cdbs.cur_policy;struct dbs_data *dbs_data = policy->governor_data;struct od_dbs_tuners *od_tuners = dbs_data->tuners;dbs_info->freq_lo = 0;/* Check for frequency increase */if (load_freq > od_tuners->up_threshold * policy->cur) {/* If switching to max speed, apply sampling_down_factor */if (policy->cur < policy->max)dbs_info->rate_mult =od_tuners->sampling_down_factor;dbs_freq_increase(policy, policy->max);return;}

当负载比预设的阀值高时(od_tuners->up_threshold,默认值是95%),立刻选择该policy最大的工作频率作为接下来的工作频率。如果负载没有达到预设的阀值,但是当前频率已经是最低频率了,则什么都不做,直接返回:

        if (policy->cur == policy->min)return;

运行到这里,cpu的频率可能已经在上面的过程中被设置为最大频率,实际上我们可能并不需要这么高的频率,所以接着判断,当负载低于另一个预设值时,这时需要计算一个合适于该负载的新频率:

        if (load_freq < od_tuners->adj_up_threshold* policy->cur) {unsigned int freq_next;freq_next = load_freq / od_tuners->adj_up_threshold;/* No longer fully busy, reset rate_mult */dbs_info->rate_mult = 1;if (freq_next < policy->min)freq_next = policy->min;if (!od_tuners->powersave_bias) {__cpufreq_driver_target(policy, freq_next,CPUFREQ_RELATION_L);return;}freq_next = od_ops.powersave_bias_target(policy, freq_next,CPUFREQ_RELATION_L);__cpufreq_driver_target(policy, freq_next, CPUFREQ_RELATION_L);}
}

对于ondemand来说,因为传入的负载是乘上了当前频率后的归一化值,所以计算新频率时,直接用load_freq除以想要的负载即可。本来计算出来的频率直接通过__cpufreq_driver_target函数,交给cpufreq_driver调节频率即可,但是这里的处理考虑了powersave_bias的设置情况,当设置了powersave_bias时,表明我们为了进一步节省电力,我们希望在计算出来的新频率的基础上,再乘以一个powersave_bias设定的百分比,作为真正的运行频率,powersave_bias的值从0-1000,每一步代表0.1%。实际的情况比想象中稍微复杂一点,考虑到乘以一个powersave_bias后的新频率可能不在cpu所支持的频率表中,ondemand算法会在频率表中查找,分别找出最接近新频率的一个区间,由高低两个频率组成,低的频率记入od_cpu_dbs_info_s结构的freq_lo字段中,高的频率通过od_ops.powersave_bias_target回调返回。同时,od_ops.powersave_bias_target回调函数还计算出高低两个频率应该运行的时间,分别记入od_cpu_dbs_info_s结构的freq_hi_jiffies和freq_low_jiffies字段中。原则是,通过两个不同频率的运行时间的组合,使得综合结果接近我们想要的目标频率。详细的计算逻辑请参考函数:generic_powersave_bias_target。
讨论完上面两个函数,让我们回到本节的开头,负载的计算工作是在一个工作队列中发起的,前面说过,ondemand对应的工作队列的工作函数是od_dbs_timer,我们看看他的实现代码:

static void od_dbs_timer(struct work_struct *work)
{....../* Common NORMAL_SAMPLE setup */core_dbs_info->sample_type = OD_NORMAL_SAMPLE;if (sample_type == OD_SUB_SAMPLE) {delay = core_dbs_info->freq_lo_jiffies;__cpufreq_driver_target(core_dbs_info->cdbs.cur_policy,core_dbs_info->freq_lo, CPUFREQ_RELATION_H);} else {dbs_check_cpu(dbs_data, cpu);if (core_dbs_info->freq_lo) {/* Setup timer for SUB_SAMPLE */core_dbs_info->sample_type = OD_SUB_SAMPLE;delay = core_dbs_info->freq_hi_jiffies;}}max_delay:if (!delay)delay = delay_for_sampling_rate(od_tuners->sampling_rate* core_dbs_info->rate_mult);gov_queue_work(dbs_data, dbs_info->cdbs.cur_policy, delay, modify_all);mutex_unlock(&core_dbs_info->cdbs.timer_mutex);
}

如果sample_type是OD_SUB_SAMPLE时,表明上一次采样时,需要用高低两个频率来模拟实际的目标频率中的第二步:需要运行freq_lo,并且持续时间为freq_lo_jiffies。否则,调用公共层计算负载的API:dbs_check_cpu,开始一次新的采样,当powersave_bias没有设置时,该函数返回前,所需要的新的目标频率会被设置,考虑到powersave_bias的设置情况,判断一下如果freq_lo被设置,说明需要用高低两个频率来模拟实际的目标频率,高频率已经在dbs_check_cpu返回前被设置(实际的设置工作是在od_check_cpu中),所以把sample_type设置为OD_SUB_SAMPLE,以便下一次运行工作函数进行采样时可以设置低频率运行。最后,调度工作队列在下一个采样时刻再次运行,这样,cpu的工作频率实现了在每个采样周期,根据实际的负载情况,动态地设定合适的工作频率进行运行,既满足了性能的需求,也降低了系统的功耗,达到了cpufreq系统的最终目的,整个流程可以参考下图:

Linux动态频率调节系统CPUFreq之三:governor相关推荐

  1. Linux动态频率调节系统CPUFreq

    Linux动态频率调节系统CPUFreq之一:概述 Linux动态频率调节系统CPUFreq之二:核心(core)架构与API Linux动态频率调节系统CPUFreq之三:governor

  2. 从基本理解到深入探究 Linux动态频率调节系统cpufreq

    文章目录 概述 1. 管中窥豹--sysfs接口 2. 刨根问底--软件架构 1. cpufreq_policy 2. cpufreq_governor 3. cpufreq_driver 4. cp ...

  3. Linux动态频率调节系统CPUFreq之一:概述【转】-- 非常好的博客

    转自:http://blog.csdn.net/droidphone/article/details/9346981 目录(?)[-] sysfs接口 软件架构 cpufreq_policy cpuf ...

  4. cpufreq动态频率调节(Sofia3GR)浅析及应用层APK实现

    cpufreq动态频率调节浅析 cpufreq核心部分的代码都在:/drivers/cpufreq/cpufreq.c中,本文章是基于SOFIA3GR 6.0的代码进行解析,linux内核版本3.14 ...

  5. ⑭tiny4412 Linux驱动开发之cpufreq子系统驱动程序

    本次我们来说一下CPU动态调频子系统. 首先来看一下三星Exynos 4412的datasheet,如下: 上图就是Exynos 4412的时钟分布图,可以看到CPU的频率可以在1.4GHz~200M ...

  6. CPU动态调频二:interactive governor

    Linux提供了多种governor供用户选择,这里以interactive为例,毕竟现在的android手机中都是采用该governor. 基于linux 3.14 以下代码若未指明位置则默认在dr ...

  7. linux代码动态分析软件,举例分析Linux动态库和静态库

    函数库分为静态库和动态库两种.创建Linux静态库和Linux动态库和使用它们在这里将以举例的形式详述一下.静态库在程序编译时会被连接到目标代码中,程序运行时将不再需要该静态库.动态库在程序编译时并不 ...

  8. Linux动态库(.so)搜索路径

    众所周知,Linux动态库的默认搜索路径是/lib和/usr/lib.动态库被创建后,一般都复制到这两个目录中.当程序执行时需要某动态库,并且该动 态库还未加载到内存中,则系统会自动到这两个默认搜索路 ...

  9. Linux动态库和静态库比较

     Linux动态库和静态库比较文件预览 文件目录树如下,如你所见,非常简单.    1. libtest/    2. |-- lt.c    3. |-- lt.h    4. `-- test.c ...

  10. 汇编语言使用C库函数和Linux动态链接

    使用printf 代码 #cpuid2.s -- Using C labrary calls .section .data output: .asciz "The processor Ven ...

最新文章

  1. AJAX GET的请求
  2. 同一公司代码下工厂间的库存转储 (轉載)
  3. 小学计算机试教教案,小学信息技术人教版三年级下册第7课《轻轻松松来上网》优质课公开课教案教师资格证面试试讲教案...
  4. 读书笔记:《一生的计划》
  5. 台式计算机M丅BF是什么,中南民族大学计算机系统结构试卷
  6. 类型字节oracle 增强型时间类型以及postgresql时间类型
  7. sas python接口_全方位比较3种数据科学工具:Python、R和SAS(附链接)
  8. 网页设计制作CSS实现隔行换色两种方法
  9. 基于Java的仓库管理系统
  10. 精彩收集的303个透明flash代码
  11. 涨点神器!特征金字塔技术总结
  12. 怎么用计算机自己做动画片,怎样用电脑制作动画,电脑动画制作怎么做
  13. unity用visual studio写代码的时候一直显示importing assets
  14. 洛谷P5708 【深基2.习2】三角形面积__C++描述
  15. Eclipse WindowBuilder安装过程
  16. 体验ChatGPT后,陷入沉思...
  17. php怎么设置表格大小,如何设置excel表中的单元格大小统一
  18. windows下连接服务器
  19. ipv4地址的编码长度为_请问IPV4是什么意思???
  20. 阿里云服务器自动备份

热门文章

  1. python——spilt和strip用法
  2. Java之spilt()函数,trim()函数
  3. 怎么让计算机文件格式显示,已知文件类型的扩展名如何设置显示与隐藏?
  4. 微信可以批量清理僵尸粉啦
  5. 计算机数字音乐我的梦,我的音乐梦
  6. JMM,synchronized
  7. 每天一个linux命令(26):用SecureCRT来上传和下载文件(转载自竹子)
  8. 常用电子邮箱格式怎么写?哪个邮箱最安全最好用?
  9. (翻译)赫曼方格如何影响用户视觉
  10. construct the binary tree.