1. 程式人生 > >【原創】(三)Linux程序排程器-程序切換

【原創】(三)Linux程序排程器-程序切換

背景

  • Read the fucking source code! --By 魯迅
  • A picture is worth a thousand words. --By 高爾基

說明:

  1. Kernel版本:4.14
  2. ARM64處理器,Contex-A53,雙核
  3. 使用工具:Source Insight 3.5, Visio

1. 概述

程序切換:核心將CPU上正在執行的程序掛起,選擇下一個程序來執行。
ARM架構中,CPU上一次只能執行一個任務,核心需要為任務分配執行時間來進行排程,以便同時能處理多個任務請求。
如下圖所示:

當進行任務切換的時候,思考下兩個問題:

  1. 怎樣通過搶佔來實現程序的切換?
  2. 當程序切換的時候,到底切換的什麼,是怎麼實現的?

這兩個問題,也是本文探討的主題了。

2. 搶佔

2.1 使用者搶佔

2.1.1 搶佔觸發點

  • 可以觸發搶佔的情況很多,比如程序的時間片耗盡、程序等待在某些資源上被喚醒時、程序優先順序改變等。Linux核心是通過設定TIF_NEED_RESCHED標誌來對程序進行標記的,設定該位則表明需要進行排程切換,而實際的切換將在搶佔執行點來完成。

不看程式碼來講結論,那都是耍流氓。先看一下兩個關鍵結構體:struct task_structstruct 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_infoflag欄位設定成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_infoflag欄位,設定TIF_NEED_RESCHED表明需要請求重新排程。
  • 搶佔觸發點的幾種情況,在使用者搶佔中已經分析過,不管是使用者搶佔還是核心搶佔,觸發點都是一致的;

2.2.2 搶佔執行點

核心搶佔:搶佔執行發生在程序處於核心態。

總體而言,核心搶佔執行點可以歸屬於兩大類:

  • 中斷執行完畢後進行搶佔排程;
  • 主動呼叫preemp_enableschedule等介面的地方進行搶佔排程;

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,