本文主要和大家講述linux作業系統原理,這是一篇非常好的linux系統基礎教程,我們總結了相關的全部精選內容,一起來學習下。希望能幫助大家。
一.電腦經歷的四個時代
#1.第一代:
#真空管計算機,輸入與輸出:穿孔卡片,對電腦操作起來非常不便,做一件事可能需要十幾個人去共同去完成,年份大概是:1945-1955。而且耗電量特別大,如果那時候你家裡有台計算機的話,可能你一開計算機你家的電燈泡亮度就會變暗,哈哈~
2.第二代:
電晶體計算機,批次(串列模式運作)系統出現。相比第一台省電多了。典型代表是Mainframe。年份大概是:1955-1965。在那個年代:Fortran語言也就誕生啦~一門非常古老的電腦語言。
3.第三代:
積體電路出現,多道處理程序(平行模式運作)設計,比較典型的代表就是:分時系統(把CPU的運算分成了時間片)。年份大概是:1965-1980年左右。
4.第四代:
PC機出現,大概是從:1980年左右。相信這個時代典型人物代表:比爾蓋茨,賈伯斯。
二.電腦的工作體系
雖然說電腦經過了四個時代的演變,但是到今天為止,電腦的工作體系還是比較簡單的。一般而言,我們的計算機有五大基本零件。
1.MMU(記憶體控制單元,實現記憶體分頁【memory page】)
#運算機制被獨立在CPU(運算控制單元)上,在CPU當中有一個獨特的晶片叫MMU。他是用來計算進程的線線位址和物理位址的對應關係的。它也用於存取保護的,即一個進程先要訪問到不是它的記憶體位址,是會被拒絕的!
2.記憶體(memory)
3.顯示裝置(VGA接口,顯示器等等)【屬於IO設備】
4.輸入裝置(keyboard,鍵盤設備)【屬於IO設備】
5.硬碟設備(Hard dish control ,硬碟控制器或適配器)【屬於IO設備】
擴充小知識:
#
#這些硬體設備在一條總線上鏈接,他們透過這條線進行數據交互,裡面的帶頭大哥就是CPU,擁有最高指揮權。那麼它是如何運作的呢? A.取指單元(從記憶體取得指令);B.解碼單元(完成解碼[講記憶體中取到的資料轉換成CPU真正能運行的指令]) ;C.執行單元(開始執行指令,根據指令的需求去呼叫不同的硬體去幹活。);我們透過上面知道了MMU是CPU的一部分,但是CPU有還要其他的部件嗎?當然是有的啦,例如指令暫存器晶片,指令計數器晶片,堆疊指標。 指令暫存器晶片:就是CPU用來將記憶體中的資料取出來存放的地方;指令計數器晶片:就是CPU為了記錄上一次在記憶體中取資料的位置,方便下一次取值;堆疊指標:CPU每次取完指令後,就會把堆疊指標指向下一個指令在記憶體中的位置。 他們的工作週期和CPU是一樣快的速度,跟CPU的工作頻率是在同一個時脈週期下,因此他的效能是非常好的,在CPU內部匯流排上完成資料通訊。指令暫存器晶片,指令計數器晶片,堆疊指標。這些設備通常都被叫做CPU的暫存器。 暫存器其實就是用來保存現場的。尤其是在時間多路復用尤為明顯。比如說CPU要被多個程式共享使用的時候,CPU常常會終止或掛起一個進程,作業系統必須要把它當時的運作狀態給保存起來(方便CPU一會回來處理它的時候可以繼續接著上次的狀態工作。 三.電腦的儲存體系。 1.對稱多處理器SMPCPU裡面除了有MMU和暫存器(接近cpu的工作週期)等等,還有cpu核心,正是專門處理資料的,一顆CPU有多個核心,可以用來並行跑你的程式碼。工業上很多公司採用多顆CPU,這種結構我們稱為對稱多處理器。 2.程式局部性原理空間局部性:程式是由指令和資料組成的。空間局部性指的是一個資料被存取到之後,那麼離這個資料很近的其他資料隨後也可能會被存取。 時間局部性:一般而言當一個程式執行完畢後,可能很快就會被存取。數據也是同樣的原理,一個數據的被訪問到,很可能會再次訪問。 正是因為程式局部性的存在,所以使得無論是在空間局部性或時間的局部性的角度來考慮,一般而言我們都需要對資料做快取。 ###擴充小知識:
由於CPU內部的暫存器儲存的空間有限,於是就用了記憶體來儲存數據,但是由於CPU和速度和記憶體的速度完全不在一個檔次上,因此在處理的數據的時候回到多數都在等待(CPU要在記憶體中取一個數據,cpu轉一圈的時間就可以處理完,記憶體可能需要轉20圈)。為了解決使得效率更加提高,就出現了快取這個概念。
既然我們知道了程式的局部性原理,有知道了CPU為了獲得更多的空間其實就是用時間去換空間,但是緩存就是可以直接讓cpu拿到數據,節省了時間,所以說緩存就是用空間去換時間
3.就算進儲存體系
工作時間就的朋友可能看過磁帶機,現在基本上都被OUT了,企業很多都用機硬碟來取代磁帶機了,所以我們這裡就從我們最熟悉的家用電腦的結構來說,存下到上一次儲存資料是不一樣。我們可以簡單舉個例子,他們的周儲存週期是有很大的差距。特別明顯的是機械硬碟和內存,他們兩個存取熟讀差距是相當大的。
擴充小知識:
比起自己家用的桌上型電腦或筆記本可能自己拆開過,講過機械式硬碟,固態硬碟或記憶體等等。但是可能你沒看過快取實體設備,其實他是在CPU上的。因此我們對它的了解可能會有些盲點。
先說說一級快取和耳機快取吧,他們的CPU在這裡面取資料的時候時間週期基本上查不了多少,因為一級快取和二級快取都在CPU核心內部資源。 (在其他硬體條件相同的情況下。一級快取128k可能市價會買到300元左右,、一級快取256k可能會買到600元左右,一級快取512k可能市價就得過四位數這個具體價格可以參考京東啊。其實三級快取就是多顆CPU共享的空間。當然多顆cpu也是共享記憶體的。
或是記憶體的時候,他們就會出現了一個問題,就是資源徵用。我們知道變數或是字串在記憶體中被保存是有記憶體位址的。他們是如何去領用記憶體位址呢?我們可以參考下圖:
沒錯,這些玩硬體的大牛們將三級快取分割,分別讓不同的CPU佔用不同的記憶體位址,這樣我們可以理解他們都有自己的三級快取區域,不會有資源搶奪的問題,但是要注意的是他們還是同一塊三級快取。就好像北京市有朝陽區,豐台區,大興區,海淀區等等,但是他們都是北京的所屬地。我們可以這裡理解。這就是NUMA,他的特性就是:非一致性記憶體訪問,都有自己的記憶體空間。
擴展小知識:
那麼問題來了,基於重新負載的結果,如果cpu1運行的進程被掛起,其位址在他自己的它的快取位址是有記錄的,但是當cpu2再次運行這個程式的時候被CPU2拿到的它是如何處理的呢? 這就沒辦法了,只能從CPU1的三級換粗區域複製一份地址過來一份或是移動過來一份讓CPU2來處理,這時候是需要一定時間的。所以說重新負載平衡會導致CPU效能降低。這時候我們就可以用進程綁定來實現,讓再次處理該進程的時候還是用之前處理的CPU來處理。即進程的CPU的親緣性。 5.快取中的通寫和回寫機制。CPU在處理資料的地方就是在暫存器中修改,當暫存器沒有要找的資料是,就會去一級快取找,如果一級快取中沒有資料就會去二級快取找,依序查找知道從磁碟找到,然後在載入到暫存器中。當三級快取從記憶體取資料發現三級快取不足時,就會自動清理三級快取的空間。
我們知道資料最終存放的位置是硬碟,這個存取過程是由作業系統來完成的。而我們CPU在處理資料是透過兩種寫入方式將資料寫到不同的地方,那就是通寫(寫到記憶體中)和回寫(寫到一級快取中)。很顯然回寫的性能好,但是如果斷電的話就尷尬了,數據會丟失,因為他直接寫到一級緩存中就完事了,但是一級緩存其他CPU是訪問不到的,因此從可靠性的角度上來說通寫方式會比較可靠。具體採用哪種方式得你自己按需而定啦。
四.IO設備
1.IO設備由設備控制器和設備本身組成。
裝置控制器:整合在主機板的一塊晶片活一組晶片。負責從作業系統接收命令,並完成命令的執行。例如負責從作業系統讀取資料。
設備本身:其有自己的接口,但是設備本身的接口並不可用,它只是一個物理接口。如IDE介面。
擴展小知識:
每個控制器都有少量的用於通訊的暫存器(幾個到幾十個不等)。這個寄存器是直接整合到設備控制器內部的。比方說,一個最小化的磁碟控制器,它也會用於指定磁碟位址,磁區計數,讀寫方向等相關操作請求的暫存器。所以任何時候想要啟動控制器,設備驅動程式從作業系統中接收操作指令,然後將它轉換成對應設備的基本操作,並把操作請求放置在暫存器中才能完成操作的。每個暫存器表現為一個IO埠。所有的暫存器組合稱為裝置的I/O位址空間,也叫I/O埠空間,
2.驅動程式
真正的硬體操作是由驅動程式操作完成的。驅動程式通常應該由設備生產上完成,通常驅動程式位於核心中,雖然驅動程式可以在核心外運行,但是很少有人這麼玩,因為它太低效率啦!
3.實現輸入和輸出
裝置的I/O埠沒法事前分配,因為各個主機板的型號不一致,所以我們需要做到動態指定。電腦開機的時候,每個IO設備都要想匯流排的I/o埠空間註冊使用I/O埠。這個動態端口是由所有的寄存器組合成為設備的I/O位址空間,有2^16次方個端口,即65535個端口。
如上圖所示,我們的CPU有要跟指定裝置打交道,就需要把指令傳給驅動,然後驅動講CPU的指令轉換成裝置能理解的訊號放在暫存器中(也可以叫套接字,socket).所以說暫存器(I/O埠)是CPU透過匯流排和裝置打交道的位址(I/O埠)。
擴充小知識:
三種方式實作I/O裝置的輸入與輸出:
A..輪詢:
通常指的是用戶程式發起一個系統調用,內核將其翻譯成一個內核對應驅動的過程調用,然後設備驅動程式啟動I/O,並在一個連續循環不斷中檢查該設備,並看該設備是否完成了工作。這有點類似於忙等待(就是cpu會用固定週期不斷通過遍歷的方式去查看每一個I/O設備去查看是否有數據, 顯然這種效率並不理想。),
B. .中斷:
中斷CPU正在處理的程序,中斷CPU正在執行的操作,從而通知核心來取得中斷請求。在我們的主機板通常有一個獨特的設備,叫做可程式中斷控制器。這個中斷控制器可以透過某個針腳和CPU直接進行通信,能夠出發CPU發生某個位置偏轉,進而讓CPU知道某個訊號到達。中斷控制器上會有一個中斷向量(我們每一個I/O設備在啟動時,要想中斷控制器註冊一個中斷號,這個號通常是唯一的。通常中斷向量的每一個針腳都是可以識別多個中斷號碼的),也可以叫中斷號。
因此當這個裝置真正發生中斷時,這個裝置不會把資料直接放到總線上,這個裝置會立即向中斷控制器發出中斷請求,中斷控制器透過中斷向量識別這個請求是哪個設備發來的,然後透過某種方式通知CPU,讓CPU知道具體哪個設備中斷求情到達了。這時候CPU可以依照設備註冊使用I/O埠號,因此就能取得到設備的資料了。 (注意,CPU是不能直接取資料的喲,因為他只是接收到了中斷訊號,它只能通知內核,讓內核自己運行在CPU上,由內核來取得中斷請求。)舉個例子,一個網卡接收到外來IP的請求,網路卡也有自己的快取區,CPU講網卡中的快取拿到記憶體中進行去讀,先判斷是不是自己的IP,如果是就開始拆報文,最後會取得到一個埠號,然後CPIU在自己的中斷控制器去找這個端口,並做對應的處理。
核心中斷處理分為兩個步驟:中斷上半部(立即處理)和中斷下半部(不一定)。還是從網卡接收資料為例,當使用者要求到達網卡時,CPU會命令講網卡快取區的資料直接拿到記憶體中來,也就是接收到資料後會立即處理(這裡的處理就是將網路卡的數據讀到記憶體而已,不做下一步處理,以方便以後處理的。 C.DMA:
直接記憶體訪問,大家都知道資料的傳輸都是在總線上實現的,CPU是控制總線的使用者,在某一時刻到底是有哪個I/O設備使用總線是由CPU的控制器來決定的。匯流排有三個功能分別是:位址匯流排(完成對設備的尋址功能),控制匯流排(控制各設備位址使用匯流排的功能)以及資料匯流排(實現資料傳輸)。
通常是I/O設備自帶的一個具有智慧型的控制晶片(我們稱之為直接記憶體存取控制器),當需要處理中斷上半部時,CPU會告知DMA設備,接下來匯流排歸DMA裝置使用,並告知其可使用的記憶體空間,用於將I/O裝置的資料讀取到記憶體空間中去。當DMA的I/O設備將資料讀取完成後,會發送訊息告訴CPU以及完成了讀取操作,這時候CPU再回通知內核資料已經載入完畢,具體中斷下半部的處理就來交個內核處理了。現在大多數設備都是用DMA控制器的,例如:網卡,硬碟等等。
五.作業系統概念
透過上面的學習,我們知道了的電腦有五個基本部件。作業系統主要就是把這五個部件給它抽象化為比較直觀的接口,由上層程式設計師或使用者直接使用的。那事實上在作業系統中被抽象化的東西又該是什麼呢?
1.CPU(time slice)
在作業系統中,CPU被抽象化了時間片,而後將程式抽象化為進程,透過分配時間片讓程式運作起來。 CPU有尋址單元用來識別變數在記憶體的中所保存的集體記憶體位址。
而我們主機內部的匯流排是取決於CPU的位寬(也叫字長),例如32bit的位址匯流排,它能表示2的32次方個記憶體位址,轉換成10進位就是4G記憶體空間,這時候你應該就明白為什麼32位元的作業系統只能辨識4G記憶體了吧?即使你的實體記憶體是16G,但是可用的還是4G,所以,你如果發現你的作業系統能辨識4G以上的記憶體位址,那麼你的作業系統一定就不是32位元的啦!
2.記憶體(memory)
在作業系統中,記憶體的實作是透過虛擬位址空間來實現的。
3.I/O裝置
在作業系統中,最核心的I/O裝置就是磁碟,大家都知道磁碟是提供儲存空間的,在核心中把它抽象成了文件。
4.進程
說白了,電腦存在的主要目的不就是執行程式嗎?程式跑起來,我們統一叫進程(我們暫時不用理會線程)。那如果多個行程同時運作就意味著把這些有限的抽象資源(cpu,memory等等)分配給多個進程。我們把這些抽象資源統稱為資源集合。
資源集包括:
1>.cpu時間;
#2>.記憶體位址:抽象化為虛擬位址空間(如32位元作業系統,支援4G空間,核心佔用1G空間,進程也會預設自己有3G可用,事實上未必有3G空間,因為你的電腦可能會是小於4G的記憶體。開啟的多個文件,透過fd(文件描述符,file descriptor)開啟指定的文件。我們把文件分為三類:正常文件、設備文件、管道文件。
每一個進行都有自己作業位址結構,即:task struct。其就是核心為每個進程維護的一個資料結構(一個資料結構就是用來保存資料的,說白了就是記憶體空間,記錄著該進程所擁有的資源集,當然還有它的父進程,保存現場【用於進程切換】,記憶體映射等待)。 task struct模擬出來了線性位址,讓行程去使用這些線性位址,但是它會記錄線性位址和物理記憶體位址的映射關係的。
5.記憶體映射-頁框
只要不是核心使用的實體記憶體空間我們稱為使用者空間。內核會吧用戶空間的實體記憶體切割成固定大小的頁框(即page frame),歡聚話說,就是且更成一個固定大小的儲存單位,比預設的單一儲存單元(預設是一個位元組,即8bit )要大.通常每4k一個存儲單位。每一個頁框作為一個獨立的單元向外進行分配,且每一個頁框也都其編號。 【舉例:假設有4G空間可用,每一個頁框是4K,一共有1M個頁框。 】這些頁框要分配給不同的進程使用。
我們假設你有4G內存,作業系統佔用了1個G,剩餘的3G物理內存分配給用戶空間使用。每一進程啟動之後,都會認為自己有3G空間可用,但實際上它壓根就用不完3G。進程進行寫入記憶體是離散儲存的。哪有空餘內存就往哪裡訪問。具體的訪問演算法不要問我,我也沒有研究過。
進程空間結構:
1>.預留空間
2>.堆疊(變數存放處)
3>.共享庫
4>.堆(開啟一個文件,文件中的資料流存放處)
5>.資料段(全域的靜態變數存放處)
6>.程式碼段
進程和記憶體的儲存關係如下:
每個行程空間都有預留空間,當某個行程發現自己開啟的資料已經不夠用,它需要打開一個新文件(打開一個新文件就需要在進程的地址空間存放資料),很顯然我們上圖的進程地址空間是線性的並不是真正意義上的。當一個行程真正去申請使用一個記憶體時,需要向內核發起系統調用,由核心在物理記憶體上找一個物理空間,並告訴該行程可以使用的記憶體位址。比方說進程要在堆上打開一個文件,它需要向操作系統(內核)申請使用內存空間,且在物理內存允許的範圍內(即請求的內存需要小於空閒物理內存),內核會分配給該進程內存地址。
每一進程都有自己想線性位址,這個位址是作業系統虛擬出來的,並不真實存在,它需要把這個虛擬位址和真正的實體記憶體做一個映射關係,如圖「進程和記憶體的儲存關係”,最終的進程資料的存放處位置還是映射到記憶體中了。這意味著,當一個進行跑到CPU上執行時,它告訴CPU的是自己的線性位址,這時候CPU不會直接去找這個線性位址(因為線性位址是虛擬出來的,不真實存在,真正存放位址進程的是實體記憶體位址。一個頁表項。 ],以讀取到進程的所擁有的線性位址所對應的真正的實體記憶體位址。
擴展小知識:
CPU存取進程的位址時,首先取得到的是進程的線性位址,它將這個線性位址交給自己的晶片MMU進行計算,得到真正的實體記憶體位址,從而達到存取進程記憶體位址的目的。換句話說,只要他想要存取一個進程的記憶體位址,就必須經過MMU運算,這樣導致效率很低,因此他們有引進了一個緩存,用於存放頻繁訪問的數據,這樣就可以提高效率,不用MMU進行計算,直接拿到資料去處理就OK了,這個快取器我們稱之為:TLB:轉換後援緩衝器(快取頁表的查詢結果)
注意:在32bit的作業系統是線線位址到實體記憶體的對應。而在64bit作業系統是恰恰相反的!
6.用戶態和核心態
作業系統運行時為了呢能夠實現協調多任務,作業系統被分割成了2段,其中接近硬體一段具有特權權限的叫做核心空間,而行程運行在用戶空間當中。所以說,應用程式需要使用特權指令或是要存取硬體資源時需要係統呼叫。
只要是被開發成應用程式的,不是作為作業系統本身的一部分而存在的,我們稱之為使用者空間的程式。他們運作狀態稱之為使用者態。
需要在核心(我們可以認為是作業系統)空間運行的程序,我們稱之他們運行在核心空間,他們運行的狀態為用戶態,也叫核心態。注意:核心不負責完成具體工作。在核心空間可用執行任何特權操作。
每一個程式要想真正運作起來,它最終是向核心發起系統呼叫來完成的,或是有一部分的程式不需要核心的參與,有我們的應用程式就能完成。我們打個比方,你要計算2的32次方的結果,要不要運行在內核態呢?答案是否定的,我們知道核心是不負責完成具體工作的,我們只是想要計算一個運算結果,也不需要呼叫任何的特權模式,因此,如果你寫了一些關於計算數值的程式碼,只需要把這個程式碼交給CPU運作就可以了。
如果一個應用程式需要呼叫內核的功能而不是用戶程式的功能的話,應用程式會發現自己需要做一個特權操作,而應用程式本身沒有這個能力,應用程式會向內核發出申請,讓內核幫忙完成特權操作。核心發現應用程式是有權限使用特權指令的,核心會運行這些特權指令並把執行結果傳回給應用程序,然後這個應用程式拿到特權指令的執行結果後,繼續後續的程式碼。這就是模式轉換。
因此一個程式設計師想要讓你的程式具有生產力,就應該盡量讓你的程式碼運行在用戶空間,如果你的程式碼大多數都運行在核心空間的話,估計你的應用程式並不會給你打來太大的生產力喲。因為我們知道內核空間不負責產生生產力。
擴充小知識:
我們知道電腦的運作就是運作指定的。指令也分特權指令等級和非特權指令等級。了解過電腦的朋友可能知道X86的CPU架構大概分成了四個層次,由內之外共有四個環,被稱為環0,環1,環2,環3。我們知道環0的都是特權指令,環3的都是使用者指令。一般來講,特權指令等級是指操作硬件,控制總線等等。
一個程式的執行,需要在核心的協調下,有可能在使用者態和核心態互相切換,所以說一個程式的執行,一定是核心調度它到CPU上去執行的 。有些應用程式是作業系統運行過程當中,為了完成基本功能而運行的,我們就讓他在後台自動運行,這叫守護程序。但是有的程式是用戶需要的時候才運行的,那如何通知內核講我們需要的應用程式運行起來呢?這時候你需要一個解釋器,它能和作業系統打交道,能夠發動指令的執行。說穿了就是能夠把用戶需要的運行請求提交給內核,進而內核給它開放其運行所需要的有賴於的基本條件。從而程序就執行起來了。
相關推薦:
以上是linux作業系統原理基礎的詳細內容。更多資訊請關注PHP中文網其他相關文章!