【原創】(三)Linux程序排程器-程序切換
背景
Read the fucking source code!
--By 魯迅A picture is worth a thousand words.
--By 高爾基
說明:
- Kernel版本:4.14
- ARM64處理器,Contex-A53,雙核
- 使用工具:Source Insight 3.5, Visio
1. 概述
程序切換:核心將CPU上正在執行的程序掛起,選擇下一個程序來執行。
ARM架構中,CPU上一次只能執行一個任務,核心需要為任務分配執行時間來進行排程,以便同時能處理多個任務請求。
如下圖所示:
當進行任務切換的時候,思考下兩個問題:
- 怎樣通過搶佔來實現程序的切換?
- 當程序切換的時候,到底切換的什麼,是怎麼實現的?
這兩個問題,也是本文探討的主題了。
2. 搶佔
2.1 使用者搶佔
2.1.1 搶佔觸發點
- 可以觸發搶佔的情況很多,比如程序的時間片耗盡、程序等待在某些資源上被喚醒時、程序優先順序改變等。Linux核心是通過設定
TIF_NEED_RESCHED
標誌來對程序進行標記的,設定該位則表明需要進行排程切換,而實際的切換將在搶佔執行點來完成。
不看程式碼來講結論,那都是耍流氓。先看一下兩個關鍵結構體:struct task_struct
和struct thread_info
。我們在前邊的文章中也講過struct task_struct
用於描述任務,該結構體的首個欄位放置的正是struct thread_info
struct thread_info
結構體中flag
欄位就可用於設定TIF_NEED_RESCHED
,此外該結構體中的preempt_count
也與搶佔相關。
struct task_struct { #ifdef CONFIG_THREAD_INFO_IN_TASK /* * For reasons of header soup (see current_thread_info()), this * must be the first element of task_struct. */ struct thread_info thread_info; #endif ... } /* * low level task data that entry.S needs immediate access to. */ struct thread_info { unsigned long flags; /* low level flags */ mm_segment_t addr_limit; /* address limit */ #ifdef CONFIG_ARM64_SW_TTBR0_PAN u64 ttbr0; /* saved TTBR0_EL1 */ #endif int preempt_count; /* 0 => preemptable, <0 => bug */ }; #include <asm/current.h> #define current_thread_info() ((struct thread_info *)current) //通過該巨集可以直接獲取thread_info的資訊 #endif
看看具體哪些函式過程中,設定了TIF_NEED_RESCHED
標誌吧:
- 核心提供了
set_tsk_need_resched
函式來將thread_info
中flag
欄位設定成TIF_NEED_RESCHED
; - 設定了
TIF_NEED_RESCHED
標誌,表明需要發生搶佔排程;
2.1.2 搶佔執行點
使用者搶佔:搶佔執行發生在程序處於使用者態。
搶佔的執行,最明顯的標誌就是呼叫了schedule()
函式,來完成任務的切換。
具體來說,在使用者態執行搶佔在以下幾種情況:
- 異常處理後返回到使用者態;
- 中斷處理後返回到使用者態;
- 系統呼叫後返回到使用者態;
如下圖:
- ARMv8有4個Exception Level,其中使用者程式執行在EL0,OS執行在EL1,Hypervisor執行在EL2,Secure monitor執行在EL3;
- 使用者程式在執行過程中,遇到異常或中斷後,將會跳到
ENTRY(vectors)
向量表處開始執行; - 返回使用者空間時進行標誌位判斷,設定了
TIF_NEED_RESCHED
則需要進行排程切換,沒有設定該標誌,則檢查是否有收到訊號,有訊號未處理的話,還需要進行訊號的處理操作;
2.2 核心搶佔
Linux核心有三種核心搶佔模型,先上圖:
- CONFIG_PREEMPT_NONE:不支援搶佔,中斷退出後,需要等到低優先順序任務主動讓出CPU才發生搶佔切換;
- CONFIG_PREEMPT_VOLUNTARY:自願搶佔,程式碼中增加搶佔點,在中斷退出後遇到搶佔點時進行搶佔切換;
- CONFIG_PREEMPT:搶佔,當中斷退出後,如果遇到了更高優先順序的任務,立即進行任務搶佔;
2.2.1 搶佔觸發點
- 在核心中搶佔觸發點,也是設定
struct thread_info
的flag
欄位,設定TIF_NEED_RESCHED
表明需要請求重新排程。 - 搶佔觸發點的幾種情況,在使用者搶佔中已經分析過,不管是使用者搶佔還是核心搶佔,觸發點都是一致的;
2.2.2 搶佔執行點
核心搶佔:搶佔執行發生在程序處於核心態。
總體而言,核心搶佔執行點可以歸屬於兩大類:
- 中斷執行完畢後進行搶佔排程;
- 主動呼叫
preemp_enable
或schedule
等介面的地方進行搶佔排程;
2.3 preempt_count
- Linux核心中使用
struct thread_info
中的preempt_count
欄位來控制搶佔。 preempt_count
的低8位用於控制搶佔,當大於0時表示不可搶佔,等於0表示可搶佔。preempt_enable()
會將preempt_count
值減1,並判斷是否需要進行排程,在條件滿足時進行切換;preempt_disable()
會將preempt_count
值加1;
此外,preemt_count
欄位還用於判斷程序處於各類上下文以及開關控制等,如圖:
3. 上下文切換
- 程序上下文:包含CPU的所有暫存器值、程序的執行狀態、堆疊中的內容等,相當於程序某一時刻的快照,包含了所有的軟硬體資訊;
- 程序切換時,完成的就是上下文的切換,程序上下文的資訊會儲存在每個
struct task_struct
結構體中,以便在切換時能完成恢復工作;
程序上下文切換的入口就是__schedule()
,分析也圍繞這函式展開。
3.1 __schedule()
__schedule()
函式呼叫分析如下:
主要的邏輯:
- 根據CPU獲取執行佇列,進而得到執行隊列當前的
task
,也就是切換前的prev
; - 根據
prev
的狀態進行處理,比如pending
訊號的處理等,如果該任務是一個worker執行緒
還需要將其睡眠,並喚醒同CPU上的另一個worker執行緒
; - 根據排程類來選擇需要切換過去的下一個
task
,也就是next
; context_switch
完成程序的切換;
3.2 context_switch()
context_switch()
的呼叫分析如下:
核心的邏輯有兩部分:
程序的地址空間切換
:切換的時候要判斷切入的程序是否為核心執行緒,1)所有的使用者程序都共用一個核心地址空間,而擁有不同的使用者地址空間;2)核心執行緒本身沒有使用者地址空間。在程序在切換的過程中就需要對這些因素來考慮,涉及到頁表的切換,以及cache/tlb
的重新整理等操作。暫存器的切換
:包括CPU的通用暫存器切換、浮點暫存器切換,以及ARM處理器相關的其他一些暫存器的切換;
程序的切換,帶來的開銷不僅是頁表切換和硬體上下文的切換,還包含了Cache/TLB
重新整理後帶來的miss
的開銷。在實際的開發中,也需要去評估新增程序帶來的排程開銷。
相關推薦
【原創】(三)Linux程序排程器-程序切換
背景 Read the fucking source code! --By 魯迅 A picture is worth a thousand words. --By 高爾基 說明: Kernel版本:4.14 ARM64處理器,Contex-A53,雙核 使用工具:Source Insight 3.5,
【原創】(三)Linux paging_init解析
背景 Read the fucking source code! --By 魯迅 A picture is worth a thousand words. --By 高爾基 說明: Kernel版本:4.14 ARM64處理器,Contex-A53,雙核 使用工具:Source Insight 3.5,
【原創】(一)Linux程序排程器-基礎
背景 Read the fucking source code! --By 魯迅 A picture is worth a thousand words. --By 高爾基 說明: Kernel版本:4.14 ARM64處理器,Contex-A53,雙核 使用工具:Source Insight 3.5,
【原創】(二)Linux程序排程器-CPU負載
背景 Read the fucking source code! --By 魯迅 A picture is worth a thousand words. --By 高爾基 說明: Kernel版本:4.14 ARM64處理器,Contex-A53,雙核 使用工具:Source Insight 3.5,
【原創】(四)Linux程序排程-組排程及頻寬控制
# 背景 - `Read the fucking source code!` --By 魯迅 - `A picture is worth a thousand words.` --By 高爾基 說明: 1. Kernel版本:4.14 2. ARM64處理器,Contex-A53,雙核 3. 使用工具:S
【原創】(五)Linux程序排程-CFS排程器
# 背景 - `Read the fucking source code!` --By 魯迅 - `A picture is worth a thousand words.` --By 高爾基 說明: 1. Kernel版本:4.14 2. ARM64處理器,Contex-A53,雙核 3. 使用工具:S
【原創】(六)Linux程序排程-實時排程器
背景 Read the fucking source code! --By 魯迅 A picture is worth a thousand words. --By 高爾基 說明: Kernel版本:4.14 ARM64處理器,Contex-A53,雙核 使用工具:Source Insight 3.5,
【原創】(四)Linux記憶體模型之Sparse Memory Model
背景 Read the fucking source code! --By 魯迅 A picture is worth a thousand words. --By 高爾基 說明: Kernel版本:4.14 ARM64處理器,Contex-A53,雙核 使用工具:Source Insight 3.5,
【原創】(六)Linux記憶體管理 - zoned page frame allocator - 1
背景 Read the fucking source code! --By 魯迅 A picture is worth a thousand words. --By 高爾基 說明: Kernel版本:4.14 ARM64處理器,Contex-A53,雙核 使用工具:Source Insight 3.5,
【原創】(七)Linux記憶體管理 - zoned page frame allocator - 2
背景 Read the fucking source code! --By 魯迅 A picture is worth a thousand words. --By 高爾基 說明: Kernel版本:4.14 ARM64處理器,Contex-A53,雙核 使用工具:Source Insight 3.5,
【原創】(八)Linux記憶體管理 - zoned page frame allocator - 3
背景 Read the fucking source code! --By 魯迅 A picture is worth a thousand words. --By 高爾基 說明: Kernel版本:4.14 ARM64處理器,Contex-A53,雙核 使用工具:Source Insight 3.5,
【原創】(九)Linux記憶體管理 - zoned page frame allocator - 4
背景 Read the fucking source code! --By 魯迅 A picture is worth a thousand words. --By 高爾基 說明: Kernel版本:4.14 ARM64處理器,Contex-A53,雙核 使用工具:Source Insight 3.5,
【原創】(十三)Linux記憶體管理之vma/malloc/mmap
背景 Read the fucking source code! --By 魯迅 A picture is worth a thousand words. --By 高爾基 說明: Kernel版本:4.14 ARM64處理器,Contex-A53,雙核 使用工具:Source Insight 3.5,
【Mybatis】(三)執行CRUD操作——基於XML實現
在【Mybatis】(一)第一個mybatis例項中,已經初步搭建了 MyBatis 框架,實現了查詢所有記錄的功能,並用 JUnit 進行了單元測試 接下來我們將在此基礎上使用基於XML的方式對錶進行CRUD操作 1、定義EmployeeMapper.xm
IDEA從0搭建SSM專案【圖文】(三)——部署到遠端伺服器執行
系列(一)和(二)實現了本地localhost執行,但是實際生產往往需要我們把專案放到遠端伺服器上執行,本文演示如何具體實現~ 一. 需要具備的知識 1.maven命令 2.linux常用命令 3.linux安裝jdk,tomcat,mysql 二.伺服器 1.
【Java】(三)運算子小結(比較、邏輯、三元運算子)
前面介紹的兩種運算子都比較簡單,下面我們來看一下比較複雜一點的三種運算子:比較運算子、邏輯運算子、三元運算子。 一、比較運算子 又叫關係運算符,用於判斷兩個被運算元的
【Docker】基於例項專案的叢集部署(三)Linux基礎命令
Linux系統作為優秀的企業級伺服器系統,有多處優點: 可靠的安全性 良好的穩定性 完善的網路功能 多使用者任務 豐富的軟體支援 跨平臺的硬體支援 目錄結構 我們可以通過以下結構瞭解Linux的目錄作用: 命令操作
【原創】(十一)Linux記憶體管理slub分配器
背景 Read the fucking source code! --By 魯迅 A picture is worth a thousand words. --By 高爾基 說明: Kernel版本:4.14 ARM64處理器,Contex-A53,雙核 使用工具:Source Insight 3.5,
【原創】(十二)Linux記憶體管理之vmap與vmalloc
背景 Read the fucking source code! --By 魯迅 A picture is worth a thousand words. --By 高爾基 說明: Kernel版本:4.14 ARM64處理器,Contex-A53,雙核 使用工具:Source Insight 3.5,
【原創】(十四)Linux記憶體管理之page fault處理
背景 Read the fucking source code! --By 魯迅 A picture is worth a thousand words. --By 高爾基 說明: Kernel版本:4.14 ARM64處理器,Contex-A53,雙核 使用工具:Source Insight 3.5,