极简组调度-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 ...
随机推荐
- C#和C++差异化对比
这里只记录和C++面向对象的区别,也并无比较成分,只做差异化学习使用. 1. 访问修饰符区别:多了一个Internal:成员仅能被同一个项目中的代码访问. 2. 字段的访问:增加了Get,Set访问器 ...
- JAVA 在开发中如何选择集合实现类
先判断存储地类型(一组对象[单列]或者键值对[多列]) 一组对象:Collection接口 允许重复:list 增删多:LinkedList[底层维护了一个双向链表] 改查多;ArratList[底层 ...
- 源代码管理工具介绍(以GITHUB为例)
Github:全球最大的社交编程及代码托管网站,可以托管各种git库,并提供一个web界面 1.基本概念 仓库(Repository):用来存放项目代码,每个项目对应一个仓库,多个开源项目则有多个仓库 ...
- mysql知识点二
1.mysql中的语言有哪些?分别代表什么意思1.DDL(Data Define Language) 数据定义语言2.DML(Data Manipulation Language) 数据操作语言3.D ...
- 代码还是那个代码,但我已经知道了hashmap背后的东西
代码还是那个代码,但我已经知道了hashmap背后的东西 数据结构是链表的数组(注:后面的版本为了提升性能,已经是改成链表或者树(节点较多)了) 思想上是空间换时间的算法 构造函数上有容量和负载因子2 ...
- redis单机搭建
1.获取redis wget http://download.redis.io/releases/redis-6.2.7.tar.gz tar -xvf redis-6.2.7.tar.gz 2.安装 ...
- js 小数和百分数的转换
百分数转化为小数 function toPoint(percent){ var str=percent.replace("%",""); str= str/10 ...
- Python项目案例开发从入门到实战 - 书籍信息
Python项目案例开发从入门到实战 - 爬虫.游戏和机器学习(微课版) 作者:郑秋生 夏敏捷 清华大学出版社 ISBN:978-7-302-45970-5
- 什么是Placement new ?
1. 什么是placementNew placement new的作用就是:创建对象(调用该类的构造函数)但是不分配内存,而是在已有的内存块上面创建对象.用于需要反复创建并删除的对象上,可以降低分配释 ...
- win10、win11安装子系统kali linux、图形化界面的安装
1.开启安装Linux子系统需要的扩展 Win+Q搜索功能 勾选需要的扩展,Hyper-V.Windows 虚拟机监控平台.适用于Linux的Windows子系统.虚拟机平台 反正这些有关于虚拟机的全 ...