搜尋
首頁系統教程LinuxLinux 進程組調度機制:如何對進程進行分組和調度

Linux 進程組調度機制:如何對進程進行分組和調度

Feb 11, 2024 pm 08:30 PM
linuxlinux教程linux系統linux指令shell腳本嵌入式linuxlinux入門linux學習

進程組是 Linux 系統中一種對進程進行分類和管理的方式,它可以將具有相同特徵或關係的進程放在一起,形成一個邏輯上的單元。進程組的作用是方便對進程進行控制、通訊和資源分配,以提高系統的效率和安全性。進程組調度是 Linux 系統中一種對進程組進行調度的機制,它可以根據進程組的屬性和需求,並分配適當的 CPU 時間和資源,從而提高系統的並發性和回應性。但是,你真的了解 Linux 進程組調度機制嗎?你知道如何在 Linux 下建立和管理進程組嗎?你知道如何在 Linux 下使用和配置進程組調度機制嗎?本文將為你詳細介紹 Linux 進程組調度機制的相關知識,讓你在 Linux 下更能運用並理解這個強大的核心功能。

Linux 进程组调度机制:如何对进程进行分组和调度

#又碰到一個神奇的進程調度問題,在系統重啟過程中,發現系統掛住了,過了30s後才重新復位,真正係統復位的原因是硬體看門狗重啟的系統,而非原來正常的reboot流程。硬體狗記錄的重設時間,將不餵狗的時間向前推30s分析串口記錄日誌,當時的日誌就印了一句話:「sched: RT throttling activated」。
從linux-3.0.101-0.7.17版本核心程式碼可以看出,sched_rt_runtime_exceeded印了這句話。在核心進程組調度過程中,即時進程調度受rt_rq->rt_throttled 的限制,下面便具體說一下涉及到的linux中進程組調度機制。

進程組調度機制

#群組調度是cgroup裡面的概念,指將N個進程視為一個整體,參與系統中的調度過程,具體體現在範例中:A任務有8個進程或線程,B任務有2個進程或線程,仍然有其他的進程或線程存在,就需要控制A任務的CPU佔用率不高於40%,B任務的CPU佔用率不高於40%,其他任務佔用率不少於20%,那麼就有對cgroup閥值的設置,cgroup A設定為200,cgroup B設定為200,其他任務預設為100,如此便實現了CPU控制的功能。
在核心中,進程組由task_group進行管理,其中涉及的內容很多都是cgroup控制機制 ,另外開闢單元在寫,此處指重點描述組調度的部分,具體見如下註釋。

struct task_group {
 struct cgroup_subsys_state css;

//下面是普通进程调度使用
#ifdef CONFIG_FAIR_GROUP_SCHED
 /* schedulable entities of this group on each cpu */
//普通进程调度单元,之所以用调度单元,因为被调度的可能是一个进程,也可能是一组进程
 struct sched_entity **se;
 /* runqueue "owned" by this group on each cpu */
//公平调度队列
 struct cfs_rq **cfs_rq;
//下面就是如上示例的控制阀值
 unsigned long shares;
 atomic_t load_weight;
#endif

#ifdef CONFIG_RT_GROUP_SCHED
//实时进程调度单元
 struct sched_rt_entity **rt_se;
//实时进程调度队列
 struct rt_rq **rt_rq;
//实时进程占用CPU时间的带宽(或者说比例)
 struct rt_bandwidth rt_bandwidth;
#endif

 struct rcu_head rcu;
 struct list_head list;
//task_group呈树状结构组织,有父节点,兄弟链表,孩子链表,内核里面的根节点是root_task_group
 struct task_group *parent;
 struct list_head siblings;
 struct list_head children;

#ifdef CONFIG_SCHED_AUTOGROUP
 struct autogroup *autogroup;
#endif

 struct cfs_bandwidth cfs_bandwidth;
};

調度單元有兩種,即普通調度單元和即時進程調度單元。

struct sched_entity {
 struct load_weight load;  /* for load-balancing */
 struct rb_node  run_node;
 struct list_head group_node;
 unsigned int  on_rq;

 u64   exec_start;
 u64   sum_exec_runtime;
 u64   vruntime;
 u64   prev_sum_exec_runtime;

 u64   nr_migrations;

#ifdef CONFIG_SCHEDSTATS
 struct sched_statistics statistics;
#endif

#ifdef CONFIG_FAIR_GROUP_SCHED
//当前调度单元归属于某个父调度单元
 struct sched_entity *parent;
 /* rq on which this entity is (to be) queued: */
//当前调度单元归属的父调度单元的调度队列,即当前调度单元插入的队列
 struct cfs_rq  *cfs_rq;
 /* rq "owned" by this entity/group: */
//当前调度单元的调度队列,即管理子调度单元的队列,如果调度单元是task_group,my_q才会有值
//如果当前调度单元是task,那么my_q自然为NULL
 struct cfs_rq  *my_q;
#endif
 void *suse_kabi_padding;
};

struct sched_rt_entity {
 struct list_head run_list;
 unsigned long timeout;
 unsigned int time_slice;
 int nr_cpus_allowed;

 struct sched_rt_entity *back;
#ifdef CONFIG_RT_GROUP_SCHED
//实时进程的管理和普通进程类似,下面三项意义参考普通进程
 struct sched_rt_entity *parent;
 /* rq on which this entity is (to be) queued: */
 struct rt_rq  *rt_rq;
 /* rq "owned" by this entity/group: */
 struct rt_rq  *my_q;
#endif
};

下面看一下調度隊列,因為即時調度和普通調度隊列需要說明的選項差不多,以即時隊列為例:

struct rt_rq {
 struct rt_prio_array active;
 unsigned long rt_nr_running;
#if defined CONFIG_SMP || defined CONFIG_RT_GROUP_SCHED
 struct {
  int curr; /* highest queued rt task prio */
#ifdef CONFIG_SMP
  int next; /* next highest */
#endif
 } highest_prio;
#endif
#ifdef CONFIG_SMP
 unsigned long rt_nr_migratory;
 unsigned long rt_nr_total;
 int overloaded;
 struct plist_head pushable_tasks;
#endif
//当前队列的实时调度是否受限
 int rt_throttled;
//当前队列的累计运行时间
 u64 rt_time;
//当前队列的最大运行时间
 u64 rt_runtime;
 /* Nests inside the rq lock: */
 raw_spinlock_t rt_runtime_lock;

#ifdef CONFIG_RT_GROUP_SCHED
 unsigned long rt_nr_boosted;
//当前实时调度队列归属调度队列
 struct rq *rq;
 struct list_head leaf_rt_rq_list;
//当前实时调度队列归属的调度单元
 struct task_group *tg;
#endif
};

透過以上3個結構體分析,可以得到下圖(點擊看大圖):

Linux 进程组调度机制:如何对进程进行分组和调度

task_group

#從圖上可以看出,調度單元和調度佇列組合一個樹節點,又是另一個單獨樹結構存在,只是需要注意的是,只有調度單元裡面有TASK_RUNNING的進程時,調度單元才會被放到調度隊列中。
另外一點是,在沒有組調度前,每個CPU上只有一個調度隊列,當時可以理解成所有的進程在一個調度組裡面,現在則是每個調度組在每個CPU上都有調度隊列。在調度過程中,原來是系統選擇一個進程運行,目前則是選擇一個調度單元運行,調度發生時,schedule進程從root_task_group開始尋找由調度策略決定的調度單元,當調度單元是task_group,則進入task_group的運行隊列選擇一個合適的調度單元,最後找一個合適的task調度單元。整個過程就是樹的遍歷,擁有TASK_RUNNING進程的task_group是樹的節點,task調度單元則是樹的葉子。

群組進程調度策略

#群組進程調度要實現的目的和原來沒有差別,就是完成即時進程調度和普通進程調度,即rt和cfs調度。

CFS组调度策略:

文章前面示例中提到的任务分配CPU,说的就是cfs调度,对于CFS调度而言,调度单元和普通调度进程没有多大区别,调度单元由自己的调度优先级,而且不受调度进程的影响,每个task_group都有一个shares,share并非我们说的进程优先级,而是调度权重,这个是cfs调度管理的概念,但在cfs中最终体现到调度优先排序上。shares值默认都是相同的,所有没有设置权重的值,CPU都是按旧有的cfs管理分配的。总结的说,就是cfs组调度策略没变化。具体到cgroup的CPU控制机制上再说。

RT组调度策略:

实时进程的优先级是设置固定,调度器总是选择优先级最高的进程运行。而在组调度中,调度单元的优先级则是组内优先级最高的调度单元的优先级值,也就是说调度单元的优先级受子调度单元影响,如果一个进程进入了调度单元,那么它所有的父调度单元的调度队列都要重排。实际上我们看到的结果是,调度器总是选择优先级最高的实时进程调度,那么组调度对实时进程控制机制是怎么样的?
在前面的rt_rq实时进程运行队列里面提到rt_time和rt_runtime,一个是运行累计时间,一个是最大运行时间,当运行累计时间超过最大运行时间的时候,rt_throttled则被设置为1,见sched_rt_runtime_exceeded函数。

if (rt_rq->rt_time > runtime) {
 rt_rq->rt_throttled = 1;
 if (rt_rq_throttled(rt_rq)) {
  sched_rt_rq_dequeue(rt_rq);
  return 1;
 }
}

设置为1意味着实时队列中被限制了,如__enqueue_rt_entity函数,不能入队。

static inline int rt_rq_throttled(struct rt_rq *rt_rq)
{
 return rt_rq->rt_throttled && !rt_rq->rt_nr_boosted;
}
static void __enqueue_rt_entity(struct sched_rt_entity *rt_se, bool head)
{
 /*
  * Don't enqueue the group if its throttled, or when empty.
  * The latter is a consequence of the former when a child group
  * get throttled and the current group doesn't have any other
  * active members.
  */
 if (group_rq && (rt_rq_throttled(group_rq) || !group_rq->rt_nr_running))
  return;
.....
}

其实还有一个隐藏的时间概念,即sched_rt_period_us,意味着sched_rt_period_us时间内,实时进程可以占用CPU rt_runtime时间,如果实时进程每个时间周期内都没有调度,则在do_sched_rt_period_timer定时器函数中将rt_time减去一个周期,然后比较rt_runtime,恢复rt_throttled。

//overrun来自对周期时间定时器误差的校正
rt_rq->rt_time -= min(rt_rq->rt_time, overrun*runtime);
if (rt_rq->rt_throttled && rt_rq->rt_time rt_throttled = 0;
  enqueue = 1;

则对于cgroup控制实时进程的占用比则是通过rt_runtime实现的,对于root_task_group,也即是所有进程在一个cgroup下,则是通过/proc/sys/kernel/sched_rt_period_us和/proc/sys/kernel/sched_rt_runtime_us接口设置的,默认值是1s和0.95s。这么看以为实时进程只能占用95%CPU,那么实时进程占用CPU100%导致进程挂死的问题怎么出现了?
原来实时进程所在的CPU占用超时了,实时进程的rt_runtime可以向别的cpu借用,将其他CPU剩余的rt_runtime-rt_time的值借过来,如此rt_time可以最大等于rt_runtime,造成事实上的单核CPU达到100%。这样做的目的自然规避了实时进程缺少CPU时间而向其他核迁移的成本,未绑核的普通进程自然也可以迁移其他CPU上,不会得不到调度,当然绑核进程仍然是个杯具。

static int do_balance_runtime(struct rt_rq *rt_rq)
{
 struct rt_bandwidth *rt_b = sched_rt_bandwidth(rt_rq);
 struct root_domain *rd = cpu_rq(smp_processor_id())->rd;
 int i, weight, more = 0;
 u64 rt_period;

 weight = cpumask_weight(rd->span);

 raw_spin_lock(&rt_b->rt_runtime_lock);
 rt_period = ktime_to_ns(rt_b->rt_period);
 for_each_cpu(i, rd->span) {
  struct rt_rq *iter = sched_rt_period_rt_rq(rt_b, i);
  s64 diff;

  if (iter == rt_rq)
   continue;

  raw_spin_lock(&iter->rt_runtime_lock);
  /*
   * Either all rqs have inf runtime and there's nothing to steal
   * or __disable_runtime() below sets a specific rq to inf to
   * indicate its been disabled and disalow stealing.
   */
  if (iter->rt_runtime == RUNTIME_INF)
   goto next;

  /*
   * From runqueues with spare time, take 1/n part of their
   * spare time, but no more than our period.
   */
  diff = iter->rt_runtime - iter->rt_time;
  if (diff > 0) {
   diff = div_u64((u64)diff, weight);
   if (rt_rq->rt_runtime + diff > rt_period)
    diff = rt_period - rt_rq->rt_runtime;
   iter->rt_runtime -= diff;
   rt_rq->rt_runtime += diff;
   more = 1;
   if (rt_rq->rt_runtime == rt_period) {
    raw_spin_unlock(&iter->rt_runtime_lock);
    break;
   }
  }
next:
  raw_spin_unlock(&iter->rt_runtime_lock);
 }
 raw_spin_unlock(&rt_b->rt_runtime_lock);

 return more;
}

通过本文,你应该对 Linux 进程组调度机制有了一个深入的了解,知道了它的定义、原理、流程和优化方法。你也应该明白了进程组调度机制的作用和影响,以及如何在 Linux 下正确地使用和配置进程组调度机制。我们建议你在使用 Linux 系统时,使用进程组调度机制来提高系统的效率和安全性。同时,我们也提醒你在使用进程组调度机制时要注意一些潜在的问题和挑战,如进程组类型、优先级、限制等。希望本文能够帮助你更好地使用 Linux 系统,让你在 Linux 下享受进程组调度机制的优势和便利。

以上是Linux 進程組調度機制:如何對進程進行分組和調度的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述
本文轉載於:良许Linux教程网。如有侵權,請聯絡admin@php.cn刪除
Linux管理員的薪水是多少?Linux管理員的薪水是多少?Apr 17, 2025 am 12:24 AM

Linux管理員的平均年薪在美國為75,000至95,000美元,歐洲為40,000至60,000歐元。提升薪資可以通過:1.持續學習新技術,如雲計算和容器技術;2.積累項目經驗並建立Portfolio;3.建立職業網絡,拓展人脈。

Linux的主要目的是什麼?Linux的主要目的是什麼?Apr 16, 2025 am 12:19 AM

Linux的主要用途包括:1.服務器操作系統,2.嵌入式系統,3.桌面操作系統,4.開發和測試環境。 Linux在這些領域表現出色,提供了穩定性、安全性和高效的開發工具。

互聯網在Linux上運行嗎?互聯網在Linux上運行嗎?Apr 14, 2025 am 12:03 AM

互聯網運行不依賴單一操作系統,但Linux在其中扮演重要角色。 Linux廣泛應用於服務器和網絡設備,因其穩定性、安全性和可擴展性受歡迎。

Linux操作是什麼?Linux操作是什麼?Apr 13, 2025 am 12:20 AM

Linux操作系統的核心是其命令行界面,通過命令行可以執行各種操作。 1.文件和目錄操作使用ls、cd、mkdir、rm等命令管理文件和目錄。 2.用戶和權限管理通過useradd、passwd、chmod等命令確保系統安全和資源分配。 3.進程管理使用ps、kill等命令監控和控制系統進程。 4.網絡操作包括ping、ifconfig、ssh等命令配置和管理網絡連接。 5.系統監控和維護通過top、df、du等命令了解系統運行狀態和資源使用情況。

使用Linux別名提高自定義命令快捷方式的生產率使用Linux別名提高自定義命令快捷方式的生產率Apr 12, 2025 am 11:43 AM

介紹 Linux是一個強大的操作系統,由於其靈活性和效率,開發人員,系統管理員和電源用戶都喜歡。但是,經常使用長而復雜的命令可能是乏味的

Linux實際上有什麼好處?Linux實際上有什麼好處?Apr 12, 2025 am 12:20 AM

Linux適用於服務器、開發環境和嵌入式系統。 1.作為服務器操作系統,Linux穩定高效,常用於部署高並發應用。 2.作為開發環境,Linux提供高效的命令行工具和包管理系統,提升開發效率。 3.在嵌入式系統中,Linux輕量且可定制,適合資源有限的環境。

在Linux上掌握道德黑客的基本工具和框架在Linux上掌握道德黑客的基本工具和框架Apr 11, 2025 am 09:11 AM

簡介:通過基於Linux的道德黑客攻擊數字邊界 在我們越來越相互聯繫的世界中,網絡安全至關重要。 道德黑客入侵和滲透測試對於主動識別和減輕脆弱性至關重要

如何學習Linux基礎知識?如何學習Linux基礎知識?Apr 10, 2025 am 09:32 AM

Linux基礎學習從零開始的方法包括:1.了解文件系統和命令行界面,2.掌握基本命令如ls、cd、mkdir,3.學習文件操作,如創建和編輯文件,4.探索高級用法如管道和grep命令,5.掌握調試技巧和性能優化,6.通過實踐和探索不斷提陞技能。

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱門文章

R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
1 個月前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳圖形設置
1 個月前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您聽不到任何人,如何修復音頻
1 個月前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.聊天命令以及如何使用它們
1 個月前By尊渡假赌尊渡假赌尊渡假赌

熱工具

WebStorm Mac版

WebStorm Mac版

好用的JavaScript開發工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

EditPlus 中文破解版

EditPlus 中文破解版

體積小,語法高亮,不支援程式碼提示功能

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

VSCode Windows 64位元 下載

VSCode Windows 64位元 下載

微軟推出的免費、功能強大的一款IDE編輯器