讀薄「Linux 內核設計與實現」(2) - 進程管理和調度

      網友投稿 904 2025-04-04

      這篇文章是《讀薄「Linux 內核設計與實現」》系列文章的第 II 篇,本文主要講了以下問題:進程管理的任務、進程管理與其他模塊的依賴關系、進程描述符和任務隊列、進程的創建、線程的實現、進程的終止、進程調度。


      0x00 進程管理的任務

      進程能創建新的進程(通過復制現有進程)

      確定哪個進程能擁有 CPU

      接受中斷并將中斷導向相應的內核子系統

      管理時鐘硬件

      當一個進程結束時釋放其資源

      動態裝載執行模塊

      0x01 進程管理與其他模塊的依賴關系

      I 進程模塊的內外界面

      對用戶進程提供了一組簡單的系統調用接口

      對內核的其他模塊提供了豐富的接口功能

      II 進程模塊與其他模塊的依賴關系

      內存管理模塊:當一個進程被調度時,為它建立內存映射

      IPC 模塊:bottom-half 處理使用了其中的信號量隊列

      文件系統模塊:在裝載 module 時為進程調度提供實際設備的訪問途徑

      所有其他模塊都依賴于進程調度模塊,因為當要進行硬件訪問時,它們需要 CPU 掛起用戶進程,切換到系統態進行處理

      0x02 進程描述符和任務隊列

      I 進程描述符

      task_struct (進程描述符)定義于

      II 分配進程描述符

      Linux 通過 slab 分配 task_struct 可以達到對象復用和緩存著色的目的,不需要頻繁調用內存管理相應功能,相當于一種高級緩存

      III 進程描述符的存放

      通過 PID 標識進程,最大值默認為32768

      如何獲得當前運行的進程?

      通過 current 宏(體系結構相關),current_thread_info()->task;

      0x03 進程的創建

      I 進程創建過程描述

      Linux 中,進程的創建是通過拷貝已存在的進程來實現的

      內核啟動的時候,start_kernel() 初始化各系統數據結構,同時生成了與系統共存亡的后臺進程:init

      這些子進程通過 fork() 系統調用生成他們的子進程

      進程的終止是通過系統調用 _exit() 實現的

      II 進程創建函數

      fork(): 進程復制自身產生子進程

      exec(): 加載可執行代碼模塊覆蓋自身代碼

      III 寫時拷貝(copy-on-write)

      使地址空間上的頁的拷貝被推遲到實際發生寫入的時候才進行

      0x04 線程的實現

      Linux 沒有真正的線程,線程當做進程來實現(僅僅是進程間資源直接共享的一種機制)

      通過 clone 時傳遞一些參數標志來實現:

      clone(CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND, 0);

      1

      這些標志定義于

      內核線程:獨立運行在內核的標準進程

      0x05 進程的終止

      刪除進程描述符

      父進程得知進程終止的消息后才能刪除子進程的描述符

      父進程 WAIT() (通過系統調用 wait4()實現),掛起父進程,直到其中一個子進程退出

      之后,真正釋放描述符(release_task()):

      讀薄「Linux 內核設計與實現」(2) - 進程管理和調度

      調用 _unhash_process()從 pidhash 上刪除該進程

      _exit_signal() 釋放目前僵死進程所使用剩余資源

      調用 put_task_struct 釋放描述符(內核棧、thread_info 所占的頁、slab 高速緩存

      若父進程異常終止,將發生什么情況?

      如果父進程在子進程之前推出,這些成為孤兒的進程會處于僵死狀態,解決方案是給子線程在當前進程中找一個父親,如果不行,則以 init 進程為父親,過程:

      do_exit() -> exit_notify() -> forget_original_parent() -> find_new_reaper()

      開始尋父。

      0x06 進程調度

      I 多任務系統分類

      搶占式:由調度程序決定一個進程的運行與掛起,掛起的動作就叫做搶占(Linux 為搶占式內核),進程在被搶占之前的運行時間是預先設置好的,叫做時間片

      非搶占式:除非進程自己停止,否則它會一直運行,它主動掛起自己的動作叫讓步(yielding)

      II 調度策略

      I/O 消耗型和 CPU 消耗型進程

      I/O 消耗型:進程大部分時間用來提交 I/O 請求或是等待 I/O 請求

      CPU 消耗型:進程把時間大多用在執行代碼上

      UNIX 系列傾向于 I/O 消耗型

      進程優先級(Linux 采用2種不同的優先級范圍)

      nice 值:從 -20~+19,默認為0,nice 越大,優先級越低

      實時優先級:從 0~99,越高的實時優先級越高,它的值可以配置

      時間片

      時間片分配多大?:時間片過長使得系統交互性不好;時間片過短會導致大部分 CPU 時間浪費在進程的切換上。

      Linux 采用可變長的時間片

      III Linux 調度算法

      O(1) 調度

      O(1)調度算法中的一個核心數據結構即為prio_array結構體,該結構體中有一個用來表示進程動態優先級的 queue,它其中的每一項都是一種優先級形成的進程的鏈表,即每一條鏈表中的進程都有相同優先級

      struct prio_array{ unsigned int nr_active; unsigned long bitmap[BITMAP_SIZE]; struct list_head queue[MAX_PRIO]; }

      1

      2

      3

      4

      5

      另外,該結構體中還包含一個優先級位圖 bitmap,該位圖使用1位來表示1種優先級,開始時,所有位置都置0,一旦有進程處于可運行狀態時,該進程所在優先級對應位圖中的相應位被置1.

      因此,在 O(1)調度中查找最高的優先級就轉化為查找優先級位圖中第一個被置1的位

      確定了最高優先級之后,選取下一個進程就是在 queue 對應的鏈表中取一個進程即可

      在 O(1)調度中,對可運行態的進程分了兩類:一類為活動進程,即時間片還未用完的進程;另一類為過期進程,即時間片已經用完的進程,但進程還未結束,它們沒有機會得到執行。

      在可運行隊列結構中(runqueue)的 arrays 的兩個元素分別來表示上述兩個集合,active 和 expire 兩個指針分別指向這2個集合

      active中的進程一旦時間片使用完畢就會放入expire中,并設置好新的時間片;當active為空時,說明所有進程時間片已耗盡,此時,將active和expire對調,重新開始下一輪時間片的遞減過程。

      選擇下一個進程

      時間片的重新分配

      CFS 調度(公平調度)

      O(1)算法是根據進程的優先級進行調度的;CFS 是根據進程的虛擬進程運行時間來進行調度的

      CFS 提出了虛擬運行時間的概念(vruntime),vruntime 記錄了一個可執行進程到當前時刻為止執行的總時間,vruntime 越大,它被調度的可能性越小,因此每次選擇 vruntime 越小的進程進行調度(在 Linux 中使用紅黑樹來找出 vruntime 最小的進程)

      現在已經知道了進程是如何調度的,那么當進程運行時,它的運行時間是多少呢?

      CFS 的運行時間是由當前所有可調度進程優先級比重來確定的

      例:3個進程優先級為5,15,20,它們時間片大小分配為:5/40, 15/40, 20/40

      IV 搶占和上下文切換

      上下文切換表示從一個可執行進程切換到另一個可執行進程(定義在 sched.h 中的 context_switch())

      調用 switch_mm():把虛擬進程從一個進程切換到新進程

      調用 switch_to():保存和恢復棧信息、寄存器信息

      need_resched 標志:每個進程都包含該標志,為了讓內核知道在什么時候調用schedule().當某進程應該被搶占時,設置該標志;當一個優先級高的進程進入可執行態時,也會設置該標志。

      用戶搶占的發生

      從系統調用返回用戶空間時

      從中斷處理程序返回用戶空間時

      當內核返回用戶空間時,如果 need_resched 被設置,會導致 schedule() 被調用,此時發生用戶搶占

      內核搶占的發生

      從中斷程序返回內核空間時

      內核代碼再一次具有可搶占性時

      如果內核中的任務顯示調用 schedule()

      如果內核中的任務阻塞(同樣也會調用schedule())

      Linux 任務調度

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      上一篇:報表工具(3分鐘之前已更新)
      下一篇:全球ERP實施之所以不為人所用的4個原因
      相關文章
      亚洲熟妇无码久久精品| 精品亚洲aⅴ在线观看| 亚洲va在线va天堂va手机| 亚洲黄色片在线观看| 国产亚洲精品xxx| 亚洲日韩精品A∨片无码| 国产亚洲精品a在线观看| 亚洲综合另类小说色区| 亚洲日韩在线第一页| 亚洲午夜爱爱香蕉片| ZZIJZZIJ亚洲日本少妇JIZJIZ| 亚洲精品国产高清不卡在线| 亚洲乱码国产一区网址| 久久综合亚洲色HEZYO国产| 亚洲综合色在线观看亚洲| 亚洲人成77777在线播放网站| 亚洲啪啪综合AV一区| 亚洲精品无码久久久久去q| 久久精品国产亚洲AV网站| 亚洲一区二区中文| 亚洲成AV人片久久| 国产成人精品日本亚洲专一区| 亚洲国产熟亚洲女视频| 亚洲精品色播一区二区| 亚洲av综合日韩| 亚洲精品无码成人片在线观看| 亚洲午夜久久久久妓女影院| 国产av天堂亚洲国产av天堂| 久久久亚洲欧洲日产国码二区| 亚洲精品视频在线播放| 亚洲ts人妖网站| 亚洲AV无码一区二区乱子仑| 亚洲裸男gv网站| 狠狠色伊人亚洲综合成人| 亚洲精选在线观看| 亚洲一区二区三区在线观看蜜桃| 亚洲熟妇丰满xxxxx| 亚洲国产精品综合久久网络| 亚洲午夜国产精品无码老牛影视| 亚洲福利视频导航| 国产成人亚洲合集青青草原精品|