linux内核死锁检测机制 | oenhan,Linux内核CPU负载均衡机制 | OenHan

还是神奇的进程调度问题引发的,参看Linux进程组调度机制分析,组调度机制是看清楚了,发现在重启过程中,很多内核调用栈阻塞在了double_rq_lock函数上,而double_rq_lock则是load_balance触发的,怀疑当时的核间调度出现了问题,在某个负责场景下产生了多核互锁,后面看了一下CPU负载平衡下的代码实现,写一下总结。

内核代码版本:kernel-3.0.13-0.27。

内核代码函数起自load_balance函数,从load_balance函数看引用它的函数可以一直找到schedule函数这里,便从这里开始往下看,在__schedule中有下面一句话。

1

2if (unlikely(!rq->nr_running))

idle_balance(cpu, rq);

从上面可以看出什么时候内核会尝试进行CPU负载平衡:即当前CPU运行队列为NULL的时候。

CPU负载平衡有两种方式:pull和push,即空闲CPU从其他忙的CPU队列中拉一个进程到当前CPU队列;或者忙的CPU队列将一个进程推送到空闲的CPU队列中。idle_balance干的则是pull的事情,具体push下面会提到。

在idle_balance里面,有一个proc阀门控制当前CPU是否pull:

1

2if (this_rq->avg_idle < sysctl_sched_migration_cost)

return;

sysctl_sched_migration_cost对应proc控制文件是/proc/sys/kernel/sched_migration_cost,开关代表如果CPU队列空闲了500ms(sysctl_sched_migration_cost默认值)以上,则进行pull,否则则返回。

for_each_domain(this_cpu, sd) 则是遍历当前CPU所在的调度域,可以直观的理解成一个CPU组,类似task_group,核间平衡指组内的平衡。负载平衡有一个矛盾就是:负载平衡的频度和CPU cache的命中率是矛盾的,CPU调度域就是将各个CPU分成层次不同的组,低层次搞定的平衡就绝不上升到高层次处理,避免影响cache的命中率。

图例如下;

45295758_1.jpg

最终通过load_balance进入正题。

首先通过find_busiest_group获取当前调度域中的最忙的调度组,首先update_sd_lb_stats更新sd的状态,也就是遍历对应的sd,将sds里面的结构体数据填满,如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22struct sd_lb_stats {

struct sched_group *busiest;/* Busiest group in this sd */

struct sched_group *this;/* Local group in this sd */

unsignedlong total_load;/* Total load of all groups in sd */

unsignedlong total_pwr;/*   Total power of all groups in sd */

unsignedlong avg_load;/* Average load across all groups in sd */

/** Statistics of this group */

unsignedlong this_load;//当前调度组的负载

unsignedlong this_load_per_task;//当前调度组的平均负载

unsignedlong this_nr_running;//当前调度组内运行队列中进程的总数

unsignedlong this_has_capacity;

unsignedint  this_idle_cpus;

/* Statistics of the busiest group */

unsignedint  busiest_idle_cpus;

unsignedlong max_load;//最忙的组的负载量

unsignedlong busiest_load_per_task;//最忙的组中平均每个任务的负载量

unsignedlong busiest_nr_running;//最忙的组中所有运行队列中进程的个数

unsignedlong busiest_group_capacity;

unsignedlong busiest_has_capacity;

unsignedint  busiest_group_weight;

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26do

{

local_group = cpumask_test_cpu(this_cpu, sched_group_cpus(sg));

if (local_group) {

//如果是当前CPU上的group,则进行赋值

sds->this_load = sgs.avg_load;

sds->this = sg;

sds->this_nr_running = sgs.sum_nr_running;

sds->this_load_per_task = sgs.sum_weighted_load;

sds->this_has_capacity = sgs.group_has_capacity;

sds->this_idle_cpus = sgs.idle_cpus;

}else if (update_sd_pick_busiest(sd, sds, sg, &sgs, this_cpu)) {

//在update_sd_pick_busiest判断当前sgs的是否超过了之前的最大值,如果是

//则将sgs值赋给sds

sds->max_load = sgs.avg_load;

sds->busiest = sg;

sds->busiest_nr_running = sgs.sum_nr_running;

sds->busiest_idle_cpus = sgs.idle_cpus;

sds->busiest_group_capacity = sgs.group_capacity;

sds->busiest_load_per_task = sgs.sum_weighted_load;

sds->busiest_has_capacity = sgs.group_has_capacity;

sds->busiest_group_weight = sgs.group_weight;

sds->group_imb = sgs.group_imb;

}

sg = sg->next;

}while (sg != sd->groups);

决定选择调度域中最忙的组的参照标准是该组内所有 CPU上负载(load) 的和, 找到组中找到忙的运行队列的参照标准是该CPU运行队列的长度, 即负载,并且 load 值越大就表示越忙。在平衡的过程中,通过比较当前队列与以前记录的busiest 的负载情况,及时更新这些变量,让 busiest 始终指向域内最忙的一组,以便于查找。

调度域的平均负载计算

1

2

3sds.avg_load = (SCHED_POWER_SCALE * sds.total_load) / sds.total_pwr;

if (sds.this_load >= sds.avg_load)

goto out_balanced;

在比较负载大小的过程中, 当发现当前运行的CPU所在的组中busiest为空时,或者当前正在运行的 CPU队列就是最忙的时, 或者当前 CPU队列的负载不小于本组内的平均负载时,或者不平衡的额度不大时,都会返回 NULL 值,即组组之间不需要进行平衡;当最忙的组的负载小于该调度域的平均负载时,只需要进行小范围的负载平衡;当要转移的任务量小于每个进程的平均负载时,如此便拿到了最忙的调度组。

然后find_busiest_queue中找到最忙的调度队列,遍历该组中的所有 CPU 队列,经过依次比较各个队列的负载,找到最忙的那个队列。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31for_each_cpu(i, sched_group_cpus(group)) {

/*rq->cpu_power表示所在处理器的计算能力,在函式sched_init初始化时,会把这值设定为SCHED_LOAD_SCALE (=Nice 0的Load Weight=1024).并可透过函式update_cpu_power (in kernel/sched_fair.c)更新这个值.*/

unsignedlong power = power_of(i);

unsignedlong capacity = DIV_ROUND_CLOSEST(power,SCHED_POWER_SCALE);

unsignedlong wl;

if (!cpumask_test_cpu(i, cpus))

continue;

rq = cpu_rq(i);

/*获取队列负载cpu_rq(cpu)->load.weight;*/

wl = weighted_cpuload(i);

/*

* When comparing with imbalance, use weighted_cpuload()

* which is not scaled with the cpu power.

*/

if (capacity && rq->nr_running == 1 && wl > imbalance)

continue;

/*

* For the load comparisons with the other cpu's, consider

* the weighted_cpuload() scaled with the cpu power, so that

* the load can be moved away from the cpu that is potentially

* running at a lower capacity.

*/

wl = (wl * SCHED_POWER_SCALE) / power;

if (wl > max_load) {

max_load = wl;

busiest = rq;

}

通过上面的计算,便拿到了最忙队列。

当busiest->nr_running运行数大于1的时候,进行pull操作,pull前对move_tasks,先进行double_rq_lock加锁处理。

1

2

3

4double_rq_lock(this_rq, busiest);

ld_moved = move_tasks(this_rq, this_cpu, busiest,

imbalance, sd, idle, &all_pinned);

double_rq_unlock(this_rq, busiest);

move_tasks进程pull task是允许失败的,即move_tasks->balance_tasks,在此处,有sysctl_sched_nr_migrate开关控制进程迁移个数,对应proc的是/proc/sys/kernel/sched_nr_migrate。

下面有can_migrate_task函数检查选定的进程是否可以进行迁移,迁移失败的原因有3个,1.迁移的进程处于运行状态;2.进程被绑核了,不能迁移到目标CPU上;3.进程的cache仍然是hot,此处也是为了保证cache命中率。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17/*关于cache cold的情况下,如果迁移失败的个数太多,仍然进行迁移

* Aggressive migration if:

* 1) task is cache cold, or

* 2) too many balance attempts have failed.

*/

tsk_cache_hot = task_hot(p, rq->clock_task, sd);

if (!tsk_cache_hot ||

sd->nr_balance_failed > sd->cache_nice_tries) {

#ifdef CONFIG_SCHEDSTATS

if (tsk_cache_hot) {

schedstat_inc(sd, lb_hot_gained[idle]);

schedstat_inc(p, se.statistics.nr_forced_migrations);

}

#endif

return 1;

}

判断进程cache是否有效,判断条件,进程的运行的时间大于proc控制开关sysctl_sched_migration_cost,对应目录/proc/sys/kernel/sched_migration_cost_ns

1

2

3

4

5

6

7static int

task_hot(struct task_struct *p, u64 now,struct sched_domain *sd)

{

s64 delta;

delta = now - p->se.exec_start;

return delta < (s64)sysctl_sched_migration_cost;

}

在load_balance中,move_tasks返回失败也就是ld_moved==0,其中sd->nr_balance_failed++对应can_migrate_task中的"too many balance attempts have failed",然后busiest->active_balance = 1设置,active_balance = 1。

1

2

3

4

5if (active_balance)

//如果pull失败了,开始触发push操作

stop_one_cpu_nowait(cpu_of(busiest),

active_load_balance_cpu_stop, busiest,

&busiest->active_balance_work);

push整个触发操作代码机制比较绕,stop_one_cpu_nowait把active_load_balance_cpu_stop添加到cpu_stopper每CPU变量的任务队列里面,如下:

1

2

3

4

5

6void stop_one_cpu_nowait(unsignedint cpu, cpu_stop_fn_t fn,void *arg,

struct cpu_stop_work *work_buf)

{

*work_buf = (struct cpu_stop_work){ .fn = fn, .arg = arg, };

cpu_stop_queue_work(&per_cpu(cpu_stopper, cpu), work_buf);

}

而cpu_stopper则是cpu_stop_init函数通过cpu_stop_cpu_callback创建的migration内核线程,触发任务队列调度。因为migration内核线程是绑定每个核心上的,进程迁移失败的1和3问题就可以通过push解决。active_load_balance_cpu_stop则调用move_one_task函数迁移指定的进程。

上面描述的则是整个pull和push的过程,需要补充的pull触发除了schedule后触发,还有scheduler_tick通过触发中断,调用run_rebalance_domains再调用rebalance_domains触发,不再细数。

1

2

3

4void __init sched_init(void)

{

open_softirq(SCHED_SOFTIRQ, run_rebalance_domains);

}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/558028.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

linux适应环境,Linux从入门到适应(一):VSCode C++环境配置

作为在Windows环境下习惯使用Visual Studio IDE的人&#xff0c;对于Linux环境下的Vim编辑使用十分难受&#xff0c;虽然网上很多人说vim非常牛逼和强大&#xff0c;但是我更加习惯于使用VS code的界面&#xff0c;所以我选择VS code作为编辑器使用。VS code本身是一个编辑器&a…

linux指令能回滚么,如何在Ubuntu上撤消或回滚“ apt-get upgrade”命令?

我今天必须在Debian系统上执行此操作。首先&#xff0c;我确定了有问题的升级发生的时间范围&#xff0c;并检索了给出升级包的新旧版本号的日志条目&#xff1a;$ awk $1"2016-03-20" && $3"upgrade" /var/log/dpkg.log2016-03-20 16:58:22 upgra…

linux文件名过长无法删除,不能删除文件,出现“源文件名长度大于系统支持的长度...

有时候删除windows中的目录的时候,会出现"源文件名长度大于系统支持的长度", 而导致不能删除, 作为一个程序猿, 怎么可以被这个折服呢, 上代码:import java.io.File;/*** author 吴庆龙* 2015年10月13日 下午1:22:33*/public class DeleteFiles {public static void …

cpu system linux,LINUX system BOOT

64位处理器指的是CPU GPRs(General-Purpose Registers 通用寄存器)的数据宽度为64位计算机在接通电源的瞬间&#xff0c;CPU处于16位实模式&#xff0c;20位的寻址空间&#xff0c;即1MB&#xff0c;此时内存里没有任何数据对CPU来讲&#xff0c;系统中所有的存储器(含BIOS的RO…

linux下IPROTO_TCP,TCP/IP协议栈在Linux内核中的运行时序分析

可选题目三&#xff1a;TCP/IP协议栈在Linux内核中的运行时序分析在深入理解Linux内核任务调度(中断处理、softirg、tasklet、wq、内核线程等)机制的基础上&#xff0c;分析梳理send和recv过程中TCP/IP协议栈相关的运行任务实体及相互协作的时序分析。编译、部署、运行、测评、…

vs2019Linux守护,Visual Studio 2019将支援Ninja显着提升Linux专案建置效率

微软更新Visual Studio 2019&#xff0c;新增多个可提升Linux开发体验的功能&#xff0c;包括在Linux上支援建置系统Ninja&#xff0c;以及更完整地支援gdbserver&#xff0c;而且现在开发者也可以使用连接管理器(Connection Manager)&#xff0c;编辑和配置预设的远端连接。使…

三个数比较大小函数调用c语言,C语言函数的调用——比较两个数的大小

**目录**一、先写好框架二、然后定义我们需要的变量三、这里就要写函数的部分四、函数部分写完了&#xff0c;但是还一个地方&#xff0c;要值得注意一、常规方法比较大小二、指针操作比较大小--------------------今天我们要写的是用**调用函数**的方法来 比较两个数字的大小我…

c语言爱心代码空心,c语言心形图案代码,是什么?

#include int main(){int i,j;printf(" ****** ******\n"" ********** **********\n"" ************* *************\n");//前三排的规律性不强 所以直接显示就好了for(i0;i<3;i)//显示中间三排{for(j0;j<29;j)printf(&quo…

c语言程序中必不可少的,C语言程序设计(第3章程序控制语句)2

3.2 数据的输入与输出在程序的运行过程中&#xff0c;往往需要由用户输入一些数据&#xff0c;而程序运算所得到的计算结果等又需要输出给用户&#xff0c;由此实现人与计算机之间的交互&#xff0c;所以在程序设计中&#xff0c;输入输出语句是一类必不可少的重要语句&#xf…

快速排序c语言实现,快速排序的C语言代码实现

快速排序实质上是对“冒泡排序”的一种改进&#xff0c;整个排序过程可概括为&#xff1a;通过N趟的排序将原本的排序数据分为若干块进行分块排序&#xff0c;而在每趟排序过程中&#xff0c;以指定的关键字将待排数据分别分为比关键字大的部分和比关键字小的部分&#xff0c;反…

android 九宫格封装,Android 九宫格布局

演示image需求满足0-9个图的适配图数量演示1image2image3image4image5image6image7image8image9image使用手动设置android:layout_width"match_parent"android:layout_height"wrap_content"app:ngl_gridSpace"10dp"app:ngl_oneChildHeight"…

android监听应用服务,Android应用中Back键的监听及处理实例

MainActivity如下:复制代码 代码如下:package cn.testnbackpressed;import android.os.Bundle;import android.view.KeyEvent;import android.app.Activity;/*** Demo描述:* 处理Back键按下事件** 注意事项:* 以下两种方法勿一起使用*/public class MainActivity extends Activ…

android放大镜无广告,Android放大镜的实现代码

快三个月了没写博客了&#xff0c;因为工作调动&#xff0c;很多经验、心得都没有时间记录下来。现在时间稍微充裕了点&#xff0c;我会尽量抽时间将之前想写而没写的东西补上。进入正题。去年某个时候&#xff0c;我偶然看到一篇文章&#xff0c;讲android里面放大镜的实现。文…

android获取3g或wifi流量信息,Android代碼----android獲取3G或wifi流量信息

Android代碼----android獲取3G或wifi流量信息日期&#xff1a;2017/2/23 18:01:39 &nbsp 編輯&#xff1a;關於Android編程android獲取3G或wifi流量信息&#xff1a;[java]IBatteryStats battryStats IBatteryStats.Stub.asInterface(ServiceManager.getService("…

如何在android进行ltp测试,Android系统完整性度量架构IMA-EVM

错误2&#xff1a;/bin/bash:m4: command not found解决 &#xff1a;sudo apt-get install m4错误3&#xff1a;prebuilts/misc/linux-x86/bison/bison:No such file or directory原因 &#xff1a;ubuntu64位系统运行32位程序的问题&#xff0c;需要安装运行32位程序的兼容…

android 如何 root权限获取,如何获取android手机root权限获取

安卓手机的卡与不卡&#xff0c;和是否root无关。安卓的卡&#xff0c;是其开发时就注定的&#xff0c;其原因如下&#xff1a;1.内存小/少手机内存在手机出厂的时候就已经固定了&#xff0c;其总量无法调整&#xff0c;不能像电脑一样加内存条。所以想提升可用内存&#xff0c…

日语输入法 android8.0,讯飞输入法发布 AndroidV8.0.6855 专注提升输入效率

作为最懂用户的讯飞输入法&#xff0c;近日发布全新 Android V8.0.6855 版本&#xff0c;坚持以用户体验为基础&#xff0c;不断优化产品性能&#xff0c;提升用户体验。本次新版更加专注于输入效率&#xff0c;带来了 BiuBiu 键盘、离线语音等多个输入功能的提升。BiuBiu 键盘…

imx6 android快速启动,freescale imx6 开机启动速度优化之Bootchart工具的使用问题

之前有安装bootchart&#xff0c;先执行以下命令&#xff0c;卸载掉bootchart工具sudo apt-get autoremove bootchartbootchart安装1、安装$sudo apt-get install bootchart$sudo apt-get install pybootchartgui2、编写android 上log文件的打包和自动生成bootchart.png的脚本文…

鸿蒙和宙斯谁厉害,漫威宇宙宙斯vs奥丁,到底谁更强

宙斯在漫威里&#xff0c;是希腊神话中的众神之王&#xff0c;奥林匹斯十二主神之一&#xff0c;也是奥林匹斯大部分神和神奇女侠戴安娜的父亲&#xff0c;同时也是沙赞的力量来源之一能力&#xff1a;不朽(只有宙斯的血能杀死宙斯)宙斯神力雷霆之怒控制天气宙斯的力量并不是某…

html 输入框 相加,JS中,如何实现两个输入框中内容的数字相加?

事件function count(){var adocument.getElementById("txt1").value;var bdocument.getElementById("txt2").value;var fdocument.getElementById("select").value;switch(f){case :document.getElementById("fruit").valueab;break;c…