一文看懂Linux內核:Linux操作系統原理與應用

X86架構

CPU包括3個部分:運算單元,數據單元,控制單元

總線上主要有兩類數據,一類是地址數據(要拿內存中哪個位置的數據),這類總線是地址總線;另一類是真正的數據,這類總線是數據總線

32位CPU包含的寄存器

  • 通用寄存器:EAX/EBX/ECX/EDX/ESI/EDI/ESP/EBP
  • 段寄存器:CS/DS/ES/FS/GS/SS
  • 指令寄存器:EIP
  • 標誌寄存器:EFLAGS
  • 控制寄存器:CR0/CR2/CR3/CR4
  • 系統表指針寄存器:IDTR/GDTR/LDTR
  • 任務寄存器:TR
  • 調試寄存器:DR0-DR7

PS:視頻相關學習文檔,後台私信(Linux)

一文帶大家明白,操作系統原理之Linux進程調度和管理
一文帶大家明白,操作系統原理之Linux進程調度和管理
  • CS:存儲代碼段的基址
  • DS:存儲數據段的基址
  • SS:存儲運行棧的基址
  • EBP:棧基指針寄存器
  • ESP:棧頂指針寄存器
  • IP:指令指針寄存器,將CS:IP獲取到的指令存儲EIP(指令寄存器)中

操作系統啟動流程

主板上的ROM固化了一段初始化程序BIOS(Basic Input and Output System,基本輸入輸出系統)

Grub2是一個linux啟動管理器 ,Grub2把boot.img共512位元組安裝到啟動盤的第一個扇區,這個扇區稱為MBR(Master Boot Record,主引導記錄扇區)

  • 主機上電後,CS重置為0xFFFF,IP重置為0x0000,所以第一條指令指向0xFFFF0,正好在ROM範圍內,從這裡開始進行硬件檢查
  • 將磁盤引導扇區讀入0x7c00處,並從0x7c000開始執行
  • boot模塊:bootsect.s,繼續讀取其它扇區
  • setup模塊:setup.s
  • 從實模式切換到保護模式(CR0寄存器最後一位置1)
  • 初始化GDT,IDT
  • system模塊:許多文件(head.s,main.c …)
  • 進入main函數,進入各種初始化操作 INIT_TASK(init_task):系統創建第一個進程,0號進程。這是唯一一個沒有通過fork或者kernel_thread產生的進程,是進程列表的第一個 trap_init() mm_init() sched_init() rest_init()
  • kernel_thread(kernel_init, NULL, CLONE_FS) 創建第二個進程,這個是1號進程。 1號進程即將運行一個用戶進程
  • 從內核態到用戶態
  • ramdisk
  • 創建2號進程

0號進程 -> 1號內核進程 -> 1號用戶進程(init進程) -> getty進程 -> shell進程 -> 命令行執行進程。

  1. 加載內核:打開電源後,加載BIOS(只讀,在主板的ROM里),硬件自檢,讀取MBR(主引導記錄),運行Boot Loader,內核就啟動了,這裡還有從實模式到保護模式的切換
  2. 進程:內核啟動後,先啟動第0號進程init_task,然後在內核運行init()函數,1號進程進入用戶態變為init進程,init進程是所有用戶態進程的老祖宗,其配置文件是/etc/inittab,通過該文件設置運行等級:根據/etc/inittab文件設置運行等級,比如有無網絡
  3. 系統初始化:啟動第一個用戶層文件/etc/rc.d/rc.sysinit,然後激活交換分區、檢查磁盤、加載硬件模塊等
  4. 建立終端:系統初始化後返回init,這時守護進程也啟動了,init接下來打開終端以供用戶登錄
  • init_task是第0號進程,唯一一個沒有通過fork或kernel_thread產生的進程,是進程列表的第一個
  • init是第1號進程,會成為用戶態,是所有用戶態進程的祖先
  • kthreadd是第2號進程,會成為內核態,是所有內核態進程的祖先

系統調用

int指令將CS中的CPL改為0,從而進入內核,這是用戶程序發起調用內核代碼的唯一方式

系統調用核心

  1. 用戶程序中包含一段包含int指令的代碼
  2. 操作系統寫中斷處理,獲取調用程序的編號
  3. 操作系統根據編號執行相應代碼
一文帶大家明白,操作系統原理之Linux進程調度和管理
  1. 在glibc里,任何一個系統調用都會調用DO_CALL,在DO_CALL里會根據系統調用名稱,得到系統調用號,放在寄存器EAX里,把請求參數放在其它寄存器里,然後執行int $0x80
  2. int $0x80觸發一個軟中斷,通過它陷入內核 內核啟動時trap_init()指定軟中斷陷入門的入口,即entry_INT80_32
  3. 保存當前用戶態的寄存器,保存在pt_regs結構
  4. 將系統調用號從寄存器EAX里取出來,然後根據系統調用號,在系統調用表中找到相應的函數進行調用
  5. 系統調用結束後, 原來用戶態保存的現場恢復回來

進程狀態切換

  • 就緒狀態(ready):等待被調度
  • 運行狀態(running)
  • 阻塞狀態(waiting):等待資源
一文帶大家明白,操作系統原理之Linux進程調度和管理
  • 只有就緒態和運行態可以相互轉換,其它的都是單向轉換。就緒狀態的進程通過調度算法從而獲得 CPU 時間,轉為運行狀態;而運行狀態的進程,在分配給它的 CPU 時間片用完之後就會轉為就緒狀態,等待下一次調度。
  • 阻塞狀態是缺少需要的資源從而由運行狀態轉換而來,但是該資源不包括 CPU 時間,缺少 CPU 時間會從運行態轉換為就緒態。

進程數據結構

Linux的PCB是task_struct結構

一文帶大家明白,操作系統原理之Linux進程調度和管理

進程狀態

一文帶大家明白,操作系統原理之Linux進程調度和管理

很多操作系統教科書將正在CPU上執行的進程定義為RUNNING狀態,而將可執行但是尚未被調度執行的進程定義為READY狀態,這兩種狀態在linux下統一為TASK_RUNNING狀態

任務ID

在Linux系統中,一個線程組中的所有線程使用和該線程組的領頭線程相同的pid,並被存放在tgid成員中。只有線程組的領頭線程的pid成員才會被設置為與tgid相同的值

注意,getpid()系統調用返回的是當前進程的tgid值而不是pid值

用戶棧和內核棧

內核在創建進程的時候,在創建task_struct的同時,會為進程創建相應的堆棧。每一個進程都有兩個棧,一個用戶棧,存在於用戶空間;一個內核棧,存在於內核空間

當進程在用戶空間運行時,CPU堆棧指針寄存器裏面的內容都是用戶棧地址,使用用戶棧;當進程在內核空間運行時,CPU堆棧指針寄存器裏面的內容是內核棧地址,使用內核棧

當進程因為中斷或者系統調用陷入到內核態時,進程所使用的堆棧也要從用戶棧轉到內核棧。

進程陷入到內核態後,先把用戶態堆棧的地址保存在內核棧之中,然後設置堆棧指針寄存器的內容為內核棧的地址,這樣就完成了用戶棧向內核棧的轉換;當進程從內核態恢復到用戶態時,在內核態之後的最後將保存在內核棧裏面的用戶棧的地址恢復到堆棧指針寄存器即可。這樣就實現了內核棧向用戶棧的轉換

在進程從用戶態轉到內核態的時候,進程的內核棧總是空的。這是因為當進程在用戶態運行時使用用戶棧,當進程陷入到內核態時,內核保存進程在內核態運行的相關信息,但是一旦進程返回到用戶態後,內核棧中保存的信息全部無效,因此每次進程從用戶態陷入內核的時候得到的內核棧都是空的。所以在進程陷入內核的時候,直接把內核棧的棧頂地址給堆棧指針寄存器就可以了

一文帶大家明白,操作系統原理之Linux進程調度和管理

task_struct->stack指向進程的內核棧,大小為8K

union thread_union {
    struct thread_info thread_info;
    unsigned long stack[THREAD_SIZE/sizeof(long)];
};

整個內核棧用union表示,thread_info和stack共用一段存儲空間,thread_info佔用低地址。在pt_regs和STACK_END_MAGIC之間,就是內核代碼的運行棧。當內核棧增長超過STACK_END_MAGIC就會報內核棧溢出

thread_info:存儲內核態運行的一些信息,如指向task_struct的task指針,使得陷入內核態之後仍然能夠找到當前進程的task_struct,還包括是否允許內核中斷的preemt_count開關等等

pt_regs存儲用戶態的硬件上下文,用戶態進入內核態後,由於使用的棧、內存地址空間、代碼段等都不同,所以用戶態的eip、esp、ebp等需要保存現場,內核態恢復到用戶態時再將棧中的信息恢復到硬件。由於進程調度一定會在內核態的schedule函數,用戶態的所有硬件信息都保存在pt_regs中了。SAVE_ALL指令就是將用戶態的cpu寄存器值保存如內核棧,RESTORE_ALL就是將pt_regs中的值恢復到寄存器中,這兩個指令在介紹中斷的時候還會提到

TSS(task state segment):這是intel為上層做進程切換提供的硬件支持,還有一個TR(task register)寄存器專門指向這個內存區域。當TR指針值變更時,intel會將當前所有寄存器值存放到當前進程的tss中,然後再講切換進程的目標tss值加載後寄存器中

這裡很多人都會有疑問,不是有內核棧的pt_regs存儲硬件上下文了嗎,為什麼還要有tss?前文說過,進程切換都是在內核態,而pt_regs是保存的用戶態的硬件上下文,tss用於保存內核態的硬件上下文

但是linux並沒有買賬使用tss,因為linux實現進程切換時並不需要所有寄存器都切換一次,如果使用tr去切換tss就必須切換全部寄存器,性能開銷會很高。這也是intel設計的敗筆,沒有把這個功能做的更加的開放導致linux沒有用。linux使用的是軟切換,主要使用thread_struct,tss僅使用esp0這個值,用於進程在用戶態 -> 內核態時,硬件會自動將該值填充到esp寄存器。在初始化時僅為每1個cpu僅綁定一個tss,然後tr指針一直指向這個tss,永不切換。

thread_struct:一個和硬件體系強相關的結構體,用來存儲內核態切換時的硬件上下文

context_switch執行過程

內存空間切換

將curr_task設置為新進程的task

將cpu寄存器保存到舊進程的thread_struct結構

將新進程的thread_struct的寄存器的值寫入cpu

切換棧頂指針

PS:文章福利後台私信(Linux)獲取

一文帶大家明白,操作系統原理之Linux進程調度和管理

Linux調度算法

調度子系統

一文帶大家明白,操作系統原理之Linux進程調度和管理

調度策略和調度類

  • Linux把進程分成實時進程非實時進程,非實時進程進一步分成交互式進程批處理進程
  • 實時進程要求執行時間控制在一定範圍內,基於優先級調度,可搶佔低優先級進程
task_struct {
  unsigned int policy  // 調度策略

  int prio, static_prio, normal_prio;   // 優先級
  unsigned int rt_priority;

  const struct sched_class* sched_class;   // 調度器類
}
// 調度策略定義
#define SCHED_NORMAL   0         // 普通進程
#define SCHED_FIFO     1         // 相同優先級先來先服務
#define SCHED_RR       2         // 相同優先級輪流調度
#define SCHED_BATCH    3         // 後台進程
#define SCHED_IDLE     5         // 空閑進程
#define SCHED_DEADLINE 6         // 相同優先級電梯算法(deadline距離當前時間點最近)
  • 實時進程的優先級範圍是0-99,非實時進程優先級範圍100-139,數值越小,優先級越高
  • 實時進程的調度策略:SCHED_FIFO、SCHED_RR、SCHED_DEADLINE
  • 非實時進程的調度策略:SCHED_NORMAL、SCHED_BATCH、SCHED_IDLE
// 調度器類定義
stop_sched_class  // 優先級最高的任務會使用這種策略,中斷所有其他線程,且不會被其他任務打斷
dl_sched_class    // 對應deadline調度策略
rt_sched_class    // 對應RR算法或者FIFO算法的調度策略,具體調度策略由進程的task_struct->policy指定
fair_sched_class  // 普通進程的調度策略
idle_sched_class  // 空閑進程的調度策略

CFS調度算法(完全公平調度算法)

  • CFS給每個進程安排一個虛擬運行時間vruntime,正在運行的進程vruntime隨tick不斷增大,沒有運行的進程vruntime不變,vruntime小的會被優先運行
  • 對於不同優先級的進程,換算vruntime時優先級高的算少,優先級低的算多,這樣優先級高的進程實際運行時間就變多了
  • 調度隊列使用紅黑樹,紅黑樹的節點是調度實體
一文帶大家明白,操作系統原理之Linux進程調度和管理
  • CFS的隊列是一棵紅黑樹,紅黑樹的節點是調度實體,每個調度實體都屬於一個task_struct,task_struct裏面有指針指向這個進程屬於哪個調度類
  • CPU需要找下一個任務執行時,會按照優先級依次調用調度類,不同的調度類操作不同的隊列。當然rt_sched_class先被調用,它會在rt_rq上找下一個任務,只有找不到的時候,才輪到fair_sched_class被調用,它會在cfs_rq上找下一個任務。這樣保證了實時任務的優先級永遠大於普通任務

調度觸發

  • 調度觸發有兩種類型:主動調度被動調度(搶佔式調度)
  • 進程的調度都最終會調用到內核態的**__schedule函數**
一文帶大家明白,操作系統原理之Linux進程調度和管理

主動調度

  • 進程發生需要等待IO的系統調用
  • 進程主動sleep
  • 進程等待佔用信號量或mutex(自旋鎖不會觸發調度)

搶佔式調度(**先標記為應該被搶佔,等到調用__schedule函數時才調度**)

  • 時鐘中斷處理函數
  • 當一個進程被喚醒的時候

搶佔的時機

用戶態的搶佔時機

  • 從系統調用返回用戶態
  • 從中斷返回用戶態

內核態的搶佔時機

  • 在內核態的執行中,有的操作是不能被中斷的,在進行這些操作之前,先調用preempt_disable關閉搶佔,當再次打開的時候,就是一次內核態被搶佔的機會
  • 從中斷返內核態

fork過程

fork系統調用最終調用_do_fork函數

  1. 複製結構copy_process
  2. 喚醒新進程wake_up_new_task
一文帶大家明白,操作系統原理之Linux進程調度和管理

線程創建過程

線程不是一個完全由內核實現的機制,它是由內核態和用戶態合作完成的

調用clone系統調用

創建進程調用的系統調用是fork,在copy_process函數裏面,會將五大結構files_struct、fs_struct、sighand_struct、signal_struct、mm_struct都複製一遍,從此父進程和子進程各用各的數據結構

創建線程調用的系統調用是clone,在copy_process函數裏面,五大結構僅僅是引用計數加一,即線程共享進程的數據結構

一文帶大家明白,操作系統原理之Linux進程調度和管理

Linux裝載和運行程序

Shell進程在讀取用戶輸入的命令之後會調用fork複製出一個新的Shell進程,然後新的Shell進程調用exec執行新的程序

函數調用堆棧

3個重要的寄存器

  • EIP指向CPU下次要執行指令
  • ESP棧指針寄存器,永遠指向系統棧最上面一個棧幀的棧頂
  • EBP基址指針寄存器,永遠指向系統棧最上面一個棧幀的棧底

棧幀

每個函數每次調用,都有自己獨立的棧幀,ESP指向當前棧幀的棧頂,EBP指向當前棧幀的棧底

棧從高地址向低地址擴展

push eax; == esp=esp-4;eax->[esp];
pop eax; == [esp]->eax;esp=esp+4;

在main()里調用A(a, b)

  1. push b; 最右邊參數先入棧
  2. push a;
  3. push eip; 將A函數的EIP壓棧
  4. push ebp; 將A函數的EBP壓棧(把main函數的棧幀底部保存起來,不用保存棧幀頂部)
  5. mov ebp esp; 將當前的ESP賦值給EBP,相當於EBP從指向main的棧基址指向了A的棧基址 …
  6. 當從A函數返回時,ESP移動到棧幀底部(釋放局部變量),然後把main函數的棧幀底部彈出到EBP,再彈出返回地址到EIP上,ESP繼續移動划過參數,這樣,EBP,ESP就回到了調用函數前的狀態,即恢復了原來的main的棧幀

CPU上下文切換

CPU上下文:CPU相關寄存器

CPU上下文切換:保存前一個任務的CPU上下文,加載新任務的CPU上下文,然後運行新任務

根據任務不同,CPU上下文切換分成不同場景

  • 進程上下文切換
  • 線程上下文切換
  • 中斷上下文切換

進程可以在用戶空間運行,也可以在內核空間運行,用戶態和內核態的轉變通過系統調用完成;

系統調用過程發生了CPU上下文切換

  • 一次系統調用,發生了兩次CPU上下文切換

進程的切換髮生在內核態,進程的上下文不僅包括了虛擬內存,棧,全局變量等用戶空間的資源,還包括內核堆棧,寄存器等內核空間的狀態

進程上下文切換比系統調用多了一步

當虛擬內存更新後,TLB也需要更新,內存訪問會隨之變慢,影響所有處理器的進程

線程上下文切換

  • 線程是調度的基本單位
  • 前後兩個線程屬於不同進程,切換過程同進程上下文切換
  • 前後兩個線程屬於同一進程,切換時虛擬內存等共享資源不動,只切換線程私有數據等非共享資源

中斷上下文只包括內核中斷服務程序執行所必需的的狀態,包括CPU寄存器,內核堆棧,硬件中斷參數等

過多的上下文切換,會把CPU時間消耗在寄存器、內核棧以及虛擬內存等數據的保存和恢復上,從而縮短進程真正運行的時間,導致系統的整體性能大幅下降

# 每隔5s輸出1組數據
vmstat 5
#    cs(context switch)是每秒上下文切換的次數
#    in(interrupt)則是每秒中斷的次數
#    r(Running or Runnable)是就緒隊列的長度,也就是正在運行和等待CPU的進程數
#    b(Blocked)則是處於不可中斷睡眠狀態的進程數

# 每隔5s輸出1組數據
pidstat -wt 5
#    cswch 每秒自願上下文切換的次數
#    nvcswch 每秒非自願上下文切換的次數

內核線程、輕量級進程、用戶進程

Linux下只有一種類型的進程,就是task_struct

一個進程由於其運行空間的不同, 從而有內核線程和用戶進程的區分, 內核線程運行在內核空間, 之所以稱之為線程是因為它沒有虛擬地址空間,只能訪問內核的代碼和數據。而用戶進程則運行在用戶空間,但是可以通過中斷, 系統調用等方式從用戶態陷入內核態

用戶進程運行在用戶空間上, 而一些通過共享資源實現的一組進程我們稱之為線程組。linux下內核其實本質上沒有線程的概念,linux下線程其實上是與其他進程共享某些資源的進程而已。但是我們習慣上還是稱他們為線程或者輕量級進程

進程棧,線程棧,內核棧,中斷棧

  • 進程棧就是Linux內存結構的棧,向低地址增長
  • 線程棧由mmap獲得,和進程處於同一地址空間,線程棧是事先分配好的,不能動態增長
  • 內核棧在地址空間的最高處,當用戶調用系統調用時就進入內核棧,進程間的內核棧是相互獨立的
  • 中斷棧用於運行中斷響應程序

中斷

中斷導致系統從用戶態轉為內核態

  • 用戶程序調用系統調用引起的軟件中斷
  • 由外部設備產生的硬件中斷
  • 代碼執行出錯導致的異常

軟件中斷

用戶進程調用系統調用時,會觸發軟件中斷(第128號中斷),該中斷使得系統轉到內核態,並執行相應的中斷處理程序,中斷處理程序根據系統調用號調用對應的系統調用

硬件中斷

CPU收到硬件中斷後,就會通知操作系統,每個硬件中斷有對應一個中斷號,並對應一個中斷處理程序(ISR)

中斷處理程序一般分為兩部分(top half,bottom half),執行前面部分時會禁止所有中斷,做一些有嚴格時限的工作,在執行後面部分時則允許被打斷

Linux2.6之後,每個處理器都擁有一個中斷棧專門用來執行中斷處理程序

中斷處理流程

  1. 硬件中斷到達中斷控制器,後者檢查該中斷是否被屏蔽
  2. 中斷到達CPU
  3. 系統跳轉到一個預定義的內存位置,執行預定義的指令
  4. 屏蔽相同的中斷,檢查是否有對應的ISR
  5. 如果有就執行對應的ISR
  6. 檢查是否需要調用schedule(),返回被中斷處

用戶態和內核態

系統調用、異常、外圍設備中斷會導致系統進入內核態

系統調用:malloc內存分配

異常:缺頁異常

外圍設備中斷:鼠標鍵盤

從用戶態轉入內核態時,系統需要先保存進程上下文,切換到內核棧,更新寄存器,將權限修改為特權級,轉而去執行相應指令

進程、線程、協程

進程

  • 進程是資源分配的基本單位,它是程序執行時的一個實例。程序運行時系統就會創建一個進程,並為它分配資源,然後把該進程放入進程就緒隊列,進程調度器選中它的時候就會為它分配CPU時間,程序開始真正運行
  • 一個進程收到來自客戶端新的請求時,可以通過fork()複製出一個子進程來處理,父進程只需負責監控請求的到來,這樣就能做到並發處理。根據寫時拷貝(COW)機制,分為兩個進程繼續運行後面的代碼。fork分別在父進程和子進程中返回,在子進程永遠返回0,在父進程返回的是子進程的pid

線程

  • 線程是資源調度的基本單位,一個進程可以由很多個線程組成,線程間共享進程的所有資源,每個線程有自己的堆棧和局部變量。線程由CPU獨立調度執行,在多CPU環境下就允許多個線程同時運行。同樣多線程也可以實現並發操作,每個請求分配一個線程來處理

線程和進程的區別

  • 進程是資源分配的最小單位,線程是資源調度的最小單位
  • 一個進程的所有線程共享地址空間,但每個線程具有獨立的程序計數器,寄存器,棧,CPU切換的代價和創建一個線程的開銷都比進程小
  • 進程間通信需要通過IPC進行,線程間通信更方便,但要處理好同步與互斥
  • 多進程程序更健壯,因為進程有自己獨立的地址空間,多線程程序只要有一個線程死掉,整個進程就死掉了

協程

協程是用戶態的輕量級線程,其調度由用戶控制,共享進程的地址空間,協程有自己的棧和寄存器,在切換時需要保存/恢復狀態,切換時減少了切換到內核態的開銷. 一個進程和線程都可以有多個協程. 相比函數調用, 協程可以在遇到IO阻塞時交出控制權

  • 子程序,或者稱為函數,在所有語言中都是層級調用,比如A調用B,B在執行過程中又調用了C,C執行完畢返回,B執行完畢返回,最後是A執行完畢。
  • 所以子程序調用是通過棧實現的,一個線程就是執行一個子程序。
  • 子程序調用總是一個入口,一次返回,調用順序是明確的。
  • 而協程的調用和子程序不同。協程看上去也是子程序,但執行過程中,在子程序內部可中斷,然後轉而執行別的子程序,在適當的時候再返回來接着執行。
  • 在一個子程序中中斷,去執行其他子程序,不是函數調用,有點類似CPU的中斷
  • 協程的特點在於是一個線程執行,那和多線程比,協程有何優勢?
  • 最大的優勢就是協程極高的執行效率。因為子程序切換不是線程切換,而是由程序自身控制,因此,沒有線程切換的開銷,和多線程比,線程數量越多,協程的性能優勢就越明顯。
  • 第二大優勢就是不需要多線程的鎖機制,因為只有一個線程,也不存在同時寫變量衝突,在協程中控制共享資源不加鎖,只需要判斷狀態就好了,所以執行效率比多線程高很多。
  • 因為協程是一個線程執行,那怎麼利用多核CPU呢?
  • 最簡單的方法是多進程+協程,既充分利用多核,又充分發揮協程的高效率,可獲得極高的性能。
  • Python對協程的支持還非常有限,用在generator中的yield可以一定程度上實現協程。雖然支持不完全,但已經可以發揮相當大的威力了。

多進程與多線程的使用場景

多進程適用於CPU密集型,或者多機分佈式場景中, 易於多機擴展

多線程模型的優勢是線程間切換代價較小,因此適用於I/O密集型的工作場景,因此I/O密集型的工作場景經常會由於I/O阻塞導致頻繁的切換線程。同時,多線程模型也適用於單機多核分佈式場景

協程

協程(coroutine)又叫微線程、纖程,完全位於用戶態,一個程序可以有多個協程

協程的執行過程類似於子例程,允許子例程在特定地方掛起和恢復

協程是一種偽多線程,在一個線程內執行,由用戶切換,由用戶選擇切換時機沒有進入內核態,只涉及CPU上下文切換,所以切換效率很高

缺點:協程適用於IO密集型,不適用於CPU密集型

進程調度算法

  • RR 時間片輪轉調度算法
  • FCFS 先來先服務調度算法
  • SJF 短作業優先調度算法
  • 優先級調度算法
  • 多級隊列調度算法
  • 多級反饋隊列調度算法
  • 高響應比優先調度算法

對於單處理器系統,每次只允許一個進程運行,任何其他進程必須等待,直到CPU空閑能被調度為止,多道程序的目的是在任何時候都有某些進程在運行,以使CPU使用率最大化。

  • 先到先服務FCFS,用了FIFO隊列,非搶佔
  • 輪轉法調度Round Robin,就緒隊列作為循環隊列,按時間片切換,一般20ms~50ms
  • 最短作業優先調度SJF,先處理短進程,平均等待時間最小,所以是最佳的,但是很難知道每個作業要多少實現,所以不太現實
  • 優先級調度,會有飢餓現象,低優先級的進程永遠得不到調度
  • 多級隊列調度,根據進程的屬性(如內存大小、類型、優先級)分到特定隊列,不同隊列執行不同的調度算法
  • 多級反饋隊列調度,允許進程在隊列之間移動
  • 如果進程使用過多的CPU時間,那麼它會被移到更低的優先級隊列。
  • 這種方案將I/O密集型和交互進程放在更高優先級隊列上。
  • 此外,在較低優先級隊列中等待過長的進程會被移到更高優先級隊列。這種形式的老化可阻止飢餓的發生。

CPU密集轉為IO密集

飢餓(starvation)是什麼,如何解決

飢餓是指某進程因為優先級的關係一直得不到CPU的調度,可能永遠處於等待/就緒狀態;定期提升進程的優先級

原創文章,作者:投稿專員,如若轉載,請註明出處:https://www.506064.com/zh-hk/n/217215.html

(0)
打賞 微信掃一掃 微信掃一掃 支付寶掃一掃 支付寶掃一掃
投稿專員的頭像投稿專員
上一篇 2024-12-09 00:26
下一篇 2024-12-09 00:26

相關推薦

發表回復

登錄後才能評論