計算機操作系統處理機調度讀后感:
筆者在看操作系統西安電子科技大學那本書的時候,初次感覺本科教的不會太難,所以沒有認真的看,但是隨后這本書講的刷新了我的世界觀。這本書居然是ring0級別的,這時不禁吐槽一下。。如果沒調試過程序,沒接觸過ring0的同學,這本書就和馬原一樣。全背完還不知道學了啥。
由於筆者之前做過逆向工程。而調試的大都是ring3級別的,這本書是ring0級別的。我必須要把這些知識和之前學的連接起來,以便以后接觸ring0的時候能更輕松一些。
1、創建進程。
在這個模塊我會從一個創建進程的函數開始,將我知道的進程線程等內核對象與目前看的這本書聯系起來。
ps:當你雙擊一個桌面一個exe執行文件的時候,實際上是一個名叫explorer.exe的進程幫你去創建一個進程。如果你把這個進程終結了的話你會發現圖標及任務欄全沒了。剛剛啟動的窗口卻還在。沒了父進程的我們一般稱作孤兒進程。(笑)

這是win32api給出的創建進程的方法。下面是對於創建進程需要的參數。
1、作業調度參數
IpApplicationName 一般填入文件路徑。指向一個NULL結尾的、用來指定可執行模塊的字符串。說白了告訴程序要從哪把PE文件拽入內存中(這個過程一般稱為作業調度)。
lpCommandLine指向一個以NULL結尾的字符串,該字符串指定要執行的命令行。 可以把他理解為main方法中的參數argv[]。
dwCreationFlags一般設置進程的優先級,和其他細節操作。有很多參數網上都有。沒啥用。
2、進程通信參數
lpProcessAttributesh、lpThreadAttributesh和bInheritHandles
這三個參數分別是子進程是否繼承父進程的句柄表。如果繼承了的話,子類可以訪問其他父類創建的進程。話說回來我覺得這個就是屬於操作系統進程通信中的共享存儲器系統 。通過共享句柄表,來進行子進程和父進程的通信。
而線程在筆者心中和進程一樣,都是屬於內核對象。原因很簡單,他們都有自己的句柄。而且在程序中都叫Handle。很多人認為線程比進程要矮一級,筆者認為這是錯的。進程是提供資源的,例如:PE文件在裝入內存后(作業調度)在內存中分配各種代碼段,數據段,堆棧,還有各種其他信息。而線程則是執行這些代碼段,享用這些堆棧的。
一個進程如果沒了線程就會被殺死,因為進程的存在是沒有意義的,空占內存!
3、各種作業調度的各種細節函數(沒啥用,一般給NULL就行)
lpEnvironment
指向一個新進程的環境塊。如果此參數為空,新進程使用調用進程的環境。
lpCurrentDirectory
指定子進程的工作路徑。
lpStartupInfo
啟動信息。
lpProcessInformation
進程信息
2、進程調度
1、虛擬內存
筆者這里需要引入一個虛擬內存的概念,這個虛擬內存不同於這本書后面的虛擬內存存儲器,(那個虛擬內存為了解決有的作業比較大,內存一時不夠用了而創建的一種分配機制)。
這里借用一下0day安全那本書的一張圖

雖然每個進程都“相信”自己擁有 4GB 的空間,但實際上它們運行時真正能用到的空間根本沒有那么多。內存管理器只是分給進程了一片“假地址”,或者說是“虛擬地址”,讓進程們“認為”這些“虛擬地址”都是可以訪問的。如果進程不使用這些“虛擬地址”,它們對進程來說就只是一筆“無形的數字財富”;當需要進行實際的內存操作時,內存管理器才會把“虛 擬地址”和“物理地址”聯系起來
2、而為什么是4GB的虛擬內存呢?
這里我給出計算虛擬內存的方法
我們日常的操作系統是32位的,這個4GB的虛擬內存就是32位操作系統的特性。
32位最小的地址是0000 0000 最大是 FFFF FFFF 這里是十六進制表示的!!

可以看到十進制是4,294,967,295 加上0000 0000這個地址,總共就是4294967296
而4294967296 代表的是可以存的Byte字節。1024B=1KB
1024KB=1M 1024M=1G
根據這個我們可以算出結果位4GB。所以這不是定義的,而是算出來的。
3、高2G和低2G
在內存中7FFFF FFFF之前的我們稱之為低2G也就是所謂的ring3用戶態
而從8000 0000開始就是所謂的ring0內核級了,普通調試器例如Ollydbg是無法對ring0進行調試的。
而ring0就保存了處理機的現場信息,操作系統內核代碼、設備驅動程序、設備I/O高速緩存、非頁面內存池的分配、頁表等一切后面的章節所講的知識。
(筆者對於之前的自大有點羞愧,之前認為本科教材很簡單,思想是有點不准確的)
4、進程調度(原書3.3)
下面就是完完全全的ring0級別的了,是普通程序員在開發時完全體會不到的,因為無論是windows編程還是Java,Linux編程那些都是應用層面的開發。ring0級別的是完全透明的無法感知的。
因為我這次是以我最熟悉的windows的平台進行的講解。而windows屬於多用戶多任務型操作系統,進程調度相對復雜,而微軟的封閉性導致我學習的困難,因此我對進程調度的學習還是以書上的相對簡單的實現方式進行講解。(這本書關於算法講的也太簡單了。連個數據結構都沒有,差評。)
(0)PCB進程控制塊的介紹:
(1)進程調度的任務(原書3.3.1)
1.保存處理機的現場信息。—這個比較好理解,把各種結構體壓進系統堆棧(push)中即可實現保存。
2.按照某種算法選取進程。—-筆者認為這里是個坑。因為沒有數據結構哪來的算法,所以還是往后再深究。
3.把處理器分配給進程。—-筆者也不知道如何分配的,書上也沒有具體介紹,姑且理解為彈棧(pop)的過程把。
三、進程調度算法
在這里,我主要介紹倆種進程調度算法的實現、輪轉法和優先級調度算法。
一、輪轉法:
一.輪轉法的基本原理:
根據先來先服務的原則,將需要執行的所有進程按照到達時間的大小排成一個升序的序列,每次都給一個進程同樣大小的時間片,在這個時間片內如果進程執行結束了,那么把進程從進程隊列中刪去,如果進程沒有結束,那么把該進程停止然后改為等待狀態,放到進程隊列的尾部,直到所有的進程都已執行完畢
二.進程的切換
時間片夠用:意思就是在該時間片內,進程可以運行至結束,進程運行結束之后,將進程從進程隊列中刪除,然后啟動新的時間片
時間片不夠用:意思是在該時間片內,進程只能完成它的一部分任務,在時間片用完之后,將進程的狀態改為等待狀態,將進程放到進程隊列的尾部,等待cpu的調用
三.關於時間片大小的選擇
時間片過小,則進程頻繁切換,會造成cpu資源的浪費
時間片過大,則輪轉調度算法就退化成了先來先服務算法
二、優先級調度算法:
一、優先級調度算法的基本原理:
優先級進程調度算法,是把處理機分配給就緒隊列種優先級最高的進程。即根據優先級來確定排名的算法。
二、根據新的更高優先級進程能否搶占正在執行的進程,可將該調度算法分為:
- 非剝奪式優先級調度算法。當某一個進程正在處理機上運行時,即使有某個更為重要或緊迫的進程進入就緒隊列,仍然讓正在運行的進程繼續運行,直到由於其自身的原因而主動讓出處理機時(任務完成或等待事件),才把處理機分配給更為重要或緊迫的進程。
- 剝奪式優先級調度算法。當一個進程正在處理機上運行時,若有某個更為重要或緊迫的進程進入就緒隊列,則立即暫停正在運行的進程,將處理機分配給更重要或緊迫的進程。
三、根據進程創建后其優先級是否可以改變,可以將進程優先級分為以下兩種:
- 靜態優先級。優先級是在創建進程時確定的,且在進程的整個運行期間保持不變。確定靜態優先級的主要依據有進程類型、進程對資源的要求、用戶要求。
- 動態優先級。在進程運行過程中,根據進程情況的變化動態調整優先級。動態調整優先級的主要依據為進程占有CPU時間的長短、就緒進程等待CPU時間的長短。
下面就是筆者使用Java語言對倆種調度算法的實現:
package process; import java.util.Collections; import java.util.Comparator; import java.util.LinkedList; import java.util.List; import java.util.PriorityQueue; import java.util.Queue; import java.util.Scanner; public class ProcessHandling { /** * @author godoforange * @PCB數據結構 */ class PCB { PCB(int id, int priority, int timeuse, int timeround) { this.id = id; this.priority = priority; this.timeuse = timeuse; this.timeround = timeround; } int id = 0;// 進程id int priority = 0;// 優先級 int timeuse = 0;// 需要的運行的時間為 int timeround = 0;// 輪轉時間片數為 int cpuuse = 0; @Override public String toString() { return "[+]進程ID:" + id + " 優先級為:" + priority + " 需要運行的時間:" + timeuse + " 輪轉時間片數為:" + timeround + " 已經占用CPU:" + cpuuse; } } /** * @author godoforange * @優先級比較器 */ class PriorityComparator implements Comparator<PCB> { @Override public int compare(PCB o1, PCB o2) { if (o1.priority < o2.priority) { return 1; } else if (o1.priority > o2.priority) { return -1; } else { return 0; } } } /** * @author godoforange * @param N 總數 * @return 包含了PCB的鏈表 */ public List<PCB> createProcess(int N) { System.out.println("[+]創建進程中。。。"); List<PCB> allPCB = new LinkedList<>(); for (int i = 0; i < N; i++) { int p = (int) (1 + Math.random() * 10); int c = (int) (1 + Math.random() * 10); int b = (int) (1 + Math.random() * 10); PCB pcb = new PCB(i, p, c, b); System.out.println("[+]創建第" + i + "個進程,優先級為:" + p + " 需要運行的時間為:" + c + "輪轉時間片數為:" + b); allPCB.add(pcb); } return allPCB; } /** * @author godoforange * @輪轉法 * @param N 進程數 */ public void round(int N) { List<PCB> allPCB = createProcess(N); System.out.println("[+]輪轉法調度執行中"); System.out.println("[+]創建隊列"); Queue<PCB> que = new LinkedList<>(); for(PCB pcb : allPCB) { que.offer(pcb); } System.out.println("[+]就緒隊列創建完畢"); while(!que.isEmpty()) { PCB pcb = que.element(); pcb.timeuse--; pcb.cpuuse++; try { Thread.sleep(500); } catch (InterruptedException e) { } System.out.println("[+]"+pcb.id + "運行了"+pcb.cpuuse+"次"+"輪轉時間片數為"+pcb.timeround); if (pcb.timeuse < 1) { System.out.println("[+]"+pcb.id + "運行時間到被移除"); que.remove(); }else if(pcb.cpuuse%pcb.timeround==0) { que.offer(que.remove()); } } } /** * @author godoforange * @優先權法 * @param N 進程數 */ public void priority(int N) { List<PCB> allPCB = createProcess(N); Collections.sort(allPCB, new PriorityComparator()); System.out.println("[+]優先權調度執行中"); System.out.println("[+]創建隊列:"); for (PCB pcb : allPCB) { System.out.println(pcb.toString()); } System.out.println("[+]就緒隊列創建完畢"); while (!allPCB.isEmpty()) { PCB pcb = allPCB.get(0); pcb.priority -= 3; pcb.timeuse--; pcb.cpuuse++; try { Thread.sleep(500); } catch (InterruptedException e) { } System.out.println("[+]"+pcb.id + "運行了"+pcb.cpuuse+"次"+"優先級為"+pcb.priority); if (pcb.timeuse < 1) { System.out.println("[+]進程"+pcb.id + "運行時間到被移除"); allPCB.remove(pcb); } Collections.sort(allPCB, new PriorityComparator()); } System.out.println("[-]優先級調度結束"); } public static void main(String[] args) { System.out.print("[+]請輸入要創建的進程數:"); int N;// 進程數 Scanner sc = new Scanner(System.in); N = sc.nextInt(); System.out.println("[+]需要創建:" +N+ "個進程"); System.out.print("[+]輸入 分別執行優先級調度和輪轉法調度 [Y/N]:"); if(sc.nextLine().equals("Y")||sc.nextLine().equals("y")) { new ProcessHandling().priority(N); }else{ new ProcessHandling().round(N); } sc.close(); } }
運行結果如下:

四、總結
實話來說、這本書其實並不適合讓大家真正去理解何為操作系統,因為這里面涉及到的知識很多很多,很多語言十分生澀,即便是有多年經驗的開發者也難以對其進行總結。還是希望本科教育能夠重視起來,真正的讓學生去理解何為操作系統,而非只是生澀的去介紹這些知識。
