從 scheduler_tick 到上下文切換:深入解析 Linux 內核的 TIF_NEED_RESCHED 標志設置流程

Linux 是如何決定何時進行上下文切換的?

在Linux中,CPU 上下文切換是指當操作系統將 CPU 從一個進程切換到另一個進程時,保存當前進程的執行狀態,并加載新進程的執行狀態的過程就稱為上下文切換。

但在 Linux 內核中,是否切換進程通常由一個關鍵標志位 TIF_NEED_RESCHED 來決定。

當該標志被設置時,內核會在合適的時機(例如從中斷返回或系統調用結束時)調用schedule(),從而觸發上下文切換。

TIF_NEED_RESCHED標志的設置過程

剛才我們提到切換進程或任務是由TIF_NEED_RESCHED標志位來判斷,那這個標志位是如何設置的呢

接下來,我們將從 scheduler_tick() 函數開始,逐步揭示這個標志位是如何被設置,并最終觸發上下文切換的。

1. scheduler_tick():調度的“心跳”

讓我們先從**scheduler_tick()**函數開始 說起

scheduler_tick() 是由定時器中斷(以 HZ 頻率)觸發的調度驅動函數。它會獲取當前 CPU 上正在運行的進程,并調用該進程所屬調度器的 task_tick() 方法,然后根據具體對應的調度策略判斷是否需要重新調度。

// kernel/sched/core.c
void scheduler_tick(void)
{int cpu = smp_processor_id();struct rq *rq = cpu_rq(cpu);struct task_struct *curr = rq->curr;struct rq_flags rf;// ...rq_lock(rq, &rf); // 加鎖以保護運行隊列// ... 省略 ...// 這是核心調用:根據任務類型調用其對應的 task_tick 方法curr->sched_class->task_tick(rq, curr, 0);// ... 其他邏輯 ...rq_unlock(rq, &rf); // 解鎖// ...
}

在上述代碼中,最關鍵的一行是 curr->sched_class->task_tick(rq, curr, 0);

這行代碼是調度器邏輯的入口,它根據當前運行任務 (curr) 所屬的調度類 (sched_class),動態地調用其特有的 task_tick 方法。對于 CFS(完全公平調度器)任務,就會調用 task_tick_fair(),從而進入具體的調度決策流程。

2. task_tick_fair():CFS 的任務周期調度

剛才提到過每種調度策略都會通過一個sched_class結構體定義其行為,完全公平調度器(CFS)的調度類定義在**kernel/sched/fair.c** 文件中,它通過 DEFINE_SCHED_CLASS(fair) 結構體被綁定到 task_tick 接口上。

/* kernel/sched/fair.c */
DEFINE_SCHED_CLASS(fair) = {.enqueue_task       = enqueue_task_fair,.dequeue_task       = dequeue_task_fair,.yield_task         = yield_task_fair,....task_tick          = task_tick_fair,  // 👈 心跳函數綁定在這里....update_curr        = update_curr_fair,
};

那我們接著就看一下task_tick_fair函數

/** scheduler tick hitting a task of our scheduling class.** NOTE: This function can be called remotely by the tick offload that* goes along full dynticks. Therefore no local assumption can be made* and everything must be accessed through the @rq and @curr passed in* parameters.*/
static void task_tick_fair(struct rq *rq, struct task_struct *curr, int queued)
{struct cfs_rq *cfs_rq;struct sched_entity *se = &curr->se;for_each_sched_entity(se) {cfs_rq = cfs_rq_of(se);entity_tick(cfs_rq, se, queued);}if (static_branch_unlikely(&sched_numa_balancing))task_tick_numa(rq, curr);update_misfit_status(curr, rq);update_overutilized_status(task_rq(curr));task_tick_core(rq, curr);
}

首先CFS 把調度的最小單位抽象成 sched_entity,它即可以是線程也可以是進程組。每個sched_entity都會對應一個 運行隊列

struct cfs_rq *cfs_rq;
struct sched_entity *se = &curr->se;

接著**for_each_sched_entity循環會一直向上遍歷到最頂層的調度實體**(例如:線程 → 進程組 → 父組),并在每一層都調用 entity_tick() 函數。

for_each_sched_entity(se) {cfs_rq = cfs_rq_of(se);entity_tick(cfs_rq, se, queued);
}

3. entity_tick()update_deadline():判斷是否“超時”

entity_tick() 函數中會調用 update_curr(),該函數會負責更新當前任務的運行時間統計信息,并在此過程中判斷任務是否已超出其分配的時間片。

update_curr() 隨后會調用 update_deadline(),這里便是我們尋找的觸發點。該函數會更新當前任務的運行時間,并判斷是否需要觸發調度。我們先看一下update_deadline的具體代碼

/** XXX: strictly: vd_i += N*r_i/w_i such that: vd_i > ve_i* this is probably good enough.*/
static void update_deadline(struct cfs_rq *cfs_rq, struct sched_entity *se)
{if ((s64)(se->vruntime - se->deadline) < 0)return;/** For EEVDF the virtual time slope is determined by w_i (iow.* nice) while the request time r_i is determined by* sysctl_sched_base_slice.*/se->slice = sysctl_sched_base_slice;/** EEVDF: vd_i = ve_i + r_i / w_i*/se->deadline = se->vruntime + calc_delta_fair(se->slice, se);/** The task has consumed its request, reschedule.*/if (cfs_rq->nr_running > 1) {resched_curr(rq_of(cfs_rq));clear_buddies(cfs_rq, se);}
}

當Linux中任務的虛擬運行時間超過其截止時間,并且運行隊列 (cfs_rq) 中有其他可運行的任務時,就會認為當前任務的時間片已用完。此時就會調用 resched_curr() 從而設置TIF_NEED_RESCHED 標志位了

if (cfs_rq->nr_running > 1) {resched_curr(rq_of(cfs_rq));clear_buddies(cfs_rq, se); 
}

4. resched_curr():設置 TIF_NEED_RESCHED 標志

最后再看一下**resched_curr**的代碼,代碼在kernel/sched/core.c中

/** resched_curr - mark rq's current task 'to be rescheduled now'.** On UP this means the setting of the need_resched flag, on SMP it* might also involve a cross-CPU call to trigger the scheduler on* the target CPU.*/
void resched_curr(struct rq *rq)
{struct task_struct *curr = rq->curr;int cpu;lockdep_assert_rq_held(rq);if (test_tsk_need_resched(curr))return;cpu = cpu_of(rq);if (cpu == smp_processor_id()) {set_tsk_need_resched(curr); // 標記當前任務需要被調度set_preempt_need_resched(); // 觸發搶占檢查return;}if (set_nr_and_not_polling(curr))smp_send_reschedule(cpu);elsetrace_sched_wake_idle_without_ipi(cpu);
}

檢查是否已標記 resched_curr函數首先會檢查當前任務 (curr) 的 need_resched 標志是否已經被設置。如果已經被設置,說明任務已經被標記為需要調度就直接返回避免重復操作。

if (test_tsk_need_resched(curr))return;

處理當前對應CPU核心上的調度

這段代碼檢查 resched_curr 是否在當前 CPU 上被調用。如果是,它會執行兩個關鍵步驟:

  • set_tsk_need_resched(curr): 顯式地設置當前任務的 TIF_NEED_RESCHED 標志。
  • set_preempt_need_resched(): 告訴搶占機制,在下一個安全點(例如從中斷返回或系統調用結束時),應該檢查該標志并立即進行一次上下文切換。
if (cpu == smp_processor_id()) {set_tsk_need_resched(curr); // 標記當前任務需要被調度set_preempt_need_resched(); // 觸發搶占檢查return;
}

總結:need_resched 的調用鏈

現在,我們可以清晰地梳理出 TIF_NEED_RESCHED 標志的完整設置流程:

這個調用鏈展示了 Linux 內核如何利用一個周期性的定時器中斷,結合 CFS 調度器的公平性原則,最終實現了搶占式多任務的調度核心。

完整代碼參考: 完整的源碼可以在 Linux 內核源碼的 kernel/sched/fair.ckernel/sched/core.c 文件中找到這些函數的完整實現。

linux/kernel/sched/fair.c at master · torvalds/linux · GitHublinux/kernel/sched/core.c at master · torvalds/linux · GitHub

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/921851.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/921851.shtml
英文地址,請注明出處:http://en.pswp.cn/news/921851.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Redis 深度解析:數據結構、持久化與集群

Redis (Remote Dictionary Server) 是一種高性能的鍵值&#xff08;Key-Value&#xff09;內存數據庫&#xff0c;以其豐富的數據結構、極低的延遲、出色的穩定性和強大的集群能力&#xff0c;在現代應用程序的開發中扮演著至關重要的角色。無論是作為緩存、消息隊列、會話存儲…

HTTPS優化簡單總結

性能損耗選擇橢圓曲線&#xff0c;并生成橢圓曲線的計算耗時CA證書驗證的耗時計算pre-master的耗時硬件優化HTTPS是計算密集型任務&#xff0c;不是IO密集型任務所以硬件最好買更高級的CPU&#xff0c;而不是網卡&#xff0c;磁盤協議優化ECDHE代替RSA&#xff0c;因為ECDHE可以…

從IFA再出發:中國制造與海信三筒洗衣機的“答案”

當全球消費電子行業的目光再次聚焦柏林&#xff0c;柏林國際電子消費品展覽會(IFA2025)不僅成為創新產品的秀場&#xff0c;更悄然變身為中國企業講述全球化進階故事的重要舞臺。近日&#xff0c;海信旗下三筒洗衣機——棉花糖Ultra全家筒迎來它的國際首秀&#xff0c;首次海外…

c++工程如何提供http服務接口

在 C 工程里給類似 /index/api/ 的服務&#xff0c;基本步驟如下&#xff1a; 選一個HTTP服務框架&#xff1b;起一條監聽線程&#xff08;或線程池&#xff09;&#xff1b;把路徑-處理函數注冊進去&#xff1b; 下面是 2 種簡單的方案。方案 A&#xff1a;Crow&#xff08;He…

cfshow-web入門-php特性

web89 <?php ? include("flag.php"); highlight_file(__FILE__); ? if(isset($_GET[num])){$num $_GET[num];if(preg_match("/[0-9]/", $num)){die("no no no!");}if(intval($num)){echo $flag;} } 正則匹配檢查不能是數字&#xff0c;但…

ctfshow - web - 命令執行漏洞總結(二)

web73該題目沒有開啟web72的open_basedir&#xff0c;所以可以使用var_export(scandir(/));exit();進行目錄掃描。讀取文件函數&#xff1a;require_once()web74scandir()函數被禁用&#xff0c;使用glob://偽協議進行讀取根目錄文件。cvar_export(glob(../../../*));exit(); c…

如何將視頻從安卓手機傳輸到電腦?

無論你是否是視頻愛好者&#xff0c;你可能都希望知道如何將視頻從安卓手機傳輸到電腦&#xff0c;以釋放存儲空間并防止性能問題。這也有助于同步視頻或防止意外刪除。在本文中&#xff0c;我們將探索七種高效的傳輸方法。方法 1&#xff1a;僅通過 USB 將手機視頻發送到電腦許…

Pico 4 Enterprise(企業版)與Unity的交互-有線串流調試篇

入手了Pico 4 E做VR開發&#xff0c;誰知入了天坑...根據官方文檔&#xff0c;嘗試了串流助手、企業串流、PICO Developer Center&#xff0c;陷入了各種版本問題、環境問題的陷阱。而且Pico4E的OS自24年12開始就不再更新&#xff0c;頭盔中預裝的企業串流版本也較低&#xff0…

redis里多線程的應用具體在哪些場景

Redis 6.0 引入的多線程I/O&#xff0c;?特指用于處理網絡數據的讀取&#xff08;read&#xff09;和寫入&#xff08;write&#xff09;/解析&#xff08;parse&#xff09;的并行化&#xff0c;而絕非將命令的執行&#xff08;真正的數據操作&#xff09;變成多線程。這是一…

DI-GAN:基于深度學習的動態形變多模光纖透反射光控制

DI-GAN:基于深度學習的動態形變多模光纖透反射光控制 1 論文核心概念 本文提出了一種名為 DI-GAN(Deep Imaging Generative Adversarial Network) 的持續深度學習框架,用于動態形變多模光纖(MMF) 的光場控制。該框架能夠同時利用透射和反射信息,實現對光纖末端光場的實…

【深度學習新浪潮】具身智能中使用到的世界模型是什么?

在具身智能中,世界模型(World Model) 是智能體對物理環境的內在“認知地圖”,它通過學習環境的動態規律(如物體運動、物理交互、因果關系等),實現對未來狀態的預測、對過去狀態的反推,以及對未觀測狀態的補全。其核心價值在于:讓智能體無需頻繁與真實環境交互,就能在…

Qt_UI界面的設計

一、設置UI窗口大小二、接收框只讀三、下拉選項雙擊添加選項1是添加&#xff0c;2是調整順序四、標簽字體居中字體大小五、發送框六、按鈕七、透明框&#xff08;可以放標簽或圖片啥的&#xff09;設置最小寬度八、水平布局九、垂直布局十、彈簧&#xff08;方便給水平垂直布局…

FTP文件傳輸服務

一、FTP協議、服務器FTP&#xff1a;文件傳輸協議&#xff08;用于網絡文件雙向傳輸的應用層協議&#xff09;特點&#xff1a;最廣泛、最底層、較簡單&#xff0c;但是明文傳輸&#xff1b;適用于較大文件的傳輸1.常見客戶端、服務器客戶端&#xff1a;WINSCP or filezilla&am…

Nginx運維之路(Docker多段構建新版本并增加第三方模塊)

喜大普奔&#xff0c;前兩天發現Nginx竟然自帶支持了ACME功能&#xff0c;讓我很想測試一下&#xff0c;但是發現手頭沒有資源讓我測試&#xff0c;忽然我想到可以用docker來構建nginx然后測試ACME功能&#xff0c;在這個過程中發現原來官方Nginx鏡像并沒有集成ACME插件&#x…

DrissionPage 優化天貓店鋪商品爬蟲:現代化網頁抓取技術詳解

概述在網絡數據采集領域&#xff0c;傳統的爬蟲方法通常面臨反爬機制、動態內容加載和效率低下等挑戰。本文將以天貓店鋪商品爬蟲為例&#xff0c;詳細介紹如何從傳統的 Requests 庫遷移到更現代化的 DrissionPage 解決方案&#xff0c;實現更高效、穩定的數據采集。----------…

pytest并發測試,資源問題導致用例失敗解決辦法

遇見的問題&#xff1a; 測試用例使用thrift資源和redis資源&#xff0c;單獨運行case沒有問題&#xff0c;但是使用并發pytest-xdist&#xff08;-n 10 和 --distloadscope&#xff09;運行失敗原因&#xff1a; 測試用例間存在共享資源競爭&#xff08;如 Redis、Thrift 連接…

C 盤又滿了?6 個「零風險清理法」+5 款神器,讓電腦瞬間多出 100GB 空間

你是否遇到過這樣的場景&#xff1a;正在趕工寫報告&#xff0c;突然彈出「C 盤存儲空間不足」的警告&#xff1b;想安裝新軟件&#xff0c;卻因為 C 盤爆紅而反復失敗&#xff1b;甚至電腦越來越卡&#xff0c;開機要等 5 分鐘&#xff0c;打開文件夾都要轉圈…… 作為系統盤…

Android 項目:畫圖白板APP開發(四)——筆鋒(單 Path)

上一章講解了如何通過多個 Path 疊加形成筆鋒效果&#xff0c;還有另外的方式實現筆鋒&#xff0c;并且只需要一條Path就可以了。在講解具體方案之前&#xff0c;我們需要了解一個有意思的工具 PathMeasure &#xff0c;這是一個非常強大且實用的工具&#xff0c;常用于高級動畫…

從C++開始的編程生活(7)——取地址運算符重載、類型轉換、static成員和友元

前言 本系列文章承接C語言的學習&#xff0c;需要有C語言的基礎才能學會哦~ 第7篇主要講的是有關于C的取地址運算符重載、類型轉換、static成員和友元。 C才起步&#xff0c;都很簡單 目錄 前言 取地址運算符重載 const成員函數 基本語法 特點 取地址運算符重載 類型轉換…

SQL 入門指南:排序與分頁查詢(ORDER BY 多字段排序、LIMIT 分頁實戰)

在 SQL 查詢中&#xff0c;我們常需要 “按報名時間先后看活動名單”“只看第 2 頁的活動報名數據”—— 這些需求靠基礎查詢無法實現&#xff0c;而ORDER BY&#xff08;排序&#xff09; 和LIMIT&#xff08;分頁&#xff09; 就是解決這類問題的核心工具。今天我們用 “校園…