作業系統學習筆記(二)
一,作業系統介面interface
作業系統向用戶提供了“使用者與作業系統的介面”,以支援使用者和作業系統之間進行互動。使用者與作業系統的介面通常是由“命令”和“系統呼叫”的形式表現出來的。介面起到連線兩個層次,實現訊號轉換,遮蔽資訊細節的作用。
如此一來便實現了作業系統的使用,這些重要的函式由作業系統提供,介面表現為函式呼叫,又稱為系統呼叫。系統呼叫就是作業系統介面。比如以下函式都是作業系統介面:
作業系統的介面函式有很多,沒必要都記住,但是要學會怎麼去查詢。
遇到需要就去檢視POSIX手冊即可。官網:https://pubs.opengroup.org/onlinepubs/7908799/index.html
二,核心態和使用者態
計算機提供了一種硬體機制,將記憶體分為兩段不同的區域——核心段(即計算機載入完作業系統之後將作業系統從硬碟中轉移到記憶體0地址開始的那一段)和使用者段,核心態的資料在這時DPL為0,若當前的呼叫CPL<DPL,才可以成功調取核心段中的資料。這就阻止了使用者程式胡亂呼叫作業系統中十分重要的資料,因為使用者程式使用呼叫機制時CPL為3,是不符合CPL<DPL的。
CPL是當前程序的許可權級別(CurrentPrivilegeLevel),是當前正在執行的程式碼所在的段的特權級,存在於cs暫存器的低兩位。
RPL說明的是程序對段訪問的請求許可權(RequestPrivilegeLevel),是對於段選擇子而言的,每個段選擇子有自己的RPL,它說明的是程序對段訪問的請求許可權,有點像函式引數。而且RPL對每個段來說不是固定的,兩次訪問同一段時的RPL可以不同。RPL可能會削弱CPL的作用,例如當前CPL=0的程序要訪問一個數據段,它把段選擇符中的RPL設為3,這樣雖然它對該段仍然只有特權為3的訪問許可權。
DPL儲存在段描述符中,規定訪問該段的許可權級別(DescriptorPrivilegeLevel),每個段的DPL固定。當程序訪問一個段時,需要程序特權級檢查,一般要求DPL>=max{CPL,RPL}
三,中斷是進入核心的唯一方法
那麼如果使用者程式是如何通過系統呼叫實現使用者段和核心段之間的聯絡的呢?
以使用者態的printf為例,首先由庫函式將printf轉換為帶有中斷指令的程式碼。呼叫中斷時,將DPL變為3,進入核心態之後,CPL變成了0,這時CPL<DPL,便呼叫system_call,由system_call呼叫call_table去查表,查到相對應的系統呼叫號,根據這個呼叫號才去執行真正要去執行相對應的系統呼叫,pringtf為例是由呼叫號4最終去呼叫了sys_write。
四,作業系統核心的兩點在於多程序管理和檔案管理
1)多程序管理
如果在cpu在工作時單道程式執行,由於外設的速度遠遠低於cpu的工作速度,這樣會使cpu的效率大大降低,使其在大部分是時間都在等外外設準備好檔案資料。這裡參考程式查詢方式。如果在一道程式之中外設還沒準備好的時候cpu切換出去執行其他的程式,然後在切換出去執行的這一道程式又出現要等待外設準備資料的情況時再進行cpu的工作切換。這樣就實現了多道程式交替執行,大大提高了cpu的工作效率。
像這樣一個CPU上交替執行多個程式稱為併發。那麼怎麼做到併發呢?由於cpu是取指執行的,而這一行動靠的是PC指標,由它從記憶體中取指令和資料。所以併發的關鍵是“切換PC指標從而跳轉執行其它程式。”。但是由於只是暫時跳開執行其它的程式而提高cpu效率,而不是不再執行原有的程式,所以要保護現場,也就是要能夠保留切換出去之前的程式原來的樣子,包括pc和相應的暫存器。完成“保護現場”這一工作的是程序控制塊PCB(process control block),以記錄每一個執行中的程式的狀態。
PCB:為了描述控制程序的執行,系統中存放程序的管理和控制資訊的資料結構稱為程序控制塊PCB (Process Control Block),它是程序實體的一部分,是作業系統中最重要的記錄性資料結構。它是程序管理和控制的最重要的資料結構,每一個程序均有一個PCB,在建立程序時,建立PCB,伴隨程序執行的全過程,直到程序撤消而撤消。
作業系統的開啟初始化過程中執行到main程式時有一段fork()指令如下,完成了桌面的啟動:
所以main是0號程序,init是1號程序。
1號程序init()一直在執行。
fork():在fork函式執行完畢後,如果建立新程序失敗,則返回-1;如果建立新程序成功,則出現兩個程序,一個是子程序,一個是父程序。在子程序中,fork函式返回0,在父程序中,fork返回新建立子程序的程序ID。我們可以通過fork返回的值來判斷當前程序是子程序還是父程序。
程序的狀態有五種:
正在執行的程式遇到突發事件比如錯誤,或者需要等待外設時就要切換當前程序到阻塞態,然後呼叫schedule()函式完成程序替代。Schedule()函式最主要作用就是從就緒程序中選擇一個優先順序最高的程序來代替當前程序執行。
getnext()是排程函式,涉及到優先順序的設計,比如FIFO策略,除此之外還有很多的複雜演算法。
比如:pCur程序需要等待外設準備資料,就將其轉換成等待狀態,然後呼叫schedule()。
2)檔案管理