极简组调度-CGroup如何限制cpu
|
说明
|
task group
|
进程组,为了支持CGroup控制cpu,引入了组调度的概念,task group即包含所有要控制cpu的task集合以及配置信息。
|
group task
|
本文的专有名词,是指一个进程组下的task,这些task受一个CGroup控制
|
cfs_bandwidth
|
task_group的重要成员,包含了所要控制cpu的period,quota,定时器等信息
|
throttle
|
当group se在一个设定的时间周期内,消耗完了指定的cpu配额,则将其从cpu运行队列中移出,并不再调度。
注意:处于throttled状态的task仍是Ready状态的,只是不在rq上。
|
unthrottle
|
将throttle状态的group se,重新加入到cpu运行队列中调度。
|
struct cfs_rq
{
struct rb_root tasks_timeline; // 以vruntime为key,se为value的红黑树根节点,schedule时,cfs调度算法每次从这里挑选vruntime最小的se投入运行
struct rb_node* rb_leftmost; // 最左的叶子节点,即vruntime最小的se,直接取这个节点以加快速度
sched_entity* curr; // cfs_rq中当前正在运行的se
struct rq* rq; /* cpu runqueue to which this cfs_rq is attached */
struct task_group* tg; /* group that "owns" this runqueue */
int throttled; // 表示该cfs_rq所属的group se是否被throttled
s64 runtime_remaining; // cfs_rq从全局时间池申请的时间片剩余时间,当剩余时间小于等于0的时候,就需要重新申请时间片
}; struct sched_entity
{
unsigned int on_rq; // se是否在rq上,不在的话即使task是Ready状态也不会投入运行的
u64 vruntime; // cpu运行时长,cfs调度算法总是选择该值最小的se投入运行
/* rq on which this entity is (to be) queued: */
struct cfs_rq* cfs_rq; // se所在的cfs_rq,如果是普通task se,等于rq的cfs_rq,如果是group中的task,则等于group的cfs_rq
/* rq "owned" by this entity/group: */
struct cfs_rq* my_q; // my_q == NULL表示是一个普通task se,否则表示是一个group se,my_q指向group的cfs_rq
}; struct task
{
struct sched_entity se;
}; struct rq
{
struct cfs_rq cfs; // 所有要调度的se都挂在cfs rq中
struct task_struct* curr; // 当前cpu上运行的task
};
本文中的sched_entity定义比《极简cfs公平调度算法》中的要复杂些,各种cfs_rq容易搞混,这里讲一下cfs公平调度挑选group task调度流程(只用到了my_q这个cfs_rq),以梳理清楚其关系
task_struct *pick_next_task_fair(struct rq *rq)
{
struct cfs_rq *cfs_rq = &rq->cfs; // 开始的cfs_rq为rq的cfs
do {
se = pick_next_entity(cfs_rq); // 《极简cfs公平调度算法》中讲过这个函数,其就是取cfs_rq->rb_leftmost,即最小vruntime的se
cfs_rq = group_cfs_rq(se); // 取se.my_q,如果是普通的task se,cfs_rq = NULL,这里就会退出循环,如果是group se,cfs_rq = group_se.my_q,然后在group se的cfs_rq中继续寻找vruntime最小的se
} while (cfs_rq); return task_of(se);
} cfs_rq *group_cfs_rq(struct sched_entity *grp)
{
return grp->my_q;
}
struct cfs_bandwidth
{
ktime_t period; // cpu.cfs_period_us的值
u64 quota; // cpu.cfs_quota_us的值
u64 runtime; // 当前周期内剩余的quota时间
int timer_active; // period_timer是否激活
struct hrtimer period_timer; // 定时分配cpu quota的定时器,定时器触发时会更新runtime
}; struct task_group
{
struct sched_entity** se; /* schedulable entities of this group on each cpu */
struct cfs_rq** cfs_rq; /* runqueue "owned" by this group on each cpu */
struct cfs_bandwidth cfs_bandwidth; // 管理记录CGroup控制cpu的信息
};
1> task_group.se是一个数组,每个cpu都有一个其对应的group se
void update_curr(struct cfs_rq* cfs_rq)
{
struct sched_entity* curr = cfs_rq->curr;
curr->vruntime += delta_exec; // 增加se的运行时间
account_cfs_rq_runtime(cfs_rq, delta_exec);
}
void account_cfs_rq_runtime(struct cfs_rq* cfs_rq, u64 delta_exec)
{
cfs_rq->runtime_remaining -= delta_exec;
if (cfs_rq->runtime_remaining > 0)
return;
// 如果runtime_remaining不够了,则要向task group分配cpu quota,分配失败则设置task的thread flag为TIF_NEED_RESCHED,表示需要重新调度
if (!assign_cfs_rq_runtime(cfs_rq) && likely(cfs_rq->curr))
resched_curr(cfs_rq->rq);
}
/* returns 0 on failure to allocate runtime */
int assign_cfs_rq_runtime(struct cfs_rq* cfs_rq)
{
struct cfs_bandwidth* cfs_b = cfs_rq->tg->cfs_bandwidth;; // 如果有限制cpu,则减去最小分配时间,如果cfs_b->runtime为0,那就没有时间可分配了,本函数就会返回0,表示分配失败
amount = min(cfs_b->runtime, min_amount);
cfs_b->runtime -= amount;
cfs_rq->runtime_remaining += amount;
return cfs_rq->runtime_remaining > 0;
}
void schedule()
{
prev = rq->curr;
put_prev_task_fair(rq, prev);
// 选择下一个task并切换运行
next = pick_next_task(rq);
context_switch(rq, prev, next);
}
void put_prev_task_fair(struct rq* rq, struct task_struct* prev)
{
struct sched_entity* se = &prev->se;
put_prev_entity(se->cfs_rq, se);
} void put_prev_entity(struct cfs_rq* cfs_rq, struct sched_entity* prev)
{
check_cfs_rq_runtime(cfs_rq);
}
void check_cfs_rq_runtime(struct cfs_rq* cfs_rq)
{
if (cfs_rq->runtime_remaining > 0)
return;
throttle_cfs_rq(cfs_rq);
}
void throttle_cfs_rq(struct cfs_rq* cfs_rq)
{
struct sched_entity* se = cfs_rq->tg->se[cpu_of(rq_of(cfs_rq))]; // 取对应cpu rq上的group se
dequeue_entity(se->cfs_rq, se, DEQUEUE_SLEEP); //从cpu rq中删除group se
cfs_rq->throttled = 1; // 标记group cfs_rq被throttled
}
viod init_cfs_bandwidth(struct cfs_bandwidth* cfs_b)
{
hrtimer_init(&cfs_b->period_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
cfs_b->period_timer.function = sched_cfs_period_timer;
}
enum hrtimer_restart sched_cfs_period_timer(struct hrtimer* timer)
{
idle = do_sched_cfs_period_timer(cfs_b, overrun);
return idle ? HRTIMER_NORESTART : HRTIMER_RESTART;
}
int do_sched_cfs_period_timer(struct cfs_bandwidth* cfs_b, int overrun)
{
__refill_cfs_bandwidth_runtime(cfs_b);
distribute_cfs_runtime(cfs_b, runtime, runtime_expires);
}
void __refill_cfs_bandwidth_runtime(struct cfs_bandwidth* cfs_b)
{
cfs_b->runtime = cfs_b->quota;
}
u64 distribute_cfs_runtime(struct cfs_bandwidth* cfs_b, u64 remaining, u64 expires)
{
struct cfs_rq* cfs_rq;
list_for_each_entry_rcu(cfs_rq, &cfs_b->throttled_cfs_rq, throttled_list)
{
unthrottle_cfs_rq(cfs_rq);
}
} void unthrottle_cfs_rq(struct cfs_rq* cfs_rq)
{
se = cfs_rq->tg->se[cpu_of(rq_of(cfs_rq))];
enqueue_entity(cfs_rq, se, ENQUEUE_WAKEUP); // 将se加回rq.cfs_rq的红黑树上
}
极简组调度-CGroup如何限制cpu的更多相关文章
- Linux进程组调度机制分析【转】
转自:http://oenhan.com/task-group-sched 又碰到一个神奇的进程调度问题,在系统重启过程中,发现系统挂住了,过了30s后才重新复位,真正系统复位的原因是硬件看门狗重启的 ...
- 极简的js点击组图切换效果
程序员进行前端开发时,时常要用到点击切换组图的动画效果,网上确实有很多此类插件,但是都很麻烦,乌糟糟无数代码,有那个看的时间,自己都能把功能写完了.在这里我提供一段极简的js点击组图切换效果代码,包含 ...
- docker cgroup技术之cpu和cpuset
在centos7的/sys/fs/cgroup下面可以看到与cpu相关的有cpu,cpuacct和cpuset 3个subsystem.cpu用于对cpu使用率的划分:cpuset用于设置cpu的亲和 ...
- cgroup 分析之CPU和内存部分
https://ggaaooppeenngg.github.io/zh-CN/2017/05/07/cgroups-%E5%88%86%E6%9E%90%E4%B9%8B%E5%86%85%E5%AD ...
- 【原创】(四)Linux进程调度-组调度及带宽控制
背景 Read the fucking source code! --By 鲁迅 A picture is worth a thousand words. --By 高尔基 说明: Kernel版本: ...
- NodeJS 极简教程 <1> NodeJS 特点 & 使用场景
NodeJS 极简教程 <1> NodeJS 特点 & 使用场景 田浩 因为看开了所以才去较劲儿. 1. NodeJS是什么 1.1 Node.js is a JavaScri ...
- WebFetch 是无依赖极简网页爬取组件
WebFetch 是无依赖极简网页爬取组件,能在移动设备上运行的微型爬虫. WebFetch 要达到的目标: 没有第三方依赖jar包 减少内存使用 提高CPU利用率 加快网络爬取速度 简洁明了的api ...
- 极极极极极简的的增删查改(CRUD)解决方案
去年这个时候写过一篇全自动数据表格的文章http://www.cnblogs.com/liuyh/p/5747331.html.文章对自己写的一个js组件做了个概述,很多人把它当作了一款功能相似的纯前 ...
- 极简Python DeBug工具——PySnooper
DeBug Python 代码的方式有很多种?比如: (1)设置断点 (2)print函数 (3)... 本文要介绍的是一个新开源的项目PySnooper ,只要给有疑问的代码加上装饰器,各种信息一目 ...
- 基于Linux-3.9.4的mykernel实验环境的极简内核分析
382 + 原创作品转载请注明出处 + https://github.com/mengning/linuxkernel/ 一.实验环境 win10 -> VMware -> Ubuntu1 ...
随机推荐
- matlab 将某文件夹的内容复制到另一文件下
%% 清空close all;clear;clc; %% 选择文件路径(复制某文件夹下部分文件夹到其他路径)folder = uigetdir('C:\Desktop','请选择文件夹'); %% i ...
- List,Set,Map存取元素各有什么特点 hashMap、hashTable的区别 Arraylist和linkedList的区别
1.List,Set,Map存取元素各有什么特点? 1.存放 (1)List存放元素是有序,可重复 (2)Set存放元素无序,不可重复 (3)Map元素键值对形式存放,键无序不可重复,值可重复 2.取 ...
- spring管理配置文件实现注入
创建配置文件 写入以下内容: 创建配置文件的bean: <bean id="configProperties" class="org.springframework ...
- 容器数据库(CDB)和传统的非容器数据库的区别
传统的非容器数据库在系统元数据和实例资源没有进行任何共享.容器数据库把 Oracle 提供的元数据.后台进程以及内存结构进行共享,把每个部门数据库的存储结构以 PDB 的形式独立出来,从而实现了系统资 ...
- 《JavaScript高级程序设计》Chapter03 JavaScript语言基础
目录 Syntax Variable var let const Data Type Undefined Null Boolean Number String Symbol Object Operat ...
- resnet模型下载
resnet模型下载: model_urls = { 'resnet18': 'https://s3.amazonaws.com/pytorch/models/resnet18-5c106cde.pt ...
- printf( )和scanf( )
printf()的转换说明 转换说明 输出 %a,%A 浮点数,十六进制数和p记数法 %c 单个字符 %d.%i 有符号的十进制整数 %e,%E 浮点数,e记数法 %f 浮点数,十进制计数法 %g/% ...
- Spring Framework学习总结
一.Spring 概述 Spring 有两个核心部分: IoC 和 AOP. Spring 是一种基于 Bean 的编程技术,它深刻地改变着 Java 开发世界.Spring 使用简单.基本的 Jav ...
- win10edge浏览器个人账户退出登录后再次登录自动登录问题
edge浏览器退出登录后,再次点击登录以同步数据会自动登录,可查看书签等个人数据 解决方法: 先在浏览器里面退出账户. 1.设置--电子邮件和账户--管理 2.登录后--安全--安全仪表板--高级安全 ...
- How to Apply WebLogic Server (WLS) Patches Using Smart Update
本文目的: 描述weblogic10.3.6及之前的版本,如何通过Smart Update打补丁 先决条件: You should download and apply the enhanced Sm ...