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.對稱多處理器SMP
  CPU裏面除了有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也是共享內存的。
                           

 

4.非一致性內存訪問(NUMA)
  咱們知道當多顆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的內存。)
        3>.I/O:一切皆文件打開的多個文件,經過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不會直接去找這個線性地址(由於線性地址是虛擬出來的,不真實存在,真正存放地址進程的是物理內存地址。),它會先去找這歌進程的「task struct」,並裝載頁表(page table)[記錄着線性地址到物理內存的映射關係,每個對應關係叫作一個頁表項。],以讀取到進程的所擁有的線性地址所對應的真正的物理內存地址。
 
擴展小知識:
  CPU訪問進程的地址時,首先獲取到的是進程的線性地址,它將這個線性地址交給本身的芯片MMU進行計算,獲得真正的物理內存地址,從而達到訪問進程內存地址的目的。換句話說,只要他想要訪問一個進程的內存地址,就必須通過MMU運算,這樣致使效率很低,所以他們有引進了一個緩存,用於存放頻繁訪問的數據,這樣就能夠提升效率,不用MMU進行計算,直接拿到數據去處理就OK了,這個緩存器咱們稱之爲:TLB:轉換後援緩衝器(緩存頁表的查詢結果)
  注意:在32bit的操做系統是線線地址到物理內存的映射。而在64bit操做系統是偏偏相反的!
 
6.用戶態和內核態
  操做系統運行時爲了呢可以實現協調多任務,操做系統被分割成了2段,其中接近於硬件一段具備特權權限的叫作內核空間,而進程運行在用戶空間當中。因此說,應用程序須要使用特權指令或是要訪問硬件資源時須要系統調用。
  只要是被開發成應用程序的,不是做爲操做系統自己的一部分而存在的,咱們稱之爲用戶空間的程序。他們運行狀態稱之爲用戶態。
  須要在內核(咱們能夠認爲是操做系統)空間運行的程序,咱們稱之他們運行在內核空間,他們運行的狀態爲用戶態,也叫核心態。注意:內核不負責完成具體工做。在內核空間可用執行任何特權操做。
  每個程序要想真正運行起來,它最終是向內核發起系統調用來完成的,或者有一部分的程序不須要內核的參與,有咱們的應用程序就能完成。咱們打個比方,你要計算2的32次方的結果,是否須要運行在內核態呢?答案是否認的,咱們知道內核是不負責完成具體工做的,咱們只是想要計算一個運算結果,也不須要調用任何的特權模式,所以,若是你寫了一些關於計算數值的代碼,只須要把這個代碼交給CPU運行就能夠了。
  若是一個應用程序須要調用內核的功能而不是用戶程序的功能的話,應用程序會發現本身須要作一個特權操做,而應用程序自身沒有這個能力,應用程序會向內核發申請,讓內核幫忙完成特權操做。內核發現應用程序是有權限使用特權指令的,內核會運行這些特權指令並把執行結果返回給應用程序,而後這個應用程序拿到特權指令的執行結果後,繼續後續的代碼。這就是模式轉換。
  所以一個程序員想要讓你的程序具備生產力,就應該儘可能讓你的代碼運行在用戶空間,若是你的代碼大多數都運行在內核空間的話,估計你的應用程序並不會給你打來太大的生產力喲。由於咱們知道內核空間不負責產生生產力。
 
擴充小知識:
  咱們知道計算機的運行就是運行指定的。指令還分特權指令級別和非特權指令級別。瞭解過計算機的朋友可能知道X86的CPU架構大概分紅了四個層次,由內以外共有四個環,被稱爲環0,環1,環2,環3。咱們知道環0的都是特權指令,環3的都是用戶指令。通常來說,特權指令級別是指操做硬件,控制總線等等。
  一個程序的執行,須要在內核的協調下,有可能在用戶態和內核態互相切換,因此說一個程序的執行,必定是內核調度它到CPU上去執行的 。有些應用程序是操做系統運行過程中,爲了完成基本功能而運行的,咱們就讓他在後臺自動運行,這叫守護進程。可是有的程序是用戶須要的時候才運行的,那如何通知內核講咱們須要的應用程序運行起來呢?這個時候你就須要一個解釋器,它能和操做系統打交道,可以發起指令的執行。說白了就是可以把用戶須要的運行請求提交給內核,進而內核給它開放其運行所須要的有賴於的基本條件。從而程序就執行起來了。
相關文章
相關標籤/搜索