0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

Linux是如何對(duì)容器下的進(jìn)程進(jìn)行CPU限制的,底層是如何工作的?

dyquk4xk2p3d ? 來源:開發(fā)內(nèi)功修煉 ? 2023-11-29 14:31 ? 次閱讀

現(xiàn)在很多公司的服務(wù)都是跑在容器下,我來問幾個(gè)容器 CPU 相關(guān)的問題,看大家對(duì)天天在用的技術(shù)是否熟悉。

容器中的核是真的邏輯核嗎?

Linux 是如何對(duì)容器下的進(jìn)程進(jìn)行 CPU 限制的,底層是如何工作的?

容器中的 throttle 是什么意思?

為什么關(guān)注容器 CPU 性能的時(shí)候,除了關(guān)注使用率,還要關(guān)注 throttle 的次數(shù)和時(shí)間?

和真正使用物理機(jī)不同,Linux 容器中所謂的核并不是真正的 CPU 核。所以在理解容器 CPU 性能的時(shí)候,必然要有一些特殊的地方需要考慮。

各家公司的容器云上,底層不管使用的是 docker 引擎,還是 containerd 引擎,都是依賴 Linux 的 cgroup 的 cpu 子系統(tǒng)來工作的,所以今天我們就來深入地學(xué)習(xí)一下 cgroup cpu 子系統(tǒng) 。理解了這個(gè),你將會(huì)對(duì)容器進(jìn)程的 CPU 性能有更深入的把握。

一、cgroup 的 cpu 子系統(tǒng)

在 Linux 下, cgroup 提供了對(duì) CPU、內(nèi)存等資源實(shí)現(xiàn)精細(xì)化控制的能力。它的全稱是 control groups。允許對(duì)某一個(gè)進(jìn)程,或者一組進(jìn)程所用到的資源進(jìn)行控制?,F(xiàn)在流行的 Docker 就是在這個(gè)底層機(jī)制上成長(zhǎng)起來的。

在你的機(jī)器執(zhí)行執(zhí)行下面的命令可以查看當(dāng)前 cgroup 都支持對(duì)哪些資源進(jìn)行控制。

$lssubsys-a
cpuset
cpu,cpuacct
...

其中 cpu 和 cpuset 都是對(duì) CPU 資源進(jìn)行控制的子系統(tǒng)。cpu 是通過執(zhí)行時(shí)間來控制進(jìn)程對(duì) cpu 的使用,cpuset 是通過分配邏輯核的方式來分配 cpu。其它可控制的資源還包括 memory(內(nèi)存)、net_cls(網(wǎng)絡(luò)帶寬)等等。

cgroup 提供了一個(gè)原生接口并通過 cgroupfs 提供控制。類似于 procfs 和 sysfs,是一種虛擬文件系統(tǒng)。默認(rèn)情況下 cgroupfs 掛載在 /sys/fs/cgroup 目錄下,我們可以通過修改 /sys/fs/cgroup 下的文件和文件內(nèi)容來控制進(jìn)程對(duì)資源的使用。

比如,想實(shí)現(xiàn)讓某個(gè)進(jìn)程只使用兩個(gè)核,我們可以通過 cgroupfs 接口這樣來實(shí)現(xiàn),如下:

#cd/sys/fs/cgroup/cpu,cpuacct
#mkdirtest
#cdtest
#echo100000>cpu.cfs_period_us//100ms
#echo100000>cpu.cfs_quota_us//200ms
#echo{$pid}>cgroup.procs

其中 cfs_period_us 用來配置時(shí)間周期長(zhǎng)度,cfs_quota_us 用來配置當(dāng)前 cgroup 在設(shè)置的周期長(zhǎng)度內(nèi)所能使用的 CPU 時(shí)間。這兩個(gè)文件配合起來就可以設(shè)置 CPU 的使用上限。

上面的配置就是設(shè)置改 cgroup 下的進(jìn)程每 100 ms 內(nèi)只能使用 200 ms 的 CPU 周期,也就是說限制使用最多兩個(gè)“核”。

要注意的是這種方式只限制的是 CPU 使用時(shí)間,具體調(diào)度的時(shí)候是可能會(huì)調(diào)度到任意 CPU 上執(zhí)行的。如果想限制進(jìn)程使用的 CPU 核,可以使用 cpuset 子系統(tǒng)。

docker 默認(rèn)情況下使用的就是 cgroupfs 接口,可以通過如下的命令來確認(rèn)。

#dockerinfo|grepcgroup
CgroupDriver:cgroupfs

二、內(nèi)核中進(jìn)程和 cgroup 的關(guān)系

在上一節(jié)中,我們?cè)?/sys/fs/cgroup/cpu,cpuacct 創(chuàng)建了一個(gè)目錄 test,這其實(shí)是創(chuàng)建了一個(gè) cgroup 對(duì)象。當(dāng)我們把某個(gè)進(jìn)程的 pid 添加到 cgroup 后,又是建立了進(jìn)程結(jié)構(gòu)體和 cgroup 之間的關(guān)系。

所以要想理解清 cgroup 的工作過程,就得先來了解一下 cgroup 和 task_struct 結(jié)構(gòu)體之間的關(guān)系。

2.1 cgroup 內(nèi)核對(duì)象

一個(gè) cgroup 對(duì)象中可以指定對(duì) cpu、cpuset、memory 等一種或多種資源的限制。我們先來找到 cgroup 的定義。

//file:include/linux/cgroup-defs.h
structcgroup{
...
structcgroup_subsys_state__rcu*subsys[CGROUP_SUBSYS_COUNT];
...
}

每個(gè) cgroup 都有一個(gè) cgroup_subsys_state 類型的數(shù)組 subsys,其中的每一個(gè)元素代表的是一種資源控制,如 cpu、cpuset、memory 等等。

76ae7904-8e7f-11ee-939d-92fbcf53809c.png

這里要注意的是,其實(shí) cgroup_subsys_state 并不是真實(shí)的資源控制統(tǒng)計(jì)信息結(jié)構(gòu),對(duì)于 CPU 子系統(tǒng)真正的資源控制結(jié)構(gòu)是 task_group。它是 cgroup_subsys_state 結(jié)構(gòu)的擴(kuò)展,類似父類和子類的概念。

76cb2f72-8e7f-11ee-939d-92fbcf53809c.png

當(dāng) task_group 需要被當(dāng)成 cgroup_subsys_state 類型使用的時(shí)候,只需要強(qiáng)制類型轉(zhuǎn)換就可以。

對(duì)于內(nèi)存子系統(tǒng)控制統(tǒng)計(jì)信息結(jié)構(gòu)是 mem_cgroup,其它子系統(tǒng)也類似。

76e616d4-8e7f-11ee-939d-92fbcf53809c.png

之所以要這么設(shè)計(jì),目的是各個(gè) cgroup 子系統(tǒng)都統(tǒng)一對(duì)外暴露 cgroup_subsys_state,其余部分不對(duì)外暴露,在自己的子系統(tǒng)內(nèi)部維護(hù)和使用。

2.2 進(jìn)程和 cgroup 子系統(tǒng)

一個(gè) Linux 進(jìn)程既可以對(duì)它的 cpu 使用進(jìn)行限制,也可以對(duì)它的內(nèi)存進(jìn)行限制。所以,一個(gè)進(jìn)程 task_struct 是可以和多種子系統(tǒng)有關(guān)聯(lián)關(guān)系的。

和 cgroup 和多個(gè)子系統(tǒng)關(guān)聯(lián)定義類似,task_struct 中也定義了一個(gè) cgroup_subsys_state 類型的數(shù)組 subsys,來表達(dá)這種一對(duì)多的關(guān)系。

771f190c-8e7f-11ee-939d-92fbcf53809c.png

我們來簡(jiǎn)單看下源碼的定義。

//file:include/linux/sched.h
structtask_struct{
...
structcss_set__rcu*cgroups;
...
}
//file:include/linux/cgroup-defs.h
structcss_set{
...
structcgroup_subsys_state*subsys[CGROUP_SUBSYS_COUNT];
}

其中subsys是一個(gè)指針數(shù)組,存儲(chǔ)一組指向 cgroup_subsys_state 的指針。一個(gè) cgroup_subsys_state 就是進(jìn)程與一個(gè)特定的子系統(tǒng)相關(guān)的信息。

通過這個(gè)指針,進(jìn)程就可以獲得相關(guān)聯(lián)的 cgroups 控制信息了。能查到限制該進(jìn)程對(duì)資源使用的 task_group、cpuset、mem_group 等子系統(tǒng)對(duì)象。

2.3 內(nèi)核對(duì)象關(guān)系圖匯總

我們把上面的內(nèi)核對(duì)象關(guān)系圖匯總起來看一下。

7743f7d6-8e7f-11ee-939d-92fbcf53809c.png

可以看到無論是進(jìn)程、還是 cgroup 對(duì)象,最后都能找到和其關(guān)聯(lián)的具體的 cpu、內(nèi)存等資源控制自系統(tǒng)的對(duì)象。

2.4 cpu 子系統(tǒng)

因?yàn)榻裉煳覀冎攸c(diǎn)是介紹進(jìn)程的 cpu 限制,所以我們把 cpu 子系統(tǒng)相關(guān)的對(duì)象 task_group 專門拿出來理解理解。

//file:kernel/sched/sched.h
structtask_group{
structcgroup_subsys_statecss;
...

//task_group樹結(jié)構(gòu)
structtask_group*parent;
structlist_headsiblings;
structlist_headchildren;

//task_group持有的N個(gè)調(diào)度實(shí)體(N=CPU核數(shù))
structsched_entity**se;

//task_group自己的N個(gè)公平調(diào)度隊(duì)列(N=CPU核數(shù))
structcfs_rq**cfs_rq;

//公平調(diào)度帶寬限制
structcfs_bandwidthcfs_bandwidth;
...
}

第一個(gè) cgroup_subsys_state css 成員我們?cè)谇懊嬲f過了,這相當(dāng)于它的“父類”。再來看 parent、siblings、children 等幾個(gè)對(duì)象。這些成員是樹相關(guān)的數(shù)據(jù)結(jié)構(gòu)。在整個(gè)系統(tǒng)中有一個(gè) root_task_group。

//file:kernel/sched/core.c
structtask_grouproot_task_group;

所有的 task_group 都是以 root_task_group 為根節(jié)點(diǎn)組成了一棵樹。

接下來的 se 和 cfs_rq 是完全公平調(diào)度的兩個(gè)對(duì)象。它們兩都是數(shù)組,元素個(gè)數(shù)等于當(dāng)前系統(tǒng)的 CPU 核數(shù)。每個(gè) task_group 都會(huì)在上一級(jí) task_group(比如 root_task_group)的 N 個(gè)調(diào)度隊(duì)列中有一個(gè)調(diào)度實(shí)體。

cfs_rq 是 task_group 自己所持有的完全公平調(diào)度隊(duì)列。是的,你沒看錯(cuò)。每一個(gè) task_group 內(nèi)部都有自己的一組調(diào)度隊(duì)列,其數(shù)量和 CPU 的核數(shù)一致。

假如當(dāng)前系統(tǒng)有兩個(gè)邏輯核,那么一個(gè) task_group 樹和 cfs_rq 的簡(jiǎn)單示意圖大概是下面這個(gè)樣子。

775b0a20-8e7f-11ee-939d-92fbcf53809c.png

Linux 中的進(jìn)程調(diào)度是一個(gè)層級(jí)的結(jié)構(gòu)。對(duì)于容器來講,宿主機(jī)中進(jìn)行進(jìn)程調(diào)度的時(shí)候,先調(diào)度到的實(shí)際上不是容器中的具體某個(gè)進(jìn)程,而是一個(gè) task_group。然后接下來再進(jìn)入容器 task_group 的調(diào)度隊(duì)列 cfs_rq 中進(jìn)行調(diào)度,才能最終確定具體的進(jìn)程 pid。

還有就是 cpu 帶寬限制 cfs_bandwidth, cpu 分配的管控相關(guān)的字段都是在 cfs_bandwidth 中定義維護(hù)的。

cgroup 相關(guān)的內(nèi)核對(duì)象我們就先介紹到這里,接下來我們看一下 cpu 子系統(tǒng)到底是如何實(shí)現(xiàn)的。

三、CPU 子系統(tǒng)的實(shí)現(xiàn)

在第一節(jié)中我們展示通過 cgroupfs 對(duì) cpu 子系統(tǒng)使用,使用過程大概可以分成三步:

第一步:通過創(chuàng)建目錄來創(chuàng)建 cgroup

第二步:在目錄中設(shè)置 cpu 的限制情況

第三步:將進(jìn)程添加到 cgroup 中進(jìn)行資源管控

那本小節(jié)我們就從上面三步展開,看看在每一步中,內(nèi)核都具體做了哪些事情。限于篇幅所限,我們只講 cpu 子系統(tǒng),對(duì)于其他的子系統(tǒng)也是類似的分析過程。

3.1 創(chuàng)建 cgroup 對(duì)象

內(nèi)核定義了對(duì) cgroupfs 操作的具體處理函數(shù)。在 /sys/fs/cgroup/ 下的目錄創(chuàng)建操作都將由下面 cgroup_kf_syscall_ops 定義的方法來執(zhí)行。

//file:kernel/cgroup/cgroup.c
staticstructkernfs_syscall_opscgroup_kf_syscall_ops={
.mkdir=cgroup_mkdir,
.rmdir=cgroup_rmdir,
...
};

創(chuàng)建目錄執(zhí)行整個(gè)過程鏈條如下

vfs_mkdir
->kernfs_iop_mkdir
->cgroup_mkdir
->cgroup_apply_control_enable
->css_create
->cpu_cgroup_css_alloc

其中關(guān)鍵的創(chuàng)建過程有:

cgroup_mkdir:在這里創(chuàng)建了 cgroup 內(nèi)核對(duì)象

css_create:創(chuàng)建每一個(gè)子系統(tǒng)資源管理對(duì)象,對(duì)于 cpu 子系統(tǒng)會(huì)創(chuàng)建 task_group

cgroup 內(nèi)核對(duì)象是在 cgroup_mkdir 中創(chuàng)建的。除了 cgroup 內(nèi)核對(duì)象,這里還創(chuàng)建了文件系統(tǒng)重要展示的目錄。

//file:kernel/cgroup/cgroup.c
intcgroup_mkdir(structkernfs_node*parent_kn,constchar*name,umode_tmode)
{
...
//查找父cgroup
parent=cgroup_kn_lock_live(parent_kn,false);

//創(chuàng)建cgroup對(duì)象出來
cgrp=cgroup_create(parent);

//創(chuàng)建文件系統(tǒng)節(jié)點(diǎn)
kn=kernfs_create_dir(parent->kn,name,mode,cgrp);
cgrp->kn=kn;
...
}

在 cgroup 中,是有層次的概念的,這個(gè)層次結(jié)構(gòu)和 cgroupfs 中的目錄層次結(jié)構(gòu)一樣。所以在創(chuàng)建 cgroup 對(duì)象之前的第一步就是先找到其父 cgroup, 然后創(chuàng)建自己,并創(chuàng)建文件系統(tǒng)中的目錄以及文件。

在 cgroup_apply_control_enable 中,執(zhí)行子系統(tǒng)對(duì)象的創(chuàng)建。

//file:kernel/cgroup/cgroup.c
staticintcgroup_apply_control_enable(structcgroup*cgrp)
{
...
cgroup_for_each_live_descendant_pre(dsct,d_css,cgrp){
for_each_subsys(ss,ssid){
structcgroup_subsys_state*css=cgroup_css(dsct,ss);
css=css_create(dsct,ss);
...
}
}
return0;
}

通過 for_each_subsys 遍歷每一種 cgroup 子系統(tǒng),并調(diào)用其 css_alloc 來創(chuàng)建相應(yīng)的對(duì)象。

//file:kernel/cgroup/cgroup.c
staticstructcgroup_subsys_state*css_create(structcgroup*cgrp,
structcgroup_subsys*ss)
{
css=ss->css_alloc(parent_css);
...
}

上面的 css_alloc 是一個(gè)函數(shù)指針,對(duì)于 cpu 子系統(tǒng)來說,它指向的是 cpu_cgroup_css_alloc。這個(gè)對(duì)應(yīng)關(guān)系在 kernel/sched/core.c 文件仲可以找到

//file:kernel/sched/core.c
structcgroup_subsyscpu_cgrp_subsys={
.css_alloc=cpu_cgroup_css_alloc,
.css_online=cpu_cgroup_css_online,
...
};

通過 cpu_cgroup_css_alloc => sched_create_group 調(diào)用后,創(chuàng)建出了 cpu 子系統(tǒng)的內(nèi)核對(duì)象 task_group。

//file:kernel/sched/core.c
structtask_group*sched_create_group(structtask_group*parent)
{
structtask_group*tg;
tg=kmem_cache_alloc(task_group_cache,GFP_KERNEL|__GFP_ZERO);
...
}

3.2 設(shè)置 CPU 子系統(tǒng)限制

第一節(jié)中,我們通過對(duì) cpu 子系統(tǒng)目錄下的 cfs_period_us 和 cfs_quota_us 值的修改,來完成了 cgroup 中限制的設(shè)置。我們這個(gè)小節(jié)再看看看這個(gè)設(shè)置過程。

當(dāng)用戶讀寫這兩個(gè)文件的時(shí)候,內(nèi)核中也定義了對(duì)應(yīng)的處理函數(shù)。

//file:kernel/sched/core.c
staticstructcftypecpu_legacy_files[]={
...
{
.name="cfs_quota_us",
.read_s64=cpu_cfs_quota_read_s64,
.write_s64=cpu_cfs_quota_write_s64,
},
{
.name="cfs_period_us",
.read_u64=cpu_cfs_period_read_u64,
.write_u64=cpu_cfs_period_write_u64,
},
...
}

寫處理函數(shù) cpu_cfs_quota_write_s64、cpu_cfs_period_write_u64 最終又都是調(diào)用 tg_set_cfs_bandwidth 來完成設(shè)置的。

//file:kernel/sched/core.c
staticinttg_set_cfs_bandwidth(structtask_group*tg,u64period,u64quota)
{
//定位cfs_bandwidth對(duì)象
structcfs_bandwidth*cfs_b=&tg->cfs_bandwidth;
...

//對(duì)cfs_bandwidth進(jìn)行設(shè)置
cfs_b->period=ns_to_ktime(period);
cfs_b->quota=quota;
...
}

在 task_group 中,其帶寬管理控制都是由 cfs_bandwidth 來完成的,所以一開始就需要先獲取 cfs_bandwidth 對(duì)象。接著將用戶設(shè)置的值都設(shè)置到 cfs_bandwidth 類型的對(duì)象 cfs_b 上。

3.3 寫 proc 進(jìn) group

cgroup 創(chuàng)建好了,cpu 限制規(guī)則也制定好了,下一步就是將進(jìn)程添加到這個(gè)限制中。在 cgroupfs 下的操作方式就是修改 cgroup.procs 文件。

內(nèi)核定義了修改 cgroup.procs 文件的處理函數(shù)為 cgroup_procs_write。

//file:kernel/cgroup/cgroup.c
staticstructcftypecgroup_base_files[]={
...
{
.name="cgroup.procs",
...
.write=cgroup_procs_write,
},
}

在 cgroup_procs_write 的處理中,主要做了這么幾件事情。

第一、邏根據(jù)用戶輸入的 pid 來查找 task_struct 內(nèi)核對(duì)象。

第二、從舊的調(diào)度組中退出,加入到新的調(diào)度組 task_group 中

第三、修改進(jìn)程其 cgroup 相關(guān)的指針,讓其指向上面創(chuàng)建好的 task_group。

我們來看下加入新調(diào)度組的過程,內(nèi)核的調(diào)用鏈條如下。

cgroup_procs_write
->cgroup_attach_task
->cgroup_migrate
->cgroup_migrate_execute

在 cgroup_migrate_execute 中遍歷各個(gè)子系統(tǒng),完成每一個(gè)子系統(tǒng)的遷移。

staticintcgroup_migrate_execute(structcgroup_mgctx*mgctx)
{
do_each_subsys_mask(ss,ssid,mgctx->ss_mask){
if(ss->attach){
tset->ssid=ssid;
ss->attach(tset);
}
}while_each_subsys_mask();
...
}

對(duì)于 cpu 子系統(tǒng)來講,attach 對(duì)應(yīng)的處理方法是 cpu_cgroup_attach。這也是在 kernel/sched/core.c 下的 cpu_cgrp_subsys 中定義的。

cpu_cgroup_attach 調(diào)用 sched_move_task 來完成將進(jìn)程加入到新調(diào)度組的過程。

//file:kernel/sched/core.c
voidsched_move_task(structtask_struct*tsk)
{
//找到task所在的runqueue
rq=task_rq_lock(tsk,&rf);

//從runqueue中出來
queued=task_on_rq_queued(tsk);
if(queued)
dequeue_task(rq,tsk,queue_flags);

//修改task的group
//將進(jìn)程先從舊tg的cfs_rq中移除且更新cfs_rq的負(fù)載;再將進(jìn)程添加入新tg的cfs_rq并更新新cfs_rq的負(fù)載
sched_change_group(tsk,TASK_MOVE_GROUP);

//此時(shí)進(jìn)程的調(diào)度組已經(jīng)更新,重新將進(jìn)程加回runqueue
if(queued)
enqueue_task(rq,tsk,queue_flags);
...
}

這個(gè)函數(shù)做了三件事。

第一、先調(diào)用 dequeue_task 從原歸屬的 queue 中退出來,

第二、修改進(jìn)程的 task_group

第三、重新將進(jìn)程添加到新 task_group 的 runqueue 中。

//file:kernel/sched/core.c
staticvoidsched_change_group(structtask_struct*tsk,inttype)
{
structtask_group*tg;

//查找task_group
tg=container_of(task_css_check(tsk,cpu_cgrp_id,true),
structtask_group,css);
tg=autogroup_task_group(tsk,tg);

//修改task_struct所對(duì)應(yīng)的task_group
tsk->sched_task_group=tg;
...
}

進(jìn)程 task_struct 的 sched_task_group 是表示其歸屬的 task_group, 這里設(shè)置到新歸屬上。

四、進(jìn)程 CPU 帶寬控制過程

在前面的操作完畢之后,我們只是將進(jìn)程添加到了 cgroup 中進(jìn)行管理而已。相當(dāng)于只是初始化,而真正的限制是貫穿在 Linux 運(yùn)行是的進(jìn)程調(diào)度過程中的。

所添加的進(jìn)程將會(huì)受到 cpu 子系統(tǒng) task_group 下的 cfs_bandwidth 中記錄的 period 和 quota 的限制。

在你的新進(jìn)程是如何被內(nèi)核調(diào)度執(zhí)行到的?一文中我們介紹過完全公平調(diào)度器在選擇進(jìn)程時(shí)的核心方法 pick_next_task_fair。

這個(gè)方法的整個(gè)執(zhí)行過程一個(gè)自頂向下搜索可執(zhí)行的 task_struct 的過程。整個(gè)系統(tǒng)中有一個(gè) root_task_group。

//file:kernel/sched/core.c
structtask_grouproot_task_group;

775b0a20-8e7f-11ee-939d-92fbcf53809c.png

CFS 中調(diào)度隊(duì)列是一顆紅黑樹, 紅黑樹的節(jié)點(diǎn)是 struct sched_entity, sched_entity 中既可以指向 struct task_struct 也可以指向 struct cfs_rq(可理解為 task_group)

調(diào)度 pick_next_task_fair()函數(shù)中的 prev 是本次調(diào)度時(shí)在執(zhí)行的上一個(gè)進(jìn)程。該函數(shù)通過 do {} while 循環(huán),自頂向下搜索到下一步可執(zhí)行進(jìn)程。

//file:kernel/sched/fair.c
staticstructtask_struct*
pick_next_task_fair(structrq*rq,structtask_struct*prev,structrq_flags*rf)
{
structcfs_rq*cfs_rq=&rq->cfs;
...

//選擇下一個(gè)調(diào)度的進(jìn)程
do{
...
se=pick_next_entity(cfs_rq,curr);
cfs_rq=group_cfs_rq(se);
}while(cfs_rq)
p=task_of(se);

//如果選出的進(jìn)程和上一個(gè)進(jìn)程不同
if(prev!=p){
structsched_entity*pse=&prev->se;
...

//對(duì)要放棄CPU的進(jìn)程執(zhí)行一些處理
put_prev_entity(cfs_rq,pse);
}

}

如果新進(jìn)程和上一次運(yùn)行的進(jìn)程不是同一個(gè),則要調(diào)用 put_prev_entity 做兩件和 CPU 的帶寬控制有關(guān)的事情。

//file:kernel/sched/fair.c
staticvoidput_prev_entity(structcfs_rq*cfs_rq,structsched_entity*prev)
{
//4.1運(yùn)行隊(duì)列帶寬的更新與申請(qǐng)
if(prev->on_rq)
update_curr(cfs_rq);

//4.2判斷是否需要將容器掛起
check_cfs_rq_runtime(cfs_rq);

//更新負(fù)載數(shù)據(jù)
update_load_avg(cfs_rq,prev,0);
...
}

在上述代碼中,和 CPU 帶寬控制相關(guān)的操作有兩個(gè)。

運(yùn)行隊(duì)列帶寬的更新與申請(qǐng)

判斷是否需要進(jìn)行帶寬限制

接下來我們分兩個(gè)小節(jié)詳細(xì)展開看看這兩個(gè)操作具體都做了哪些事情。

4.1 運(yùn)行隊(duì)列帶寬的更新與申請(qǐng)

在這個(gè)小節(jié)中我們專門來看看 cfs_rq 隊(duì)列中 runtime_remaining 的更新與申請(qǐng)

在實(shí)現(xiàn)上帶寬控制是在 task_group 下屬的 cfs_rq 隊(duì)列中進(jìn)行的。cfs_rq 對(duì)帶寬時(shí)間的操作歸總起來就是更新與申請(qǐng)。申請(qǐng)到的時(shí)間保存在字段 runtime_remaining 字段中,每當(dāng)有時(shí)間支出需要更新的時(shí)候也是從這個(gè)字段值從去除。

其實(shí)除了上述場(chǎng)景外,系統(tǒng)在很多情況下都會(huì)調(diào)用 update_curr,包括任務(wù)在入隊(duì)、出隊(duì)時(shí),調(diào)度中斷函數(shù)也會(huì)周期性地調(diào)用該方法,以確保任務(wù)的各種時(shí)間信息隨時(shí)都是最新的狀態(tài)。在這里會(huì)更新 cfs_rq 隊(duì)列中的 runtime_remaining 時(shí)間。如果 runtime_remaining 不足,會(huì)觸發(fā)時(shí)間申請(qǐng)。

//file:kernel/sched/fair.c
staticvoidupdate_curr(structcfs_rq*cfs_rq)
{
//計(jì)算一下運(yùn)行了多久
u64now=rq_clock_task(rq_of(cfs_rq));
u64delta_exec;
delta_exec=now-curr->exec_start;
...

//更新帶寬限制
account_cfs_rq_runtime(cfs_rq,delta_exec);
}

在 update_curr 先計(jì)算當(dāng)前執(zhí)行了多少時(shí)間。然后在 cfs_rq 的 runtime_remaining 減去該時(shí)間值,具體減的過程是在 account_cfs_rq_runtime 中處理的。

//file:kernel/sched/fair.c
staticvoid__account_cfs_rq_runtime(structcfs_rq*cfs_rq,u64delta_exec)
{
cfs_rq->runtime_remaining-=delta_exec;

//如果還有剩余時(shí)間,則函數(shù)返回
if(likely(cfs_rq->runtime_remaining>0))
return;
...
//調(diào)用assign_cfs_rq_runtime申請(qǐng)時(shí)間余額
if(!assign_cfs_rq_runtime(cfs_rq)&&likely(cfs_rq->curr))
resched_curr(rq_of(cfs_rq));
}

更新帶寬時(shí)間的邏輯比較簡(jiǎn)單,先從 cfs->runtime_remaining 減去本次執(zhí)行的物理時(shí)間。如果減去之后仍然大于 0 ,那么本次更新就算是結(jié)束了。

如果相減后發(fā)現(xiàn)是負(fù)數(shù),表示當(dāng)前 cfs_rq 的時(shí)間余額已經(jīng)耗盡,則會(huì)立即嘗試從任務(wù)組中申請(qǐng)。具體的申請(qǐng)函數(shù)是 assign_cfs_rq_runtime。如果申請(qǐng)沒能成功,調(diào)用 resched_curr 標(biāo)記 cfs_rq->curr 的 TIF_NEED_RESCHED 位,以便隨后將其調(diào)度出去。

我們展開看下申請(qǐng)過程 assign_cfs_rq_runtime 。

//file:kernel/sched/fair.c
staticintassign_cfs_rq_runtime(structcfs_rq*cfs_rq)
{
//獲取當(dāng)前task_group的cfs_bandwidth
structtask_group*tg=cfs_rq->tg;
structcfs_bandwidth*cfs_b=tg_cfs_bandwidth(tg);

//申請(qǐng)時(shí)間數(shù)量為保持下次有sysctl_sched_cfs_bandwidth_slice這么多
min_amount=sched_cfs_bandwidth_slice()-cfs_rq->runtime_remaining;

//如果沒有限制,則要多少給多少
if(cfs_b->quota==RUNTIME_INF)
amount=min_amount;
else{
//保證定時(shí)器是打開的,保證周期性地為任務(wù)組重置帶寬時(shí)間
start_cfs_bandwidth(cfs_b);

//如果本周期內(nèi)還有時(shí)間,則可以分配
if(cfs_b->runtime>0){
//確保不要透支
amount=min(cfs_b->runtime,min_amount);
cfs_b->runtime-=amount;
cfs_b->idle=0;
}
}

cfs_rq->runtime_remaining+=amount;
returncfs_rq->runtime_remaining>0;
}

首先,獲取當(dāng)前 task_group 的 cfs_bandwidth,因?yàn)檎麄€(gè)任務(wù)組的帶寬數(shù)據(jù)都是封裝在這里的。接著調(diào)用 sched_cfs_bandwidth_slice 來獲取后面要留有多長(zhǎng)時(shí)間,這個(gè)函數(shù)訪問的 sysctl 下的 sched_cfs_bandwidth_slice 參數(shù)

//file:kernel/sched/fair.c
staticinlineu64sched_cfs_bandwidth_slice(void)
{
return(u64)sysctl_sched_cfs_bandwidth_slice*NSEC_PER_USEC;
}

這個(gè)參數(shù)在我的機(jī)器上是 5000 us(也就是說每次申請(qǐng) 5 ms)。

$sysctl-a|grepsched_cfs_bandwidth_slice
kernel.sched_cfs_bandwidth_slice_us=5000

在計(jì)算要申請(qǐng)的時(shí)間的時(shí)候,還需要考慮現(xiàn)在有多少時(shí)間。如果 cfs_rq->runtime_remaining 為正的話,那可以少申請(qǐng)一點(diǎn),如果已經(jīng)變?yōu)樨?fù)數(shù)的話,需要在 sched_cfs_bandwidth_slice 基礎(chǔ)之上再多申請(qǐng)一些。

所以,最終要申請(qǐng)的時(shí)間值 min_amount = sched_cfs_bandwidth_slice() - cfs_rq->runtime_remaining

計(jì)算出 min_amount 后,直接在向自己所屬的 task_group 下的 cfs_bandwidth 把時(shí)間申請(qǐng)出來。整個(gè) task_group 下可用的時(shí)間是保存在 cfs_b->runtime 中的。

這里你可能會(huì)問了,那 task_group 下的 cfs_b->runtime 的時(shí)間又是哪兒給分配的呢?我們將在 5.1 節(jié)來討論這個(gè)過程。

4.2 帶寬限制

check_cfs_rq_runtime 這個(gè)函數(shù)檢測(cè) task group 的帶寬是否已經(jīng)耗盡, 如果是則調(diào)用 throttle_cfs_rq 對(duì)進(jìn)程進(jìn)行限流。

//file:kernel/sched/fair.c
staticboolcheck_cfs_rq_runtime(structcfs_rq*cfs_rq)
{
//判斷是不是時(shí)間余額已用盡
if(likely(!cfs_rq->runtime_enabled||cfs_rq->runtime_remaining>0))
returnfalse;
...

throttle_cfs_rq(cfs_rq);
returntrue;
}

我們?cè)賮砜纯?throttle_cfs_rq 的執(zhí)行過程。

//file:kernel/sched/fair.c
staticvoidthrottle_cfs_rq(structcfs_rq*cfs_rq)
{
//1.查找到所屬的task_group下的se
se=cfs_rq->tg->se[cpu_of(rq_of(cfs_rq))];
...

//2.遍歷每一個(gè)可調(diào)度實(shí)體,并從隸屬的 cfs_rq 上面刪除。
for_each_sched_entity(se){
structcfs_rq*qcfs_rq=cfs_rq_of(se);

if(dequeue)
dequeue_entity(qcfs_rq,se,DEQUEUE_SLEEP);
...
}

//3.設(shè)置一些 throttled 信息。
cfs_rq->throttled=1;
cfs_rq->throttled_clock=rq_clock(rq);

//4.確保unthrottle的高精度定時(shí)器處于被激活的狀態(tài)
start_cfs_bandwidth(cfs_b);
...
}

在 throttle_cfs_rq 中,找到其所屬的 task_group 下的調(diào)度實(shí)體 se 數(shù)組,遍歷每一個(gè)元素,并從其隸屬的 cfs_rq 的紅黑樹上刪除。這樣下次再調(diào)度的時(shí)候,就不會(huì)再調(diào)度到這些進(jìn)程了。

那么 start_cfs_bandwidth 是干啥的呢?這正好是下一節(jié)的引子。

五、進(jìn)程的可運(yùn)行時(shí)間的分配

在第四小節(jié)我們看到,task_group 下的進(jìn)程的運(yùn)行時(shí)間都是從它的 cfs_b->runtime 中申請(qǐng)的。這個(gè)時(shí)間是在定時(shí)器中分配的。負(fù)責(zé)給 task_group 分配運(yùn)行時(shí)間的定時(shí)器包括兩個(gè),一個(gè)是 period_timer,另一個(gè)是 slack_timer。

structcfs_bandwidth{
ktime_tperiod;
u64    quota;
...
structhrtimerperiod_timer;
structhrtimerslack_timer;
...
}

peroid_timer 是周期性給 task_group 添加時(shí)間,缺點(diǎn)是 timer 周期比較長(zhǎng),通常是100ms。而 slack_timer 用于有 cfs_rq 處于 throttle 狀態(tài)且全局時(shí)間池有時(shí)間供分配但是 period_timer 有還有比較長(zhǎng)時(shí)間(通常大于7ms)才超時(shí)的場(chǎng)景。這個(gè)時(shí)候我們就可以激活比較短的slack_timer(5ms超時(shí))進(jìn)行throttle,這樣的設(shè)計(jì)可以提升系統(tǒng)的實(shí)時(shí)性。

這兩個(gè) timer 在 cgroup 下的 cfs_bandwidth 初始化的時(shí)候,都設(shè)置好了到期回調(diào)函數(shù),分別是 sched_cfs_period_timer 和 sched_cfs_slack_timer。

//file:kernel/sched/fair.c
voidinit_cfs_bandwidth(structcfs_bandwidth*cfs_b)
{
cfs_b->runtime=0;
cfs_b->quota=RUNTIME_INF;
cfs_b->period=ns_to_ktime(default_cfs_period());

//初始化period_timer并設(shè)置回調(diào)函數(shù)
hrtimer_init(&cfs_b->period_timer,CLOCK_MONOTONIC,HRTIMER_MODE_ABS_PINNED);
cfs_b->period_timer.function=sched_cfs_period_timer;

//初始化slack_timer并設(shè)置回調(diào)函數(shù)
hrtimer_init(&cfs_b->slack_timer,CLOCK_MONOTONIC,HRTIMER_MODE_REL);
cfs_b->slack_timer.function=sched_cfs_slack_timer;
...
}

在上一節(jié)最后提到的 start_cfs_bandwidth 就是在打開 period_timer 定時(shí)器。

//file:kernel/sched/fair.c
voidstart_cfs_bandwidth(structcfs_bandwidth*cfs_b)
{
...
hrtimer_forward_now(&cfs_b->period_timer,cfs_b->period);
hrtimer_start_expires(&cfs_b->period_timer,HRTIMER_MODE_ABS_PINNED);
}

在 hrtimer_forward_now 調(diào)用時(shí)傳入的第二個(gè)參數(shù)表示是觸發(fā)的延遲時(shí)間。這個(gè)就是在 cgroup 是設(shè)置的 period,一般為 100 ms。

我們來分別看看這兩個(gè) timer 是如何給 task_group 定期發(fā)工資(分配時(shí)間)的。

5.1 period_timer

在 period_timer 的回調(diào)函數(shù) sched_cfs_period_timer 中,周期性地為任務(wù)組分配帶寬時(shí)間,并且解掛當(dāng)前任務(wù)組中所有掛起的隊(duì)列。

分配帶寬時(shí)間是在 __refill_cfs_bandwidth_runtime 中執(zhí)行的,它的調(diào)用堆棧如下。

sched_cfs_period_timer
->do_sched_cfs_period_timer
->__refill_cfs_bandwidth_runtime
//file:kernel/sched/fair.c
void__refill_cfs_bandwidth_runtime(structcfs_bandwidth*cfs_b)
{
if(cfs_b->quota!=RUNTIME_INF)
cfs_b->runtime=cfs_b->quota;
}

可見,這里直接給 cfs_b->runtime 增加了 cfs_b->quota 這么多的時(shí)間。其中 cfs_b->quota 你就可以認(rèn)為是在 cgroupfs 目錄下,我們配置的那個(gè)值。在第一節(jié)中,我們配置的是 500 ms。

#echo500000>cpu.cfs_period_us//500ms

5.2 slack_timer

設(shè)想一下,假如說某個(gè)進(jìn)程申請(qǐng)了 5 ms 的執(zhí)行時(shí)間,但是當(dāng)進(jìn)程剛一啟動(dòng)執(zhí)行便執(zhí)行了同步阻塞的邏輯,這時(shí)候所申請(qǐng)的時(shí)間根本都沒有用完。在這種情況下,申請(qǐng)但沒用完的時(shí)間大部分是要返還給 task_group 中的全局時(shí)間池的。

在內(nèi)核中的調(diào)用鏈如下

dequeue_task_fair
–>dequeue_entity
–>return_cfs_rq_runtime
–>__return_cfs_rq_runtime

具體的返還是在 __return_cfs_rq_runtime 中處理的。

//file:kernel/sched/fair.c
staticvoid__return_cfs_rq_runtime(structcfs_rq*cfs_rq)
{
//給自己留一點(diǎn)
s64slack_runtime=cfs_rq->runtime_remaining-min_cfs_rq_runtime;
if(slack_runtime<=?0)
??return;

?//返還到全局時(shí)間池中
?if?(cfs_b->quota!=RUNTIME_INF){
cfs_b->runtime+=slack_runtime;

//如果時(shí)間又足夠多了,并且還有進(jìn)程被限制的話
//則調(diào)用start_cfs_slack_bandwidth來開啟slack_timer
if(cfs_b->runtime>sched_cfs_bandwidth_slice()&&
!list_empty(&cfs_b->throttled_cfs_rq))
start_cfs_slack_bandwidth(cfs_b);
}
...
}

這個(gè)函數(shù)做了這么幾件事情。

min_cfs_rq_runtime 的值是 1 ms,我們選擇至少保留 1ms 時(shí)間給自己

剩下的時(shí)間 slack_runtime 歸還給當(dāng)前的 cfs_b->runtime

如果時(shí)間又足夠多了,并且還有進(jìn)程被限制的話,開啟slack_timer,嘗試接觸進(jìn)程 CPU 限制

在 start_cfs_slack_bandwidth 中啟動(dòng)了 slack_timer。

//file:kernel/sched/fair.c
staticvoidstart_cfs_slack_bandwidth(structcfs_bandwidth*cfs_b)
{
...

//啟動(dòng)slack_timer
cfs_b->slack_started=true;
hrtimer_start(&cfs_b->slack_timer,
ns_to_ktime(cfs_bandwidth_slack_period),
HRTIMER_MODE_REL);
...
}

可見 slack_timer 的延遲回調(diào)時(shí)間是 cfs_bandwidth_slack_period,它的值是 5 ms。這就比 period_timer 要實(shí)時(shí)多了。

slack_timer 的回調(diào)函數(shù) sched_cfs_slack_timer 我們就不展開看了,它主要就是操作對(duì)進(jìn)程解除 CPU 限制

六、總結(jié)

今天我們介紹了 Linux cgroup 的 cpu 子系統(tǒng)給容器中的進(jìn)程分配 cpu 時(shí)間的原理。

和真正使用物理機(jī)不同,Linux 容器中所謂的核并不是真正的 CPU 核,而是轉(zhuǎn)化成了執(zhí)行時(shí)間的概念。在容器進(jìn)程調(diào)度的時(shí)候給其滿足一定的 CPU 執(zhí)行時(shí)間,而不是真正的分配邏輯核。

cgroup 提供了的原生接口是通過 cgroupfs 提供控制各個(gè)子系統(tǒng)的設(shè)置的。默認(rèn)是在 /sys/fs/cgroup/ 目錄下,內(nèi)核這個(gè)文件系統(tǒng)的處理是定義了特殊的處理,和普通的文件完全不一樣的。

內(nèi)核處理 cpu 帶寬控制的核心對(duì)象就是下面這個(gè) cfs_bandwidth。

//file:kernel/sched/sched.h
structcfs_bandwidth{
//帶寬控制配置
ktime_tperiod;
u64quota;

//當(dāng)前task_group的全局可執(zhí)行時(shí)間
u64runtime;
...

//定時(shí)分配
structhrtimerperiod_timer;
structhrtimerslack_timer;
}

用戶在創(chuàng)建 cgroup cpu 子系統(tǒng)控制過程主要分成三步:

第一步:通過創(chuàng)建目錄來創(chuàng)建 cgroup 對(duì)象。在 /sys/fs/cgroup/cpu,cpuacct 創(chuàng)建一個(gè)目錄 test,實(shí)際上內(nèi)核是創(chuàng)建了 cgroup、task_group 等內(nèi)核對(duì)象。

第二步:在目錄中設(shè)置 cpu 的限制情況。在 task_group 下有個(gè)核心的 cfs_bandwidth 對(duì)象,用戶所設(shè)置的 cfs_quota_us 和 cfs_period_us 的值最后都存到它下面了。

第三步:將進(jìn)程添加到 cgroup 中進(jìn)行資源管控。當(dāng)在 cgroup 的 cgroup.proc 下添加進(jìn)程 pid 時(shí),實(shí)際上是將該進(jìn)程加入到了這個(gè)新的 task_group 調(diào)度組了。將使用 task_group 的 runqueue,以及它的時(shí)間配額

當(dāng)創(chuàng)建完成后,內(nèi)核的 period_timer 會(huì)根據(jù) task_group->cfs_bandwidth 下用戶設(shè)置的 period 定時(shí)給可執(zhí)行時(shí)間 runtime 上加上 quota 這么多的時(shí)間(相當(dāng)于按月發(fā)工資),以供 task_group 下的進(jìn)程執(zhí)行(消費(fèi))的時(shí)候使用。

structcfs_rq{
...
intruntime_enabled;
s64runtime_remaining;
}

在完全公平器調(diào)度的時(shí)候,每次 pick_next_task_fair 時(shí)會(huì)做兩件事情

第一件:將從 cpu 上拿下來的進(jìn)程所在的運(yùn)行隊(duì)列進(jìn)行執(zhí)行時(shí)間的更新與申請(qǐng)。會(huì)將 cfs_rq 的 runtime_remaining 減去已經(jīng)執(zhí)行了的時(shí)間。如果減為了負(fù)數(shù),則從 cfs_rq 所在的 task_group 下的 cfs_bandwidth 去申請(qǐng)一些。

第二件:判斷 cfs_rq 上是否申請(qǐng)到了可執(zhí)行時(shí)間,如果沒有申請(qǐng)到,需要將這個(gè)隊(duì)列上的所有進(jìn)程都從完全公平調(diào)度器的紅黑樹上取下。這樣再次調(diào)度的時(shí)候,這些進(jìn)程就不會(huì)被調(diào)度了。

當(dāng) period_timer 再次給 task_group 分配時(shí)間的時(shí)候,或者是自己有申請(qǐng)時(shí)間沒用完回收后觸發(fā) slack_timer 的時(shí)候,被限制調(diào)度的進(jìn)程會(huì)被解除調(diào)度限制,重新正常參與運(yùn)行。

這里要注意的是,一般 period_timer 分配時(shí)間的周期都是 100 ms 左右。假如說你的進(jìn)程前 50 ms 就把 cpu 給用光了,那你收到的請(qǐng)求可能在后面的 50 ms 都沒有辦法處理,對(duì)請(qǐng)求處理耗時(shí)會(huì)有影響。這也是為啥在關(guān)注 CPU 性能的時(shí)候要關(guān)注對(duì)容器 throttle 次數(shù)和時(shí)間的原因了。






審核編輯:劉清

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • Linux
    +關(guān)注

    關(guān)注

    87

    文章

    11123

    瀏覽量

    207916
  • 調(diào)度器
    +關(guān)注

    關(guān)注

    0

    文章

    98

    瀏覽量

    5210

原文標(biāo)題:內(nèi)核是如何給容器中的進(jìn)程分配CPU資源的?

文章出處:【微信號(hào):良許Linux,微信公眾號(hào):良許Linux】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    CPU底層工作原理

    前段時(shí)間,我連續(xù)寫了十來篇CPU底層系列技術(shù)故事文章,有不少讀者私信我讓我寫一CPU的寄存器。
    發(fā)表于 07-25 10:20 ?1257次閱讀

    Linux開發(fā)_Linux進(jìn)程編程

    介紹Linux進(jìn)程概念、進(jìn)程信號(hào)捕獲、進(jìn)程管理相關(guān)的命令的使用等知識(shí)點(diǎn)。
    的頭像 發(fā)表于 09-17 15:38 ?1254次閱讀
    <b class='flag-5'>Linux</b>開發(fā)_<b class='flag-5'>Linux</b><b class='flag-5'>下</b><b class='flag-5'>進(jìn)程</b>編程

    Linux進(jìn)程怎么綁定CPU

    昨天在群里有朋友問:把進(jìn)程綁定到某個(gè) CPU 上運(yùn)行是怎么實(shí)現(xiàn)的。
    發(fā)表于 10-26 10:26 ?1789次閱讀

    Linux系統(tǒng)進(jìn)程的幾種狀態(tài)介紹

    文章對(duì) Linux 系統(tǒng)進(jìn)程的幾種狀態(tài)進(jìn)行介紹,并對(duì)系統(tǒng)出現(xiàn)大量僵尸進(jìn)程和不可中斷進(jìn)程的場(chǎng)景
    發(fā)表于 11-24 16:15 ?1.2w次閱讀
    <b class='flag-5'>Linux</b>系統(tǒng)<b class='flag-5'>下</b><b class='flag-5'>進(jìn)程</b>的幾種狀態(tài)介紹

    Linux進(jìn)程的睡眠和喚醒

    Linux中,僅等待CPU時(shí)間的進(jìn)程稱為就緒進(jìn)程,它們被放置在一個(gè)運(yùn)行隊(duì)列中,一個(gè)就緒進(jìn)程的狀 態(tài)標(biāo)志位為 TASK_RUNNING。一旦
    發(fā)表于 06-07 12:26 ?391次閱讀

    Linux進(jìn)程結(jié)構(gòu)

    `#嵌入式培訓(xùn)#華清遠(yuǎn)見嵌入式linux學(xué)習(xí)資料《Linux進(jìn)程結(jié)構(gòu)》,進(jìn)程不但包括程序的指令和數(shù)據(jù),而且包括程序計(jì)數(shù)器和處理器的所有寄
    發(fā)表于 08-05 11:05

    Linux進(jìn)程間通信方式-管道

    Linux進(jìn)程間通信方式-管道分享到: 本文關(guān)鍵字: linux 管道通信,linux 進(jìn)程
    發(fā)表于 08-29 15:29

    Linux進(jìn)程間通信

    華清遠(yuǎn)見嵌入式linux學(xué)習(xí)資料《Linux進(jìn)程間通信》,通過前面的學(xué)習(xí),讀者已經(jīng)知道了進(jìn)程是一個(gè)程序的一次執(zhí)行,是系統(tǒng)資源分配的最小單元
    發(fā)表于 09-04 10:07

    Linux進(jìn)程結(jié)構(gòu)

    (TASK_KILLABLE):Linux內(nèi)核 2.6.25 引入了一種新的進(jìn)程狀態(tài),名為 TASK_KILLABLE。該狀態(tài)的運(yùn)行機(jī)制類似于 TASK_UNINTERRUPTIBLE,只不過處在該狀態(tài)
    發(fā)表于 05-27 09:24

    linux操作系統(tǒng)進(jìn)程通信設(shè)計(jì)

    linux進(jìn)程通信手段基本上是從Unix平臺(tái)上的進(jìn)程通信手段繼承而來的。而對(duì)Unix發(fā)展做出重大貢獻(xiàn)的兩大主力AT&T的貝
    發(fā)表于 11-24 10:53 ?613次閱讀

    linux操作系統(tǒng)進(jìn)程通信

    的側(cè)重點(diǎn)有所不同。前者對(duì)Unix早期的進(jìn)程間通信手段進(jìn)行了系統(tǒng)的改進(jìn)和擴(kuò)充,形成了system V IPC,通信進(jìn)程局限在單個(gè)計(jì)算機(jī)內(nèi);后者則跳過了該限制,形成了基于套接口(socke
    發(fā)表于 10-31 11:15 ?0次下載

    Linux CPU的性能應(yīng)該如何優(yōu)化

    Linux系統(tǒng)中,由于成本的限制,往往會(huì)存在資源上的不足,例如 CPU、內(nèi)存、網(wǎng)絡(luò)、IO 性能。本文,就對(duì) Linux 進(jìn)程
    的頭像 發(fā)表于 01-18 08:52 ?3261次閱讀

    基于linux eBPF的進(jìn)程off-cpu的方法

    的swap等。如下圖所示,紅色部分屬于on-cpu部分,藍(lán)色部分屬于off-cpu。 一般我們用的perf命令等都是采樣on-cpu的指令進(jìn)行CPU
    的頭像 發(fā)表于 09-25 15:41 ?3010次閱讀
    基于<b class='flag-5'>linux</b> eBPF的<b class='flag-5'>進(jìn)程</b>off-<b class='flag-5'>cpu</b>的方法

    Linux技術(shù)中Cgroup的原理和實(shí)踐

    一、什么是Cgroup,使用場(chǎng)景? 容器本質(zhì)上是進(jìn)程,既然是進(jìn)程就會(huì)消耗掉系統(tǒng)資源,比如:CPU、內(nèi)存、磁盤、網(wǎng)絡(luò)帶寬等,如果不加以限制,
    的頭像 發(fā)表于 10-15 14:04 ?4830次閱讀
    <b class='flag-5'>Linux</b>技術(shù)中Cgroup的原理和實(shí)踐

    如何將進(jìn)程CPU 進(jìn)行綁定

    Linux 系統(tǒng)提供了一個(gè)名為 sched_setaffinity 的系統(tǒng)調(diào)用,此系統(tǒng)調(diào)用可以設(shè)置進(jìn)程CPU 親和性。我們來看看 sched_setaffinity 系統(tǒng)調(diào)用的原型。
    發(fā)表于 10-26 10:29 ?467次閱讀