淺談操作系統與內存


淺談操作系統與內存

對於計算機的發明,相信大家都有耳聞那個占地面積按平米算的第一台計算機。在那個時候,CPU的資源是極其珍貴的,隨着這些年突飛猛進的發展,一片指甲蓋大小的民用級CPU一秒鍾能執行的指令數可以達到上億級別。

隨着計算能力的增長,芯片外圍的硬件和配套的軟件也是一路高歌,發生了天翻地覆的變化,今天我們簡單回顧歷史,來看一看操作系統和內存機制的演變,不僅要了解它們是怎樣,同時也看看它們為什么會是這樣。


CPU的運行

一說到CPU(Center processing unit),大家都覺得這是非常了不起的東西,我們的手機電腦都是由它進行核心控制,擁有掌管一切的能力,但是,它真的有傳說中那么聰明么?

事實並非如此,CPU唯一的能力其實就是處理二進制數據,CPU的組成是這樣的:

  • CPU有三種總線:控制總線,地址總線,數據總線,這些總線統稱為系統總線,主要用來與外設交換數據。
  • 內部寄存器若干,一般只有幾十個字節,普通寄存器負責傳輸數據,特殊寄存器如PC,SP,LD則控制程序流程。
  • ALU-算數邏輯運算單元,既然是處理數據,當然也就依賴運算單元,這些運算單元只能處理加減乘法,其實嚴格來說,就是處理加法,減和乘是在加法基礎上實現的。

這三個就是早期CPU的主要部件了,那它是怎么處理數據的呢?

系統總線負責與外部的數據交換,將交換的數據暫時放在寄存器中,然后CPU再從寄存器中獲取數據使用算數邏輯運算單元進行運算,必要時將數據寫回。

是的,僅此而已,CPU不能直接播放音樂,它也不能生成游戲界面,只是孜孜不倦地拿數據,處理數據,寫回數據,像個一刻也不停的流水線工人。


系統的存儲

上文中我們提到CPU的內部寄存器,因為是集成在CPU內部,所以速度非常快,但是同時也因為集成度的問題,CPU中一般只有幾十字節的寄存器,這對於數據處理是遠遠不夠的,所以當運行時存儲空間不夠的時候我們必須有另一個地方進行存儲,這就是系統內存。

即使都是數據,也分別有不同的屬性,比如是否需要掉電保存,數據處理的速率要求。

在程序執行時,需要在存儲設備中保存一些運行參數,這部分存儲數據的速率直接決定了程序運行速率,所以對這部分存儲的要求是速度快,同時因為是存儲運行時參數,所以不需要掉電保存。

在程序執行之外,需要保存大量的靜態數據,比如用戶數據,文件,這部分數據需要掉電能夠保存,且要求可存儲數據量大,由於訪問並不頻繁,所以對速率要求可以降低以節省成本。

由此,存儲設備分化出了ram和rom兩大類,ram有速率快,掉電不保存的特性,而rom是速率慢,存儲量大,掉電保存。

ram和rom只是分別對應易失性存儲器和非易失性存儲器的統稱,事實上ram有sram,sdram等等,而rom有eeprom,flash,硬盤等等。


微控制器和單板機

上面說到CPU的作用,可能你開始有疑問了,既然CPU只能處理簡單的數據,那么它是怎么處理復雜軟件的運行的呢?

這就是很多人的一個常見誤區,將CPU和MCU,還有單板機搞混了。

MCU(Microcontroller unit):微控制器,又被稱為單片微型計算機,常被直接稱為單片機。它通常集成了CPU,rom,ram以及硬件控制器、外圍電路等等,ram和rom的容量有限,接口電路也有限。

單板機:單板機是把微型計算機的整個功能體系電路(CPU、ROM、RAM、輸入/輸出接口電路以及其他輔助電路)全部組裝在一塊印制電板上,再用印制電路將各個功能芯片連接起來,一般來說資源比如主頻、ram&rom容量要比MCU高出一大截。

事實上,在我們常見的手機電腦中,用戶常說的CPU,事實上指的是單板機。而在嵌入式設備上,經常會使用到單片機。

常見的第二個誤區就是:認為整個單板機(或者MCU)上只有CPU是可編程元件,其他部分都是硬件搭建起來的。

事實上,除了CPU,像各種硬件控制器比如gpio、i2c、dma或者硬盤控制器,這些都是可編程器件,只是這些控制器所扮演的都是一個從機角色,而主控是CPU。

CPU通過系統總線與各個控制器之間進行數據交互,通過特定的預定義的指令來控制控制器的行為:

比如控制gpio的操作,將某個引腳的電平從0設置成1,這樣再配合上繼電器等硬件上的電路,就可以實現220V電路的控制。

再者是通過控制gpio以通過預定義的控制協議與連接在另一端的設備進行通信。

又或者是告訴硬盤控制器,CPU需要訪問某些數據,硬盤控制器將數據傳遞給CPU。

CPU就是這樣通過將指令一級級地將數據傳遞給外設,通過在外設處理器中預定義了一些指令字段,外設接收CPU的數據相當於接收到控制指令,以此實現外設的控制。

而CPU本身則只是孜孜不倦地處理數據,反饋數據。


單片機到操作系統

在早期的CPU上,CPU都是順序執行,一個CPU只運行一個程序,這樣造成的問題就是:當程序在等待某個資源或者讀寫磁盤的時候,CPU就處於空閑狀態,這對CPU來說是非常浪費的。由於CPU的資源非常珍貴,人們不得不想辦法解決這個問題,所以就有人編寫多任務程序:

一個CPU中可以存在多個任務,一個時刻只允許一個任務運行,當檢測程序檢測到某個任務處於空閑狀態,就切換下一個任務運行。或者是當前任務主動放棄運行權,切換下一個任務運行。

這就是操作系統的原型,檢測程序一般被稱為調度器,遵循某種調度算法。


任務調度算法

調度算法又常被稱為調度策略,目前的操作系統常用的調度策略有兩種:

  • 基於優先級的調度,優先級高的任務總是有權利搶到CPU的使用權,高優先級的任務通過主動睡眠和被動睡眠讓出CPU使用權(如等待信號量,等待鎖)
  • 時間片機制,每個任務分配時間片,一般是ms級別的時間,當時間片用完就切換到下一個任務,營造一種所有任務同時運行的假象。

事實上,操作系統往往在基於上述調度算法的基礎上,運行着更復雜的調度算法,比如以優先級為權重分配時間片,比如任務分組,不同的分組有不同的調度策略等等....


任務調度的執行原理

上文說到,操作系統的的最早雛形就是允許CPU中存在多個任務,通過某種調度算法來使每個任務交替運行。

那么,CPU到底是怎么做到這件事的呢?

首先我們要了解以下概念:時鍾、中斷,程序執行流的切換。

  • 時鍾:是CPU上最重要的部分之一,任何一個CPU都必須有時鍾部分,它提供了CPU上時間的概念,系統不同部分之間的交互和同步都要靠時鍾信號來進行同步。通俗來說,就像人類定義了時間的概念,我們才可以統一做到准時上下班,相約同一時間去做某事。CPU也是一樣,在進行數據傳輸時,通信雙方必須統一傳輸單元數據的時間,才能進行同步和數據解析。
  • 中斷:即使是目前非常簡單的單片機,也提供中斷功能。我們可以將中斷理解為CPU中的突發事件需要緊急處理,這時CPU暫停處理原本任務轉去處理中斷,處理完中斷之后再回頭來處理原先的任務。
  • 程序執行流的切換:CPU中一般會有多個寄存器,分為通用寄存器和特殊寄存器,通用寄存器用來存取CPU處理的數據,而特殊寄存器中需要提及的就是PC指針(程序計數器),SP(棧指針),PC指針中存儲着下一條將要執行的指令地址,而SP指針則指向棧地址。
    在正常情況下,程序順序運行,PC指針也是按順序裝載需要執行程序的地址,CPU每次執行一條指令都會從PC指針讀取程序執行地址,然后從執行地址上取到數據,執行相應動作,而棧則保存着函數調用過程中的信息(因為調用完需要返回到調用前狀態)。

在發生中斷的時候,因為需要跳轉去執行中斷服務函數,肯定需要更改程序執行流,在單片機上,CPU將會去查中斷向量表,找到需要執行的函數地址裝載到PC指針處,然后保存一些當前運行參數,比如寄存器數據,棧數據,下一個指令周期將會跳轉執行中斷服務函數。

既然程序的執行流可以由程序員自由控制,只要控制PC指針指向就可以,那么我們也可以預先定義兩個任務,當一個任務空閑時,直接跳轉執行到另一個任務,每個任務的運行參數比如寄存器數據,棧數據,使用的資源等等進行保存,在切換到某個任務時,只要將保存的信息恢復到原來的狀態就可以繼續運行任務,這種操作方式當然可以從兩個延伸到多個,這就是操作系統的模型。

通常,保存信息的部分一般使用結構體鏈表,一般被稱為任務控制塊(每個操作系統的叫法不一樣,但實現的思想是一樣的),每個線程都有獨立的棧空間以保存每個線程的運行時狀態。

那么,我們怎么知道什么時候執行另一個任務呢?這就需要借助時鍾中斷提供一個時間的概念,任務的運行時間就以這個時鍾中斷作為度量,具體的做法就是每隔一個周期(通常非常短,1ms、10ms等)產生一次中斷,在中斷程序中檢查是否需要切換任務運行,而切換的原因就比較多了,比如運行時間到了、等待某項資源自主休眠等等。

事實上,到這里,你已經了解了嵌入式實時系統的運行原理了,比如ucos,freeRTOS之類的,建議去看看相關源代碼,尤其是ucos,代碼量少且易懂,麻雀雖小五臟俱全。


嵌入式實時操作系統的內存限制

在早期的單片機上,程序運行在物理內存中,也就是說,程序在運行時直接訪問到物理地址,在程序運行開始,將全部程序加載到內存中,所有的數據地址和程序地址就此固定。

在運行多任務系統時,比較直接的辦法也是直接為每個任務分配各自需要的內存空間,比如總內存為100M,task1需要40M,task2需要50M,task3需要20M,那么最簡單的辦法是給task1分配40M,給task2分配50M,而task3,不好意思,內存不夠了,不允許運行,這樣簡單的分配方式有以下問題:

  • 地址空間不隔離 試想一下,所有程序都訪問物理地址,那么就存在task1可以直接訪問到task2任務內存空間的情況,這就給了一些惡意程序機會來進行一些非法操作,即使是非惡意但是有bug的程序,也可能會導致其他任務無法運行,這對於需要安全穩定的的計算機環境的用戶是不能容忍的。
  • 內存使用效率極低 由於沒有有效的內存管理機制,通常在一個程序執行時,將整個程序裝載進內存中運行,如果我們要運行task3,內存已經不夠了,其實系統完全可以將暫時沒有運行的task2先裝入磁盤中,將task3裝載到內存,當需要運行task2時,再將task2換回,雖然犧牲了一些執行效率,但總歸是可以支持更多程序運行。
  • 程序地址空間不確定 需要了解的是程序在編譯階段生成的可執行文件中符號地址是連續且確定的,即使實現了內存數據與磁盤的交換,將暫時不需要的任務放入磁盤而運行其他任務,程序每次裝入內存時,都需要從內存中分配一段連續內存,這個空間是不確定的,但是在程序實現過程中,有些數據往往要求其地址空間是確定的,這就會給編寫程序帶來很大的麻煩。

虛擬內存機制

那么怎么解決這個問題呢?曾經有人說過一句名言:

計算機科學領域的任何問題都可以通過增加一個間接的中間層來解決。  

在這里這個道理同樣適用,后來的操作系統設計者設計了一種新的模型,在內存中增加一層虛擬地址。

面對開發者而言,程序中使用的地址都是虛擬地址,這樣,只要我們能妥善處理好虛擬地址到物理地址的映射過程,而這個映射過程被當作獨立的一部分存在,就可以解決上述提到的三個問題:

  • 首先,對於地址隔離,因為程序員操作的是虛擬地址,虛擬地址在最后會轉化為物理地址,可能程序A訪問的0x8000,轉換到物理地址是0x3000,而程序B同樣訪問0x8000,但是會被轉換到物理地址0x4000,這樣程序中並不知道自己操作的物理地址,也操作不到實際的物理地址,因為虛擬地址的轉換總會將地址映射到不沖突的物理地址上,同時進行檢測,也就無從影響到別的任務執行。
  • 對於內存使用率來說 建立完善的內存管理機制,可以更方便地進行內存與磁盤數據的交換,將空置的內存利用起來而不增加編程者的負擔。
  • 對於程序空間不確定 虛擬內存機制完美地解決了這個問題,在直接操作物理地址時,因為內存與磁盤的交換,可能導致函數或者變量地址的變化,原來程序中的數據必須進行更新。
    而引入虛擬內存機制,程序員只操作虛擬地址,函數、變量虛擬地址不變,物理地址可能變化,但是程序員只需要關注虛擬地址就行了,虛擬地址到物理地址的轉換就由內存管理部分負責。

既然增加了一個中間層,那么這個中間層最好是由獨立的部分進行管理,實現這個功能的器件就是MMU,它接管了程序中虛擬地址和物理地址的轉換,MMU一般直接集成在CPU中,不會以獨立的器件存在。


內存分段分頁機制

在經典32位桌面操作系統中,有32條地址線(特殊情況下可能36條),那么CPU可直接尋址到的內存空間為2^32字節,也就是4GB,雖說內存尋址可以到4G,但是常常在單板機上 並不會有這么大的物理內存。

根據實際情況而不同,可能是512M或者更少,但是由於虛擬內存機制的存在,程序看起來可操作的內存就是4GB,因為MMU總會找到與程序中的虛擬地址相對應的物理地址,在內存不夠用時,它就會征用硬盤中的空間,在linux下安裝系統時會讓你分出一片swap分區,顧名思義,swap分區就是用來內存交換的。

那4GB這么大的內存,如果不進行組織,在CPU讀寫數據時將會是一場災難,因為要找到一個數據在最壞的情況下需要遍歷整個內存。

就像一個部隊,總要按照軍、師、旅、團、營、連、排、班來划分,如果全由總司令管理所有人,那也會是一場災難。

因此,對於內存而言,衍生了分段和分頁機制,根據功能划分段,然后再細分成頁,一般一頁是4K,當然,這其中會有根據不同業務的差別做一些特別的定制。

它的問題就是即使是只存儲一個字節,也要用掉一頁的內存,造成一定的浪費。

但是如果將分頁粒度定得過細,將會造成訪問成本的增加,因為在很多時候,進行訪問都是直接使用輪詢機制。而且,就像每本書都有目錄和前言,段和頁的信息都要在系統中進行記錄,分頁更細則代表頁數更多,這部分的開銷也就更多。這也是一種浪費。

就像程序中時間與空間的拉鋸戰,計算機中充滿了妥協。


單片機與單板機程序執行的不同

處理器是否攜帶MMU幾乎完全成了划分單片機和單板機的分界線。

帶MMU的處理器直接運行桌面系統,如linux、windows之類的,與不帶MMU的單片機相比,體現在用戶眼前的最大區別就是進程的概念,一個進程就是一個程序的運行實體,使得桌面系統中可以同時運行多個程序,而每個程序由於虛擬機制的存在,看起來是獨占了整個內存空間。

而不帶MMU的處理器,一般是嵌入式設備,程序直接在物理地址上運行,支持多線程。

從程序的加載執行來說,桌面系統中程序編譯完成之后存儲在文件系統中,在程序被調用執行時由加載器加載到內存中執行。而在單片機中,程序編譯生成的可執行文件一般是直接下載到片上flash(rom的一種)中。

32位單片機的尋址范圍為4G,由於不支持虛擬內存技術,一般在總線上連接的ram不會太大,所以可以直接將rom也掛載在總線上,CPU可以直接通過總線訪問flash上的數據,所以程序可以直接在片上flash中執行,在運行時只是將數據部分加載到ram中運行。


無系統單線程,嵌入式實時操作系統和桌面操作系統

可以在單板機上運行桌面操作系統,是不是運行單線程的單片機和嵌入式實時操作系統就可以拋棄了呢?

從功能實現上來說,單板機確實比單片機強很多,但是資源的增加同時帶來成本的增加,所以在一些成本敏感的應用場合下反而是單片機的天下。

而對於操作系統而言,嵌入式實時操作系統由於沒有一些臃腫的系統服務,有時反而有着比桌面系統更好的實時性,在實時性要求高的場合更是風生水起,比如無人機、車載系統。而且運行在單片機上,成本上更有優勢。

對於嵌入式開發而言,很有必要了解這三種程序運行方式:無系統單線程,嵌入式實時操作系統,桌面操作系統。

  • 無系統單線程很好理解,程序順序執行,除了發生中斷之后跳轉執行中斷服務函數,一般不會再發生程序執行流的切換,程序直接操作物理地址,常用於嵌入式小型設備,如燈、插座開關等等。
  • 嵌入式實時操作系統:支持多線程的並發執行,多任務循環執行,遵循一定的調度算法,程序直接操作物理地址。例如ucos、free rtos,通常也在移植在資源相對較多的單片機上。
  • 桌面操作系統:CPU自帶MMU,所以支持虛擬內存機制,支持多進程和多線程編程,程序操作虛擬地址,在人機交互上有很好的表現。例如windows,linux,常用的手機、ipad、服務器、個人電腦。

在單片機上,無系統單線程模式可以很簡單地切換到嵌入式實時操作系統,進行相應的移植即可。

但是是否能在其上運行桌面操作系統,是否集成MMU是一個決定性的因素。


好了,關於操作系統和內存的討論就到此為止啦,如果朋友們對於這個有什么疑問或者發現有文章中有什么錯誤,歡迎留言

原創博客,轉載請注明出處!

祝各位早日實現項目叢中過,bug不沾身.
(完)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM