(资料图)
#includeView Code#include #include #include #include #include #include #include #include #include #include using namespace std;std::string sub_cgroup_dir("/sys/fs/cgroup/cpu/test");// common libbool is_dir(const std::string& path){ struct stat statbuf; if (stat(path.c_str(), &statbuf) == 0 ) { if (0 != S_ISDIR(statbuf.st_mode)) { return true; } } return false;}bool write_file(const std::string& file_path, int num){ FILE* fp = fopen(file_path.c_str(), "w"); if (fp = NULL) { return false; } std::string write_data = to_string(num); fputs(write_data.c_str(), fp); fclose(fp); return true;}// mslong get_ms_timestamp(){ timeval tv; gettimeofday(&tv, NULL); return (tv.tv_sec * 1000 + tv.tv_usec / 1000);}// cgroupbool create_cgroup(){ if (is_dir(sub_cgroup_dir) == false) { if (mkdir(sub_cgroup_dir.c_str(), S_IRWXU | S_IRGRP) != 0) { cout << "mkdir cgroup dir fail" << endl; return false; } } int pid = getpid(); cout << "pid is " << pid << endl; std::string procs_path = sub_cgroup_dir + "/cgroup.procs"; return write_file(procs_path, pid);}bool set_period(int period){ std::string period_path = sub_cgroup_dir + "/cpu.cfs_period_us"; return write_file(period_path, period);}bool set_quota(int quota){ std::string quota_path = sub_cgroup_dir + "/cpu.cfs_quota_us"; return write_file(quota_path, quota);}// thread// param: ms intervalvoid* thread_func(void* param){ int i = 0; int interval = (long)param; long last = get_ms_timestamp(); while (true) { i++; if (i % 1000 != 0) { continue; } long current = get_ms_timestamp(); if ((current - last) >= interval) { usleep(1000); last = current; } } pthread_exit(NULL);} void test_thread() { const int k_thread_num = 10; pthread_t pthreads[k_thread_num]; for (int i = 0; i < k_thread_num; i++) { if (pthread_create(&pthreads[i], NULL, thread_func, (void*)(i + 1)) != 0) { cout << "create thread fail" << endl; } else { cout << "create thread success,tid is " << pthreads[i] << endl; } }}//argv[0] : period//argv[1] : quotaint main(int argc,char* argv[]){ if (argc <3) { cout << "usage : ./inactive timer $period $quota" << endl; return -1; } int period = stoi(argv[1]); int quota = stoi(argv[2]); cout << "period is " << period << endl; cout << "quota is " << quota << endl; test_thread(); if (create_cgroup() == false) { cout << "create cgroup fail" << 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;}
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 100002.1.4 上述代码主要干了2件事1> 将自己进程设置为CGroup控制cpu2> 反复设置CGroup的cpu.cfs_period_us和cpu.cfs_quota_us3> 起10个线程消耗cpu2.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使用率也一直是02> 查看线程的stack,task都在系统调用返回中3> 用crash查看进程的主线程32764状态确实为"running",但对应的0号cpu上的rq cfs运行队列中并没有任何运行task4> 查看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切换到线程A3> 线程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_cfs_bandwidth()上发生线程竞争,导致period_timer被cancel后不再激活,然后CGroup控制的task不能分配cpu quota,导致不再被调度3.5 恢复方法知道了漏洞成因,我们也看到tg_set_cfs_quota()会调用__start_cfs_bandwidth() cancel掉timer,然后重新激活timer,这样就能在timer回调中unthrottle了,所以只要手动设置下这个CGroup的cpu.cfs_period_us或cpu.cfs_quota_us,就能恢复运行。4. 修复3.10.0-693以上的版本并不会出现这个问题,通过和2.6.32版本(下图右边)的代码对比,可知3.10.0-693版的代码(下图左边)将hrtimer_cancel()该为hrtimer_try_to_cancel(),并将其和cfs_b->timer_active的判定都放在自旋锁中保护,这样就不会cfs_b->timer_active被置1后,仍然还会去cancel period_timer的问题了,但看这个bug fix的邮件组讨论,是为了修另一个问题顺便把这个问题也修了,痛失给linux提patch的机会- -ref : https://gfiber.googlesource.com/kernel/bruno/+/09dc4ab03936df5c5aa711d27c81283c6d09f4955. 漏洞利用1> 在国内,仍有大量的公司在使用CentOS6和CentOS7.0~7.5,这些系统都存在这个漏洞,使用了CGroup限制cpu就有可能触发这个bug导致业务中断,且还不一定能重启恢复2> 一旦触发这个bug,由于task本身已经是running状态了,即使去kill,由于task得不到调度,是无法kill掉的,因此可以通过这种方法攻击任意软件程序(如杀毒软件),让其不能执行又不能重启(很多程序为了保证不双开,都会只保证只有一个进程存在),即使他们不用CGroup,也可以给他建一个对其进行攻击3> 该bug由于是linux内核bug,一旦触发还不易排查和感知,因为看进程状态都是running,直觉上认为进程仍然在正常执行的
1 说明1>本篇是实际工作中linux上碰到的一个问题,一个使用了CGroup的进程处于R状态但不执行,也不退出,还不能kill,经过深入挖掘才发现是Cgr
一、目字的笔顺的部首1、部首:目2、笔画:53、繁体:目4、五笔:HHHH二、目字的笔顺的笔顺5、目字的笔顺的笔顺:丨
【国内增压能力最大的海上循环注气平台机械完工】据新华社,4月14日,记者从中国海油获悉,渤海油田首个高压注气平台——渤中19-6凝析气田I...
不到1分钟,路政人员赶到事故现场台城站入口广场,眼前一幕让人揪心:两辆货车车头相撞,其中一辆车头严重变形损坏,2人从车内走出用纸巾捂着
孕妇可以适量吃甘蔗。孕妇在怀孕期间会消耗大量的营养和能量。甘蔗含糖量高,能为身体补充足够的能量,所以孕妇可以适量食用甘蔗
一彬科技(001278)04月15日在投资者关系平台上答复了投资者关心的问题。
俄外长:“去美元化”趋势不可逆转据俄罗斯媒体报道,当地时间14日,俄罗斯外长拉夫罗夫表示,在国际贸易中放弃使用美元结算转而使用本国货...
王小川回怼百度副总裁今日凌晨,王小川发文回应百度副总裁肖阳称其脱离一线太久,表示:“什么样的平行宇宙里,才能做到让一个脱离一线15年...
一季度新能源车市的发展速度已明显放缓,以造车新势力和传统新造车为代表的新能源车企酣战正浓,强者恒强、弱者恒弱的分化更加明显。版图之争
1、首先要在手机上面登录自己的微信账号,然后在聊天窗口上面点击群聊的信息。2、进入到群聊窗口以后,在窗口上面点击右上角的头像。3、点击头
1、多刷图,打到蓝色装备拿到诺顿那里分解,所有的晶块都留好,观察市场行情,全部以低于市场行情20%的价格脱手。2、另外到
1、今年涨价比较多;我们桐乡那边今年是330一斤。2、春蚕丝。本文到此分享完毕,希望对大家有所帮助。
新华社华盛顿4月14日电(记者潘丽君杨士龙)国际货币基金组织(IMF)总裁格奥尔基耶娃13日表示,全球贸易碎片化会让世界付出代价,甚至可能引
国芳集团(SH601086,收盘价:3 98元)4月14日晚间发布公告称,蒋勇先生因个人原因辞去非职工代表监事职务,其原定任期至第六届监事会届满之日
据微信公众号“中国光伏行业协会CPIA”,2023年4月14日下午,中国光伏行业协会组织召开2023年第一季度光伏行业发
今天小编肥嘟来为大家解答以上的问题。cad如何设置比例尺寸,cad怎样设置比例尺相信很多小伙伴还不知道,现在让我们一起来看看吧!1、在放大的
大连通报4起乡村振兴领域不正之风和腐败问题典型案例
现代快报网是由凤凰出版传媒集团旗下的现代快报倾力打造的江苏新闻门户网站,目前在南京、苏州、无锡、常州、扬州、泰州等十三地市,影响力辐射
500)this width=500 "align= "center "hspace= "10 "vspace= "10 "rel= "nofollow ">易车讯近日
1、1 先看看什么是商业用途吧!商业金融信息商业服务指各类商店、门市部、饮食店、粮油店、菜场、理发店、照相馆、浴
配图来自Canva可画五一小长假未到,大学生、职场人就早早制定了旅游路线,准备了旅游装备,还预订了机票和酒店。这边消费者们旅游之心急不可待
作为2022-2023赛季花滑项目的收官赛事,2023国际滑联世界花滑团体锦标赛于4月13日至4月16日在日本东京举办,参赛的队伍是该赛季世界排名前六的
4月14日中庚基金旗下丘栋荣管理的中庚小盘价值股票型基金公布一季报,近1年净值增长率19 49%。与上一季度相比,该基金前十大重仓股新增华统股
什么样的上衣配阔腿裤,对于现代女性来说,阔腿裤确实是一种非常时尚的裤子款式。尤其是对于一些职场女性来说,这种裤腿裤款式在职场服装搭配
光明日报北京4月13日电(记者杨舒)自然资源部海洋战略规划与经济司于13日发布《2022年中国海洋经济统计公报》(以下简称《公报》)。《公报》
本报记者王僖近期,淄博烧烤频频出圈,带动山东省成为今年“五一”假期国内游的新“顶流”。美团、大众点评数据显示,截至4月10日,山东省...
1、在微信页点击“我的”。2、点击“支付”。3、点击右上角的“三个点”。4、点击“实名认证”。5、点击“账户更名”,点击“确认注销”。6...
1、不要做廉价的自己,不要随意去付出,不要一厢情愿去迎合别人,圈子不同,不必强融,将时间浪费在别人身上,倒不如专心做自己喜欢的事情;2
1、羚羊系列是长安铃木生产的经济型轿车,羚羊是一辆紧凑型三厢轿车,它的整体外观以直线条为主。2、棱角分明的车身设计个性鲜
华天科技00218504月13日在投资者关系平台上答复了投资者关心的问题投资者公司有存储芯片封装测试业务吗华天科技董秘公司有存储芯片封装测试业