设为首页 加入收藏

TOP

频繁设置CGroup触发linux内核bug导致CGroup running task不调度(二)
2023-07-23 13:34:05 】 浏览:53
Tags:CGroup 触发 linux 内核 bug 导致 running task
uot;
<< endl; return -1; } int i =0; while (true) { if (i > 20) { i = 0; } i++; long current = get_ms_timestamp(); long last = current; while ((current - last) < i) { usleep(1000); current = get_ms_timestamp(); } set_period(period); set_quota(quota); } return 0; } View Code

 

2.1.2 编译

g++ -std=c++11 -lpthread trigger_cgroup_timer_inactive.cpp -o inactive_timer

 

2.1.3 在CentOS7.0~7.5的系统上执行程序

./inactive_timer 100000 10000

 

2.1.4 上述代码主要干了2件事
1> 将自己进程设置为CGroup控制cpu
2> 反复设置CGroup的cpu.cfs_period_us和cpu.cfs_quota_us
3> 起10个线程消耗cpu
 
2.1.5《极简组调度-CGroup如何限制cpu》已经讲过CGroup限制cpu的原理:
CGroup控制cpu是通过cfs_period_us指定的一个时间周期内,CGroup下的进程,能使用cfs_quota_us时间长度的cpu,如果在该周期内使用的cpu超过了cfs_quota_us设定的值,则将其throttled,即将其从公平调度运行队列中移出,然后等待定时器触发下个周期unthrottle后再移入,从而达到控制cpu的效果。
 
2.2 现象
1> 程序跑几分钟后,所有的线程一直处于running状态,但实际线程都已经不再执行了,cpu使用率也一直是0
 
2> 查看线程的stack,task都在系统调用返回中
 
3> 用crash查看进程的主线程32764状态确实为"running",但对应的0号cpu上的rq cfs运行队列中并没有任何运行task
 
4> 查看task对应的se没有在rq上,cfs_rq显示被throttled
《极简组调度-CGroup如何限制cpu》中说过,throttle后经过一个period(程序设的是100ms),CGroup的定时器会再次分配quota,并unthrottle,将group se重新加入到rq中,这里一直throttle不恢复,只能怀疑是不是定时器出问题了
 
5> 再查看task group对应的cfs_bandwidth的period timer,发现state为0,即HRTIMER_STATE_INACTIVE,表示未激活,问题就在这里, 正常情况下该timer是激活的,该定时器未激活会导致对应cpu上的group cfs_rq分配不到quota,quota用完后就会导致其对应的se被移出rq,此时task虽然处于Ready状态,但由于不在rq上,仍然不会被调度的
 
3. 原因
3.1 linux的定时器是一次性,到期后需要再次激活才能继续使用,搜索代码可知period_timer是在__start_cfs_bandwidth()中实现调用start_bandwidth_timer()进行激活的
这里有一个关键点,当cfs_b->timer_active不为0时,__start_cfs_bandwidth()就会不激活period_timer,和问题现象相符,那么什么时候cfs_b->timer_active会不为0呢?
 
3.2 当设置CGroup的quota或者period时,会最终进入到__start_cfs_bandwidth(),这里就会将cfs_b->timer_active设为0,并进入__start_cfs_bandwidth()
tg_set_cfs_quota()
    tg_set_cfs_bandwidth()
            /* restart the period timer (if active) to handle new period expiry */
            if (runtime_enabled && cfs_b->timer_active) {
                /* force a reprogram */
                cfs_b->timer_active = 0;
                __start_cfs_bandwidth(cfs_b);
            }
仔细观察上述代码,设想如下场景:
1> 在线程A设置CGroup的quota或者period时,将cfs_b->timer_active设为0,调用_start_cfs_bandwidth()后,在未执行到__start_cfs_bandwidth()代码580行hrtimer_cancel()之前,cpu切换到B线程
2> 线程B也调用__start_cfs_bandwidth(),执行完后将cfs_b->timer_active设为1,并调用start_bandwidth_timer()激活timer,此时cpu切换到线程A
3> 线程A恢复并继续执行,调用hrtimer_cancel()让period_timer失效,然后执行到__start_cfs_bandwidth()代码585行后,发现cfs_b->timer_active为1,直接return,而不再将period_timer激活
 
3.3 搜索__start_cfs_bandwidth()的调用,发现时钟中断中会调用update_curr()函数,其最终会调用assign_cfs_rq_runtime()检查cgroup cpu配额使用情况,决定是否需要throttle,这里在cfs_b->timer_active = 0时,也会调用__start_cfs_bandwidth(),即执行上面B线程的代码,从而和设置CGroup的线程A发生线程竞争,导致timer失效。
1> 完整代码执行流程图

 

 

2> 当定时器失效后,由于3.2中线程B将cfs_b->timer_active = 1,所以即使下次时钟中断执行到assign_cfs_rq_runtime()中时,由于误判timer是active的,也不会调用__start_cfs_bandwidth()再次激活timer,这样被throttle的group se永远不会被unthrottle投入rq调度了
 
3.4 总结
频繁设置CGroup配置,会和时钟中断中检查group quota的线程在__start_
首页 上一页 1 2 3 下一页 尾页 2/3/3
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇bash shell 无法使用 perl 正则 下一篇Linux中如何通过yum或者apt下载安..

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目