一个睡眠中的进程被唤醒后,应该去哪个cpu上去执行呢?
为方便称呼,执行唤醒流程的进程是waker,被唤醒进程是wakee。
显而易见,选择cpu是一个非常复杂的问题。
我们从try_to_wake_up
-->select_task_rq
此条路径出发,简单了解一下睡眠进程被唤醒过程中的选核思路。
select_task_rq
函数会选择wakee
的调度类中定义的select_task_rq
去执行。
这里以CFS中定义的select_task_rq_fair
函数为例进行分析。
先看函数开始部分:
static int
select_task_rq_fair(struct task_struct *p, int prev_cpu, int sd_flag, int wake_flags)
{
struct sched_domain *tmp, *sd = NULL;
int cpu = smp_processor_id();
int new_cpu = prev_cpu;
int want_affine = 0;
int sync = (wake_flags & WF_SYNC) && !(current->flags & PF_EXITING);
cpu
为waker
现在执行的cpu;
new_cpu
以及prev_cpu
为wakee
上次执行的cpu;
want_affine
用于表示是否满足亲和条件,在后续判断(如果满足亲和条件,则将进程唤醒到waker
现在执行的cpu上也是一个比较优的选择);
sync
是用来表示waker
与wakee
之间的关系的。
我们认为waker与wakee之间有两种关系:sync,以及non-sync。
sync——waker在唤醒wakee的时候知道自己很快进入睡眠状态,故最好不要进行抢占。
non-sync——没有同步关系,唤醒的时候,可以尝试触发一次调度。
在这里,sync
本质上是放宽了对waker
的cpu的idle的判断条件。
接着分析:
if (sd_flag & SD_BALANCE_WAKE) {
record_wakee(p);
if (sched_energy_enabled()) {
new_cpu = find_energy_efficient_cpu(p, prev_cpu);
if (new_cpu >= 0)
return new_cpu;
new_cpu = prev_cpu;
}
want_affine = !wake_wide(p) && cpumask_test_cpu(cpu, p->cpus_ptr);
}
进程唤醒可以视为一次主动的BALANCE。
record_wakee
主要用于更新waker
的wakee_flips
,用于后续的判断。
static void record_wakee(struct task_struct *p)
{
/*
* Only decay a single time; tasks that have less then 1 wakeup per
* jiffy will not have built up many flips.
*/
if (time_after(jiffies, current->wakee_flip_decay_ts + HZ)) {
current->wakee_flips >>= 1;
current->wakee_flip_decay_ts = jiffies;
}
if (current->last_wakee != p) {
current->last_wakee = p;
current->wakee_flips++;
}
}
task_struct
中有三个成员:
wakee_flips_decay_ts
表示上次进行衰减的时刻;
last_wakee
为上次由它唤醒的进程;
wakee_flips
度量由它唤醒进程的数量,wakee_flips
一秒之前的计数全部除以2,有点衰减的那味儿了。
sched_energy_enabled
分支的部分,与功耗EAS调度器有关,暂不分析。
可以明确的是,find_energy_efficient_cpu
用于在开启功耗模型下寻找功耗最低的cpu去执行。
wake_wide
函数检测waker
/wakee
是否符合wake_affine
模型。(最终用want_affine
表示)
当wake_wide
支持以及waker
所在的cpu可以执行wakee
时,want_affine
生效。
符合wake_affine
模型则优先去waker
现在执行以及wakee
上次执行的cpus中进行选择(放宽对waker
执行cpu的条件,最后从二者中挑选一个target cpu)。
static int wake_wide(struct task_struct *p)
{
unsigned int master = current->wakee_flips;
unsigned int slave = p->wakee_flips;
int factor = __this_cpu_read(sd_llc_size);
if (master < slave)
swap(master, slave);
if (slave < factor || master < slave * factor)
return 0;
return 1;
}
sd_llc_domain
为当前sched_domain
中能够共享cache的CPU数目;
将waker
和wakee
中wakee_flips
中较大的称为master
,较小的称为slave
。
当较小的wakee_flips
或者较大的wakee_flips
与较小的wakee_flips
的比值小于sd_llc_size
是可以作为wake_affine
生效的一部分判断依据。
继续看select_task_rq_fair
函数:
rcu_read_lock();
for_each_domain(cpu, tmp) {
/*
* If both 'cpu' and 'prev_cpu' are part of this domain,
* cpu is a valid SD_WAKE_AFFINE target.
*/
if (want_affine && (tmp->flags & SD_WAKE_AFFINE) &&
cpumask_test_cpu(prev_cpu, sched_domain_span(tmp))) {
if (cpu != prev_cpu)
new_cpu = wake_affine(tmp, p, cpu, prev_cpu, sync);
sd = NULL; /* Prefer wake_affine over balance flags */
break;
}
if (tmp->flags & sd_flag)
sd = tmp;
else if (!want_affine)
break;
}
以waker
执行的cpu调度域逐步向上,进行如下判断:
当want_affine
成立,并且调度域满足亲和性条件,并且waker
的cpu与wakee
上次执行cpu在同一调度域中时,sd
为NULL。
如果条件均不满足,则sd
置为最后满足sd_flag
的调度域。
如果满足want_affine
,退出。
在满足第1个条件,且waker
执行的cpu与wakee
上次执行cpu不相等的情况下,执行wake_affine
进行选择。
wake_affine
本质上是在waker
现在执行的cpu以及wakee
上次执行的cpu进行对比,决定要不要给waker
现在执行的cpu一点机会。
static int wake_affine(struct sched_domain *sd, struct task_struct *p,
int this_cpu, int prev_cpu, int sync)
{
int target = nr_cpumask_bits;
if (sched_feat(WA_IDLE))
target = wake_affine_idle(this_cpu, prev_cpu, sync);
if (sched_feat(WA_WEIGHT) && target == nr_cpumask_bits)
target = wake_affine_weight(sd, p, this_cpu, prev_cpu, sync);
schedstat_inc(p->se.statistics.nr_wakeups_affine_attempts);
if (target == nr_cpumask_bits)
return prev_cpu;
schedstat_inc(sd->ttwu_move_affine);
schedstat_inc(p->se.statistics.nr_wakeups_affine);
return target;
}
首先,如果waker
执行的cpu(此时waker
为idle,中断唤醒wakee
)为空闲或者很快进入空闲态或者wakee
上次执行的cpu为空闲,则通过wake_affine_idle
可以找到一个合适的target cpu。
static int
wake_affine_idle(int this_cpu, int prev_cpu, int sync)
{
if (available_idle_cpu(this_cpu) && cpus_share_cache(this_cpu, prev_cpu))
return available_idle_cpu(prev_cpu) ? prev_cpu : this_cpu;
if (sync && cpu_rq(this_cpu)->nr_running == 1)
return this_cpu;
return nr_cpumask_bits;
}
如果waker
的cpu空闲(中断唤醒)并且waker
执行的cpu与wakee
上次执行的cpu共享cache时:
wakee
上次执行的cpu也空闲,那么回到上次执行的cpu,否则是waker
的cpu。
否则sync
为1(表示waker
很快要退出)并且只有一个进程在执行,那么返回waker
执行的cpu。
如果在wake_affine_idle
中没有找到target cpu,则进入wake_affine_weight
中继续寻找。
static int
wake_affine_weight(struct sched_domain *sd, struct task_struct *p,
int this_cpu, int prev_cpu, int sync)
{
s64 this_eff_load, prev_eff_load;
unsigned long task_load;
this_eff_load = cpu_load(cpu_rq(this_cpu));
if (sync) {
unsigned long current_load = task_h_load(current);
if (current_load > this_eff_load)
return this_cpu;
this_eff_load -= current_load;
}
task_load = task_h_load(p);
this_eff_load += task_load;
if (sched_feat(WA_BIAS))
this_eff_load *= 100;
this_eff_load *= capacity_of(prev_cpu);
prev_eff_load = cpu_load(cpu_rq(prev_cpu));
prev_eff_load -= task_load;
if (sched_feat(WA_BIAS))
prev_eff_load *= 100 + (sd->imbalance_pct - 100) / 2;
prev_eff_load *= capacity_of(this_cpu);
if (sync)
prev_eff_load += 1;
return this_eff_load < prev_eff_load ? this_cpu : nr_cpumask_bits;
}
waker
的负载以及cpu负载,在waker
即将执行完成的情况下,cpu负载减去进程负载;waker
的cpu负载加上wakee
的进程负载wakee
的cpu负载减去wakee
的进程负载(此处是假设进程从上次执行的cpu迁移到waker
执行的cpu)最终,哪个cpu负载小,最后return哪个cpu。
如果sd
不为空,则进入find_idlest_cpu
这条慢速路径。
否则,进入select_idle_sibling
快速路径。
if (unlikely(sd)) {
/* Slow path */
new_cpu = find_idlest_cpu(sd, p, cpu, prev_cpu, sd_flag);
} else if (sd_flag & SD_BALANCE_WAKE) { /* XXX always ? */
/* Fast path */
new_cpu = select_idle_sibling(p, prev_cpu, new_cpu);
if (want_affine)
current->recent_used_cpu = cpu;
}
rcu_read_unlock();
return new_cpu;
}
find_idlest_cpu
为SMP负载均衡的常用函数,即选择负载最小的cpu去执行。
select_idle_sibling
在之前挑选出来的target cpu与上一次执行的cpu(有可能taget与上一次执行是一个cpu)共享cache的调度域中,根据负载选择一个最合适的cpu去执行。
cpu的选择,主要有以下关键点(满足SD_BALANCE_WAKE):
wake_wide
(依据唤醒进程的强度)以及进程可执行的cpu掩码决定满足waker
执行cpu的亲和性;wake_affine
来判断要不要给唤醒wakee
的cpu一点执行进程的机会;find_idlest_cpu
(慢速路径),进行负载均衡的选择;select_idle_sibling
,在wake_affine
挑选出来的target cpu以及上一次执行的cpu共享cache的调度域中,根据负载选择一个cpu进行执行。本文由哈喽比特于3年以前收录,如有侵权请联系我们。
文章来源:https://mp.weixin.qq.com/s/e5L82fcuQ0XhEhvTcxtXuw
京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。
日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。
据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。
今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。
日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。
近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。
据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。
9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...
9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。
据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。
特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。
据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。
近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。
据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。
9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。
《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。
近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。
社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”
2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。
罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。