面試之操作系統

基本特征

1. 并發

  • 并發是指宏觀上在一段時間內能同時運行多個程序,而并行則指同一時刻能運行多個指令。
  • 并行需要硬件支持,如多流水線、多核處理器或者分布式計算系統。
  • 操作系統通過引入進程和線程,使得程序能夠并發運行。

2. 共享

  • 共享是指系統中的資源可以被多個并發進程共同使用。
  • 有兩種共享方式:互斥共享和同時共享。
  • 互斥共享的資源稱為臨界資源,例如打印機等,在同一時間只允許一個進程訪問,需要用同步機制來實現對臨界資源的訪問。

3. 虛擬

  • 虛擬技術把一個物理實體轉換為多個邏輯實體。
  • 主要有兩種虛擬技術:時分復用技術和空分復用技術。
  • 多個進程能在同一個處理器上并發執行使用了時分復用技術,讓每個進程輪流占有處理器,每次只執行一小個時間片并快速切換。
  • 虛擬內存使用了空分復用技術,它將物理內存抽象為地址空間,每個進程都有各自的地址空間。地址空間的頁被映射到物理內存,地址空間的頁并不需要全部在物理內存中,當使用到一個沒有在物理內存的頁時,執行頁面置換算法,將該頁置換到內存中。

4. 異步

  • 異步指進程不是一次性執行完畢,而是走走停停,以不可知的速度向前推進。

基本功能

  1. 進程管理:進程控制、進程同步、進程通信、死鎖處理、處理機調度等。
  2. 內存管理:內存分配、地址映射、內存保護與共享、虛擬內存等。
  3. 文件管理:文件存儲空間的管理、目錄管理、文件讀寫管理和保護等。
  4. 設備管理:完成用戶的 I/O 請求,方便用戶使用各種設備,并提高設備的利用率。主要包括緩沖管理、設備分配、設備處理、虛擬設備等。

進程線程相關

進程的常見狀態?以及各種狀態之間的轉換條件?

  • 就緒:進程已處于準備好運行的狀態,即進程已分配到除CPU外的所有必要資源后,只要再獲得CPU,便可立即執行。
  • 執行:進程已經獲得CPU,程序正在執行狀態。
  • 阻塞:正在執行的進程由于發生某事件(如I/O請求、申請緩沖區失敗等)暫時無法繼續執行的狀態。

? ? ? ? ??

守護、僵尸、孤兒進程的概念

  • 守護進程:運行在后臺的一種特殊進程,獨立于控制終端并周期性地執行某些任務
  • 僵尸進程:一個進程 fork 子進程,子進程退出,而父進程沒有wait/waitpid子進程,那么子進程的進程描述符仍保存在系統中,這樣的進程稱為僵尸進程。
  • 孤兒進程:一個父進程退出,而它的一個或多個子進程還在運行,這些子進程稱為孤兒進程。(孤兒進程將由 init 進程收養并對它們完成狀態收集工作)

進程的通信方式有哪些?

進程通信,是指進程之間的信息交換(信息量少則一個狀態或數值,多者則是成千上萬個字節)。

對于用信號量進行的進程間的互斥和同步,由于其所交換的信息量少而被歸結為低級通信。高級進程通信指:用戶可以利用操作系統所提供的一組通信命令傳送大量數據的一種通信方式。操作系統隱藏了進程通信的實現細節。或者說,通信過程對用戶是透明的。

高級通信機制可歸結為三大類:

  1. 共享存儲器系統(存儲器中劃分的共享存儲區);實際操作中對應的是“剪貼板”(剪貼板實際上是系統維護管理的一塊內存區域)的通信方式,比如舉例如下:word進程按下ctrl+c,在ppt進程按下ctrl+v,即完成了word進程和ppt進程之間的通信,復制時將數據放入到剪貼板,粘貼時從剪貼板中取出數據,然后顯示在ppt窗口上。
  2. 消息傳遞系統(進程間的數據交換以消息(message)為單位,當今最流行的微內核操作系統中,微內核與服務器之間的通信,無一例外地都采用了消息傳遞機制。應用舉例:郵槽(MailSlot)是基于廣播通信體系設計出來的,它采用無連接的不可靠的數據傳輸。郵槽是一種單向通信機制,創建郵槽的服務器進程讀取數據,打開郵槽的客戶機進程寫入數據。
  3. 管道通信系統(管道即:連接讀寫進程以實現他們之間通信的共享文件(pipe文件,類似先進先出的隊列,由一個進程寫,另一進程讀))。實際操作中,管道分為:匿名管道、命名管道。匿名管道是一個未命名的、單向管道,通過父進程和一個子進程之間傳輸數據。匿名管道只能實現本地機器上兩個進程之間的通信,而不能實現跨網絡的通信。命名管道不僅可以在本機上實現兩個進程間的通信,還可以跨網絡實現兩個進程間的通信。

進程間的通信的幾種方式

  • 管道管道是單向的、先進先出的、無結構的、固定大小的字節流,它把一個進程的標準輸出和另一個進程的標準輸入連接在一起。寫進程在管道的尾端寫入數據,讀進程在管道的道端讀出數據。數據讀出后將從管道中移走,其它讀進程都不能再讀到這些數據。管道提供了簡單的流控制機制。進程試圖讀空管道時,在有數據寫入管道前,進程將一直阻塞。同樣地,管道已經滿時,進程再試圖寫管道,在其它進程從管道中移走數據之前,寫進程將一直阻塞。
  1. 注1:無名管道只能實現父子或者兄弟進程之間的通信,有名管道(FIFO)可以實現互不相關的兩個進程之間的通信。
  2. 注2:用FIFO讓一個服務器和多個客戶端進行交流時候,每個客戶在向服務器發送信息前建立自己的讀管道,或者讓服務器在得到數據后再建立管道。使用客戶的進程號(pid)作為管道名是一種常用的方法。客戶可以先把自己的進程號告訴服務器,然后再到那個以自己進程號命名的管道中讀取回復。
  • 信號量信號量是一個計數器,可以用來控制多個進程對共享資源的訪問。它常作為一種鎖機制,防止某進程正在訪問共享資源時,其它進程也訪問該資源。因此,主要作為進程間以及同一進程內不同線程之間的同步手段。

  • 信號(signal):信號是一種比較復雜的通信方式,用于通知接收進程某個事件已經發生;

  • 消息隊列是一個在系統內核中用來保存消息的隊列,它在系統內核中是以消息鏈表的形式出現的。消息隊列克服了信號傳遞信息少、管道只能承載無格式字節流以及緩沖區大小受限等缺點

  • 共享內存:共享內存允許兩個或多個進程訪問同一個邏輯內存。這一段內存可以被兩個或兩個以上的進程映射至自身的地址空間中,一個進程寫入共享內存的信息,可以被其他使用這個共享內存的進程,通過一個簡單的內存讀取讀出,從而實現了進程間的通信。如果某個進程向共享內存寫入數據,所做的改動將立即影響到可以訪問同一段共享內存的任何其他進程。共享內存是最快的IPC方式,它是針對其它進程間通信方式運行效率低而專門設計的。它往往與其它通信機制(如信號量)配合使用,來實現進程間的同步和通信。

  • 套接字:套接字也是一種進程間通信機制,與其它通信機制不同的是,它可用于不同機器間的進程通信。

線程間的通信機制

  • 鎖機制:互斥鎖、條件變量、讀寫鎖?
  1. 互斥鎖提供了以排他方式防止數據結構被并發修改的方法。?
  2. 讀寫鎖允許多個線程同時讀共享數據,而對寫操作是互斥的。?
  3. 條件變量可以以原子的方式進行阻塞進程,直到某個特定條件為真為止。對條件的測試是在互斥鎖的保護下進行的。條件變量始終與互斥鎖一起使用。?
  • 信號量機制:包括無名信號量和命名線程信號量?
  • 信號機制:類似進程間的信號處理?

線程間的通信目的主要是用于線程同步,所以線程沒有像進程通信中的用于數據交換的通信機制。

上下文切換

對于單核單線程CPU而言,在某一時刻只能執行一條CPU指令。上下文切換(Context Switch)是一種將CPU資源從一個進程分配給另一個進程的機制。從用戶角度看,計算機能夠并行運行多個進程,這恰恰是操作系統通過快速上下文切換造成的結果。在切換的過程中,操作系統需要先存儲當前進程的狀態(包括內存空間的指針,當前執行完的指令等等),再讀入下一個進程的狀態,然后執行此進程。

進程與線程的區別和聯系?

  • 進程是具有一定獨立功能的程序關于某個數據集合上的一次運行活動,進程是系統進行資源分配和調度的一個獨立單位。
  • 線程是進程的一個實體,是CPU調度和分派的基本單位,它是比進程更小的能獨立運行的基本單位。

進程和線程的關系

  • 一個線程只能屬于一個進程,而一個進程可以有多個線程,但至少有一個線程。線程是操作系統可識別的最小執行和調度單位。
  • 資源分配給進程,同一進程的所有線程共享該進程的所有資源。 同一進程中的多個線程共享代碼段(代碼和常量),數據段(全局變量和靜態變量),擴展段(堆存儲)。但是每個線程擁有自己的棧段,棧段又叫運行時段,用來存放所有局部變量和臨時變量。
  • 處理機分給線程,即真正在處理機上運行的是線程。
  • 線程在執行過程中,需要協作同步。不同進程的線程間要利用消息通信的辦法實現同步。

進程與線程的區別?

  • 進程有自己的獨立地址空間,線程沒有
  • 進程是資源分配的最小單位,線程是CPU調度的最小單位
  • 進程和線程通信方式不同(線程之間的通信比較方便。同一進程下的線程共享數據(比如全局變量,靜態變量),通過這些數據來通信不僅快捷而且方便,當然如何處理好這些訪問的同步與互斥正是編寫多線程程序的難點。而進程之間的通信只能通過進程通信的方式進行。)
  • 進程上下文切換開銷大,線程開銷小
  • 一個進程掛掉了不會影響其他進程,而線程掛掉了會影響其他線程
  • 對進程進程操作一般開銷都比較大,對線程開銷就小了?

?為什么進程上下文切換比線程上下文切換代價高?

進程切換分兩步:1.切換頁目錄以使用新的地址空間;2.切換內核棧和硬件上下文

對于linux來說,線程和進程的最大區別就在于地址空間,對于線程切換,第1步是不需要做的,第2是進程和線程切換都要做的。

切換的性能消耗:

  1. 線程上下文切換和進程上下文切換一個最主要的區別是:線程切換的虛擬內存空間依然是相同的,但是進程切換是不同的。這兩種上下文切換的處理都是通過操作系統內核來完成的。內核的這種切換過程伴隨的最顯著的性能損耗是將寄存器中的內容切換出。
  2. 另外一個隱藏的損耗是上下文的切換會擾亂處理器的緩存機制。簡單的說,一旦去切換上下文,處理器中所有已經緩存的內存地址一瞬間都作廢了。還有一個顯著的區別是當你改變虛擬內存空間的時候,處理的頁表緩沖(processor's Translation Lookaside Buffer (TLB))或者相當的神馬東西會被全部刷新,這將導致內存的訪問在一段時間內相當的低效。但是在線程的切換中,不會出現這個問題。

轉自知乎:進程和線程的區別

參考鏈接:https://www.zhihu.com/question/25532384/answer/81152571

首先來一句概括的總論:進程和線程都是一個時間段的描述,是CPU工作時間段的描述。

下面細說背景
CPU+RAM+各種資源(比如顯卡,光驅,鍵盤,GPS, 等等外設)構成我們的電腦,但是電腦的運行,實際就是CPU和相關寄存器以及RAM之間的事情。

一個最最基礎的事實:CPU太快,太快,太快了,寄存器僅僅能夠追的上他的腳步,RAM和別的掛在各總線上的設備完全是望其項背。那當多個任務要執行的時候怎么辦呢?輪流著來?或者誰優先級高誰來?不管怎么樣的策略,一句話就是在CPU看來就是輪流著來。

一個必須知道的事實:執行一段程序代碼,實現一個功能的過程介紹 ,當得到CPU的時候,相關的資源必須也已經就位,就是顯卡啊,GPS啊什么的必須就位,然后CPU開始執行。這里除了CPU以外所有的就構成了這個程序的執行環境,也就是我們所定義的程序上下文。當這個程序執行完了,或者分配給他的CPU執行時間用完了,那它就要被切換出去,等待下一次CPU的臨幸。在被切換出去的最后一步工作就是保存程序上下文,因為這個是下次他被CPU臨幸的運行環境,必須保存。

串聯起來的事實:前面講過在CPU看來所有的任務都是一個一個的輪流執行的,具體的輪流方法就是:先加載程序A的上下文,然后開始執行A,保存程序A的上下文,調入下一個要執行的程序B的程序上下文,然后開始執行B,保存程序B的上下文。。。

========= 重要的東西出現了========

進程和線程就是這樣的背景出來的,兩個名詞不過是對應的CPU時間段的描述,名詞就是這樣的功能。

  • 進程就是包換上下文切換的程序執行時間總和?=?CPU加載上下文+CPU執行+CPU保存上下文

線程是什么呢?
進程的顆粒度太大,每次都要有上下的調入,保存,調出。如果我們把進程比喻為一個運行在電腦上的軟件,那么一個軟件的執行不可能是一條邏輯執行的,必定有多個分支和多個程序段,就好比要實現程序A,實際分成 a,b,c等多個塊組合而成。那么這里具體的執行就可能變成:

程序A得到CPU =》CPU加載上下文,開始執行程序A的a小段,然后執行A的b小段,然后再執行A的c小段,最后CPU保存A的上下文。

這里a,b,c的執行是共享了A的上下文,CPU在執行的時候沒有進行上下文切換的。這里的a,b,c就是線程,也就是說線程是共享了進程的上下文環境,的更為細小的CPU時間段。

到此全文結束,再一個總結:


進程和線程都是一個時間段的描述,是CPU工作時間段的描述,不過是顆粒大小不同。

  • 進程(process)與線程(thread)最大的區別是進程擁有自己的地址空間,某進程內的線程對于其他進程不可見,即進程A不能通過傳地址的方式直接讀寫進程B的存儲區域。進程之間的通信需要通過進程間通信(Inter-process communication,IPC)。與之相對的,同一進程的各線程間之間可以直接通過傳遞地址或全局變量的方式傳遞信息

  • 進程作為操作系統中擁有資源和獨立調度的基本單位,可以擁有多個線程。通常操作系統中運行的一個程序就對應一個進程。在同一進程中,線程的切換不會引起進程切換。在不同進程中進行線程切換,如從一個進程內的線程切換到另一個進程中的線程時,會引起進程切換。相比進程切換,線程切換的開銷要小很多。線程于進程相互結合能夠提高系統的運行效率。

線程可以分為兩類:

  • 用戶級線程(user level thread):對于這類線程,有關線程管理的所有工作都由應用程序完成,內核意識不到線程的存在。在應用程序啟動后,操作系統分配給該程序一個進程號,以及其對應的內存空間等資源。應用程序通常先在一個線程中運行,該線程被成為主線程。在其運行的某個時刻,可以通過調用線程庫中的函數創建一個在相同進程中運行的新線程。用戶級線程的好處是非常高效,不需要進入內核空間,但并發效率不高。

  • 內核級線程(kernel level thread):對于這類線程,有關線程管理的所有工作由內核完成,應用程序沒有進行線程管理的代碼,只能調用內核線程的接口。內核維護進程及其內部的每個線程,調度也由內核基于線程架構完成。內核級線程的好處是,內核可以將不同線程更好地分配到不同的CPU,以實現真正的并行計算。

事實上,在現代操作系統中,往往使用組合方式實現多線程,即線程創建完全在用戶空間中完成,并且一個應用程序中的多個用戶級線程被映射到一些內核級線程上,相當于是一種折中方案。

進程調度

調度種類

  • 高級調度:(High-Level Scheduling)又稱為作業調度,它決定把后備作業調入內存運行;
  • 低級調度:(Low-Level Scheduling)又稱為進程調度,它決定把就緒隊列的某進程獲得CPU;
  • 中級調度:(Intermediate-Level Scheduling)又稱為在虛擬存儲器中引入,在內、外存對換區進行進程對換。

非搶占式調度與搶占式調度

  • 非搶占式:分派程序一旦把處理機分配給某進程后便讓它一直運行下去,直到進程完成或發生進程調度進程調度某事件而阻塞時,才把處理機分配給另一個進程。
  • 搶占式:操作系統將正在運行的進程強行暫停,由調度程序將CPU分配給其他就緒進程的調度方式。

調度策略的設計

  • 響應時間: 從用戶輸入到產生反應的時間
  • 周轉時間: 從任務開始到任務結束的時間

CPU任務可以分為交互式任務批處理任務,調度最終的目標是合理的使用CPU,使得交互式任務的響應時間盡可能短,用戶不至于感到延遲,同時使得批處理任務的周轉時間盡可能短,減少用戶等待的時間。

調度算法:

FIFO或First Come, First Served (FCFS)先來先服務

  • 調度的順序就是任務到達就緒隊列的順序。
  • 公平、簡單(FIFO隊列)、非搶占、不適合交互式。
  • 未考慮任務特性,平均等待時間可以縮短。

Shortest Job First (SJF)短作業優先

  • 最短的作業(CPU區間長度最小)最先調度。
  • SJF可以保證最小的平均等待時間。

Shortest Remaining Job First (SRJF)可搶占的短作業優先

  • SJF的可搶占版本,比SJF更有優勢。
  • SJF(SRJF): 如何知道下一CPU區間大小?根據歷史進行預測: 指數平均法。

優先權調度

  • 每個任務關聯一個優先權,調度優先權最高的任務。
  • 注意:優先權太低的任務一直就緒,得不到運行,出現“饑餓”現象。

Round-Robin(RR)輪轉調度算法

  • 設置一個時間片,按時間片來輪轉調度(“輪叫”算法)
  • 優點: 定時有響應,等待時間較短;缺點: 上下文切換次數較多;
  • 時間片太大,響應時間太長;吞吐量變小,周轉時間變長;當時間片過長時,退化為FCFS。

多級隊列調度

  • 按照一定的規則建立多個進程隊列
  • 不同的隊列有固定的優先級(高優先級有搶占權)
  • 不同的隊列可以給不同的時間片和采用不同的調度方法
  • 存在問題1:沒法區分I/O bound和CPU bound;
  • 存在問題2:也存在一定程度的“饑餓”現象;

多級反饋隊列

  • 在多級隊列的基礎上,任務可以在隊列之間移動,更細致的區分任務。
  • 可以根據“享用”CPU時間多少來移動隊列,阻止“饑餓”。
  • 最通用的調度算法,多數OS都使用該方法或其變形,如UNIX、Windows等。

多級反饋隊列調度算法描述:

clipboard.png

  • 進程在進入待調度的隊列等待時,首先進入優先級最高的Q1等待。
  • 首先調度優先級高的隊列中的進程。若高優先級中隊列中已沒有調度的進程,則調度次優先級隊列中的進程。例如:Q1,Q2,Q3三個隊列,只有在Q1中沒有進程等待時才去調度Q2,同理,只有Q1,Q2都為空時才會去調度Q3。
  • 對于同一個隊列中的各個進程,按照時間片輪轉法調度。比如Q1隊列的時間片為N,那么Q1中的作業在經歷了N個時間片后若還沒有完成,則進入Q2隊列等待,若Q2的時間片用完后作業還不能完成,一直進入下一級隊列,直至完成。
  • 在低優先級的隊列中的進程在運行時,又有新到達的作業,那么在運行完這個時間片后,CPU馬上分配給新到達的作業(搶占式)。

線程安全

如果你的代碼所在的進程中有多個線程在同時運行,而這些線程可能會同時運行這段代碼。如果每次運行結果和單線程運行的結果是一樣的,而且其他的變量的值也和預期的是一樣的,就是線程安全的。或者說:一個類或者程序所提供的接口對于線程來說是原子操作或者多個線程之間的切換不會導致該接口的執行結果存在二義性,也就是說我們不用考慮同步的問題。

線程安全問題都是由全局變量及靜態變量引起的。

若每個線程中對全局變量、靜態變量只有讀操作,而無寫操作,一般來說,這個全局變量是線程安全的;若有多個線程同時執行寫操作,一般都需要考慮線程同步,否則的話就可能影響線程安全。

線程共享資源和獨占資源問題

參考鏈接

一個進程中的所有線程共享該進程的地址空間,但它們有各自獨立的(/私有的)棧(stack),Windows線程的缺省堆棧大小為1M。堆(heap)的分配與棧有所不同,一般是一個進程有一個C運行時堆,這個堆為本進程中所有線程共享,windows進程還有所謂進程默認堆,用戶也可以創建自己的堆。?

用操作系統術語,線程切換的時候實際上切換的是一個可以稱之為線程控制塊的結構(TCB),里面保存所有將來用于恢復線程環境必須的信息,包括所有必須保存的寄存器集,線程的狀態等。

堆:是大家共有的空間,分全局堆和局部堆。全局堆就是所有沒有分配的空間,局部堆就是用戶分配的空間。堆在操作系統對進程初始化的時候分配,運行過程中也可以向系統要額外的堆,但是記得用完了要還給操作系統,要不然就是內存泄漏。

棧:是個線程獨有的,保存其運行狀態和局部自動變量的。棧在線程開始的時候初始化,每個線程的棧互相獨立,因此,棧是 thread safe的。操作系統在切換線程的時候會自動的切換棧,就是切換 SS/ESP寄存器。棧空間不需要在高級語言里面顯式的分配和釋放。


進程互斥,同步

互斥:指某一個資源同時只允許一個訪問者對其進行訪問,具有唯一性和排它性。但互斥無法限制訪問者對資源的訪問順序,即訪問是無序的?

同步:是指在互斥的基礎上(大多數情況下),通過其它機制實現訪問者對資源的有序訪問。大多數情況下,同步已經實現了互斥,特別是所有寫入資源的情況必定是互斥的。少數情況是指可以允許多個訪問者同時訪問資源。?

同步:體現的是一種協作性。互斥:體現的是排它性。??

進程同步

進程同步的主要任務:是對多個相關進程在執行次序上進行協調,以使并發執行的諸進程之間能有效地共享資源和相互合作,從而使程序的執行具有可再現性。

  同步機制遵循的原則:

  1. 空閑讓進;
  2. 忙則等待(保證對臨界區的互斥訪問);
  3. 有限等待(有限代表有限的時間,避免死等);
  4. 讓權等待,(當進程不能進入自己的臨界區時,應該釋放處理機,以免陷入忙等狀態)。

進程同步有哪幾種機制

  原子操作、信號量機制、自旋鎖管程、會合、分布式系統

線程同步

線程同步是指多個線程同時訪問某資源時,采用一系列的機制以保證最多只能一個線程訪問該資源。線程同步是多線程中必須考慮和解決的問題,以為很有可能發生多個線程同時訪問(主要是寫操作)同一資源,如果不進行線程同步,很可能會引起數據混亂,造成線程死鎖等問題。?

多線程同步和互斥有幾種實現方法

線程間的同步方法大體可分為兩類:用戶模式和內核模式。顧名思義,內核模式就是指利用系統內核對象的單一性來進行同步,使用時需要切換內核態與用戶態,而用戶模式就是不需要切換到內核態,只在用戶態完成操作。

用戶模式下的方法有:原子操作(例如一個單一的全局變量),臨界區。

內核模式下的方法有:事件,信號量,互斥量

  1. 臨界區:通過對多線程的串行化來訪問公共資源或一段代碼,速度快,適合控制數據訪問。?
  2. 互斥量:為協調共同對一個共享資源的單獨訪問而設計的。?
  3. 信號量:為控制一個具有有限數量用戶資源而設計。?
  4. 事件(信號):用來通知線程有一些事件已發生,從而啟動后繼任務的開始

線程同步不同方式間的總結比較:?

互斥量和臨界區很相似,但是互斥量是可以命名的,它可以跨越進程使用,所以創建互斥量所需要的資源更多,如果只是為了在進程內部使用使用臨界區會帶來速度上的優勢并能夠減少資源占用量。?
  互斥量、信號量、事件都可以被跨越進程使用來進行同步數據操作,而其他的對象與數據同步操作無關,但對于進程和線程來講,如果進程和線程在運行狀態則為無信號狀態,所以可以使用WaitForSingleObject來等待進程和線程退出。

Semaphore(信號量) Vs Mutex(互斥鎖)

  • 當用戶創立多個線程/進程時,如果不同線程/進程同時讀寫相同的內容,則可能造成讀寫錯誤,或者數據不一致。此時,需要通過加鎖的方式,控制臨界區(critical section)的訪問權限。對于semaphore而言,在初始化變量的時候可以控制允許多少個線程/進程同時訪問一個臨界區,其他的線程/進程會被堵塞,直到有人解鎖。
  • Mutex相當于只允許一個線程/進程訪問的semaphore。此外,根據實際需要,人們還實現了一種讀寫鎖(read-write lock),它允許同時存在多個閱讀者(reader),但任何時候至多只有一個寫者(writer),且不能于讀者共存。

同步與異步

同步的定義:是指一個進程在執行某個請求的時候,若該請求需要一段時間才能返回信息,那么,這個進程將會一直等待下去,直到收到返回信息才繼續執行下去。

  • 特點:
  1. 同步是阻塞模式;
  2. 同步是按順序執行,執行完一個再執行下一個,需要等待,協調運行;

異步是指進程不需要一直等下去,而是繼續執行下面的操作,不管其他進程的狀態。當有消息返回時系統會通知進程進行處理,這樣可以提高執行的效率。

  • 特點:
  1. 異步是非阻塞模式,無需等待;
  2. 異步是彼此獨立,在等待某事件的過程中,繼續做自己的事,不需要等待這一事件完成后再工作。線程是異步實現的一個方式。

同步與異步的優缺點:

  • 同步可以避免出現死鎖,讀臟數據的發生。一般共享某一資源的時候,如果每個人都有修改權限,同時修改一個文件,有可能使一個讀取另一個人已經刪除了內容,就會出錯,同步就不會出錯。但,同步需要等待資源訪問結束,浪費時間,效率低。
  • 異步可以提高效率,但,安全性較低。

死鎖

死鎖的條件?以及如何處理死鎖問題?

定義:如果一組進程中的每一個進程都在等待僅由該組進程中的其他進程才能引發的事件,那么該組進程就是死鎖的。或者在兩個或多個并發進程中,如果每個進程持有某種資源而又都等待別的進程釋放它或它們現在保持著的資源,在未改變這種狀態之前都不能向前推進,稱這一組進程產生了死鎖。通俗地講,就是兩個或多個進程被無限期地阻塞、相互等待的一種狀態。

產生死鎖的必要條件:

  • 互斥(Mutual exclusion):資源不能被共享,只能由一個進程使用。
  • 請求與保持(Hold and wait):已經得到資源的進程可以再次申請新的資源。
  • 非搶占(No pre-emption):已經分配的資源不能從相應的進程中被強制地剝奪。
  • 循環等待(Circular wait):系統中若干進程組成環路,該環路中每個進程都在等待相鄰進程正占用的資源。

死鎖的處理基本策略和常用方法

解決死鎖的基本方法主要有 預防死鎖、避免死鎖、檢測死鎖、解除死鎖 、鴕鳥策略 等。

(1). 死鎖預防?

死鎖預防的基本思想是 只要確保死鎖發生的四個必要條件中至少有一個不成立,就能預防死鎖的發生,具體方法包括:

  • 打破互斥條件:允許進程同時訪問某些資源。但是,有些資源是不能被多個進程所共享的,這是由資源本身屬性所決定的,因此,這種辦法通常并無實用價值。
  • 打破占有并等待條件:可以實行資源預先分配策略(進程在運行前一次性向系統申請它所需要的全部資源,若所需全部資源得不到滿足,則不分配任何資源,此進程暫不運行;只有當系統能滿足當前進程所需的全部資源時,才一次性將所申請資源全部分配給該線程)或者只允許進程在沒有占用資源時才可以申請資源(一個進程可申請一些資源并使用它們,但是在當前進程申請更多資源之前,它必須全部釋放當前所占有的資源)。但是這種策略也存在一些缺點:在很多情況下,無法預知一個進程執行前所需的全部資源,因為進程是動態執行的,不可預知的;同時,會降低資源利用率,導致降低了進程的并發性。
  • 打破非搶占條件:允許進程強行從占有者哪里奪取某些資源。也就是說,但一個進程占有了一部分資源,在其申請新的資源且得不到滿足時,它必須釋放所有占有的資源以便讓其它線程使用。這種預防死鎖的方式實現起來困難,會降低系統性能。
  • 打破循環等待條件:實行資源有序分配策略。對所有資源排序編號,所有進程對資源的請求必須嚴格按資源序號遞增的順序提出,即只有占用了小號資源才能申請大號資源,這樣就不回產生環路,預防死鎖的發生。

(2). 死鎖避免

死鎖避免的基本思想是動態地檢測資源分配狀態,以確保循環等待條件不成立,從而確保系統處于安全狀態。所謂安全狀態是指:如果系統能按某個順序為每個進程分配資源(不超過其最大值),那么系統狀態是安全的,換句話說就是,如果存在一個安全序列,那么系統處于安全狀態。資源分配圖算法和銀行家算法是兩種經典的死鎖避免的算法,其可以確保系統始終處于安全狀態。其中,資源分配圖算法應用場景為每種資源類型只有一個實例(申請邊,分配邊,需求邊,不形成環才允許分配),而銀行家算法應用于每種資源類型可以有多個實例的場景。

(3). 死鎖解除

死鎖解除的常用兩種方法為進程終止和資源搶占。所謂進程終止是指簡單地終止一個或多個進程以打破循環等待,包括兩種方式:終止所有死鎖進程和一次只終止一個進程直到取消死鎖循環為止;所謂資源搶占是指從一個或多個死鎖進程那里搶占一個或多個資源,此時必須考慮三個問題:

  • 選擇一個犧牲品
  • 回滾:回滾到安全狀態
  • 饑餓(在代價因素中加上回滾次數,回滾的越多則越不可能繼續被作為犧牲品,避免一個進程總是被回滾)

一些算法:

  • 鴕鳥算法,該算法可以應用在極少發生死鎖的的情況下。為什么叫鴕鳥算法呢,因為傳說中鴕鳥看到危險就把頭埋在地底下,可能鴕鳥覺得看不到危險也就沒危險了吧。跟掩耳盜鈴有點像。
  • 銀行家算法

臨界資源

在操作系統中,進程是占有資源的最小單位(線程可以訪問其所在進程內的所有資源,但線程本身并不占有資源或僅僅占有一點必須資源)。但對于某些資源來說,其在同一時間只能被一個進程所占用。這些一次只能被一個進程所占用的資源就是所謂的臨界資源。典型的臨界資源比如物理上的打印機,或是存在硬盤或內存中被多個進程所共享的一些變量和數據等(如果這類資源不被看成臨界資源加以保護,那么很有可能造成丟數據的問題)。

對于臨界資源的訪問,必須是互斥進行。也就是當臨界資源被占用時,另一個申請臨界資源的進程會被阻塞,直到其所申請的臨界資源被釋放。而進程內訪問臨界資源的代碼被成為臨界區。

臨界區、如何解決沖突?

每個進程中訪問臨界資源的那段程序稱為臨界區,每次只準許一個進程進入臨界區,進入后不允許其他進程進入。如果有若干個進程要求進入空閑的臨界區,一次僅允許一個進程進入。任何時候,處于臨界區的進程不可多于一個。如已有進程進入自己的臨界區,則其他試圖進入臨界區的進程必須等待。進入臨界區的進程要在有限時間內退出,以便其他進程能及時進入自己的臨界區。如果不能進入自己的臨界區,就應該讓出CPU,避免進程出現忙等等現象。


內存

1).內存的發展歷程

  沒有內存抽象(單進程,除去操作系統所用的內存之外,全部給用戶程序使用) —> 有內存抽象(多進程,進程獨立的地址空間,交換技術(內存大小不可能容納下所有并發執行的進程)?
)—> 連續內存分配(固定大小分區(多道程序的程度受限),可變分區(首次適應,最佳適應,最差適應),碎片) —> 不連續內存分配(分段,分頁,段頁式,虛擬內存)

2).虛擬內存

  虛擬內存允許執行進程不必完全在內存中,它使得應用程序認為它擁有連續的可用的內存(一個連續完整的地址空間)。虛擬內存的基本思想是:每個進程擁有獨立的地址空間,這個空間被分為大小相等的多個塊,稱為頁(Page),每個頁都是一段連續的地址。這些頁被映射到物理內存,但并不是所有的頁都必須在內存中才能運行程序。當程序引用到一部分在物理內存中的地址空間時,由硬件立刻進行必要的映射;當程序引用到一部分不在物理內存中的地址空間時,由操作系統負責將缺失的部分裝入物理內存并重新執行失敗的命令。這樣,對于進程而言,邏輯上似乎有很大的內存空間,實際上其中一部分對應物理內存上的一塊(稱為幀,通常頁和幀大小相等),還有一些沒加載在內存中的對應在硬盤上,如下圖所示。

注意,請求分頁系統、請求分段系統和請求段頁式系統都是針對虛擬內存的,通過請求實現內存與外存的信息置換。

  由上圖可以看出,虛擬內存實際上可以比物理內存大。當訪問虛擬內存時,會訪問MMU(內存管理單元)去匹配對應的物理地址(比如上圖的0,1,2)。如果虛擬內存的頁并不存在于物理內存中(如上圖的3,4),會產生缺頁中斷,從磁盤中取得缺的頁放入內存,如果內存已滿,還會根據某種算法將磁盤中的頁換出。

而虛擬內存和物理內存的匹配是通過頁表實現,頁表存在MMU中,頁表中每個項通常為32位(4byte)除了存儲虛擬地址和頁框地址之外,還會存儲一些標志位,比如是否缺頁,是否修改過,寫保護等。可以把MMU想象成一個接收虛擬地址項返回物理地址的方法。

因為頁表中每個條目是4字節,現在的32位操作系統虛擬地址空間會是2的32次方,即使每頁分為4K,也需要2的20次方*4字節=4M的空間,為每個進程建立一個4M的頁表并不明智。因此在頁表的概念上進行推廣,產生二級頁表,二級頁表每個對應4M的虛擬地址,而一級頁表去索引這些二級頁表,因此32位的系統需要1024個二級頁表,雖然頁表條目沒有減少,但內存中可以僅僅存放需要使用的二級頁表和一級頁表,大大減少了內存的使用。

兩級頁表結構

兩級表結構的第一級稱為頁目錄,存儲在一個4K字節的頁面中。頁目錄表共有1K個表項,每個表項為4個字節,并指向第二級表。線性地址的最高10位(即位31~位32)用來產生第一級的索引,由索引得到的表項中,指定并選擇了1K個二級表中的一個表。

兩級表結構的第二級稱為頁表,也剛好存儲在一個4K字節的頁面中,包含1K個字節的表項,每個表項包含一個頁的物理基地址。第二級頁表由 線性地址的中間10位(即位21~位12) 進行索引,以獲得包含頁的物理地址的頁表項,這個物理地址的高20位與線性地址的低12位形成了最后的物理地址,也就是頁轉化過程輸出的物理地址。

線性地址到物理地址的轉換

擴展分頁

從奔騰處理器開始,Intel微處理器引進了擴展分頁,它允許頁的大小為4MB。

頁面高速緩存:

?虛擬內存的應用與優點

  虛擬內存很適合在多道程序設計系統中使用,許多程序的片段同時保存在內存中。當一個程序等待它的一部分讀入內存時,可以把CPU交給另一個進程使用。虛擬內存的使用可以帶來以下好處:

  • 在內存中可以保留多個進程,系統并發度提高
  • 解除了用戶與內存之間的緊密約束,進程可以比內存的全部空間還大

虛擬內存的實現有以下兩種方式:

  • 請求分頁存儲管理。
  • 請求分段存儲管理。

分頁和分段有什么區別?

  段式存儲管理是一種符合用戶視角的內存分配管理方案。在段式存儲管理中,將程序的地址空間劃分為若干段(segment),如代碼段,數據段,堆棧段;這樣每個進程有一個二維地址空間,相互獨立,互不干擾。段式管理的優點是:沒有內碎片(因為段大小可變,改變段大小來消除內碎片)。但段換入換出時,會產生外碎片(比如4k的段換5k的段,會產生1k的外碎片)

  頁式存儲管理方案是一種用戶視角內存與物理內存相分離的內存分配管理方案。在頁式存儲管理中,將程序的邏輯地址劃分為固定大小的頁(page),而物理內存劃分為同樣大小的幀,程序加載時,可以將任意一頁放入內存中任意一個幀,這些幀不必連續,從而實現了離散分離。頁式存儲管理的優點是:沒有外碎片(因為頁的大小固定),但會產生內碎片(一個頁可能填充不滿)。

兩者的不同點:

  • 目的不同:分頁是由于系統管理的需要而不是用戶的需要,它是信息的物理單位;分段的目的是為了能更好地滿足用戶的需要,它是信息的邏輯單位,它含有一組其意義相對完整的信息;
  • 大小不同:頁的大小固定且由系統決定,而段的長度卻不固定,由其所完成的功能決定;
  • 地址空間不同: 段向用戶提供二維地址空間;頁向用戶提供的是一維地址空間;
  • 信息共享:段是信息的邏輯單位,便于存儲保護和信息的共享,頁的保護和共享受到限制;
  • 內存碎片:頁式存儲管理的優點是沒有外碎片(因為頁的大小固定),但會產生內碎片(一個頁可能填充不滿);而段式管理的優點是沒有內碎片(因為段大小可變,改變段大小來消除內碎片)。但段換入換出時,會產生外碎片(比如4k的段換5k的段,會產生1k的外碎片)。

邏輯地址 Vs 物理地址 Vs 虛擬內存

  • 所謂的邏輯地址,是指計算機用戶(例如程序開發者),看到的地址。例如,當創建一個長度為100的整型數組時,操作系統返回一個邏輯上的連續空間:指針指向數組第一個元素的內存地址。由于整型元素的大小為4個字節,故第二個元素的地址時起始地址加4,以此類推。事實上,邏輯地址并不一定是元素存儲的真實地址,即數組元素的物理地址(在內存條中所處的位置),并非是連續的,只是操作系統通過地址映射,將邏輯地址映射成連續的,這樣更符合人們的直觀思維

  • 另一個重要概念是虛擬內存。操作系統讀寫內存的速度可以比讀寫磁盤的速度快幾個量級。但是,內存價格也相對較高,不能大規模擴展。于是,操作系統可以通過將部分不太常用的數據移出內存,“存放到價格相對較低的磁盤緩存,以實現內存擴展。操作系統還可以通過算法預測哪部分存儲到磁盤緩存的數據需要進行讀寫,提前把這部分數據讀回內存。虛擬內存空間相對磁盤而言要小很多,因此,即使搜索虛擬內存空間也比直接搜索磁盤要快。唯一慢于磁盤的可能是,內存、虛擬內存中都沒有所需要的數據,最終還需要從硬盤中直接讀取。這就是為什么內存和虛擬內存中需要存儲會被重復讀寫的數據,否則就失去了緩存的意義。現代計算機中有一個專門的轉譯緩沖區(Translation Lookaside Buffer,TLB),用來實現虛擬地址到物理地址的快速轉換。

與內存/虛擬內存相關的還有如下兩個概念:

1) Resident Set

當一個進程在運行的時候,操作系統不會一次性加載進程的所有數據到內存,只會加載一部分正在用,以及預期要用的數據。其他數據可能存儲在虛擬內存,交換區和硬盤文件系統上。被加載到內存的部分就是resident set。

2) Thrashing

由于resident set包含預期要用的數據,理想情況下,進程運行過程中用到的數據都會逐步加載進resident set。但事實往往并非如此:每當需要的內存頁面(page)不在resident set中時,操作系統必須從虛擬內存或硬盤中讀數據,這個過程被稱為內存頁面錯誤(page faults)。當操作系統需要花費大量時間去處理頁面錯誤的情況就是thrashing。

參考鏈接:https://blog.csdn.net/newcong0123/article/details/52792070

內部碎片與外部碎片

在內存管理中,內部碎片是已經被分配出去的的內存空間大于請求所需的內存空間。

外部碎片是指還沒有分配出去,但是由于大小太小而無法分配給申請空間的新進程的內存空間空閑塊。

固定分區存在內部碎片,可變式分區分配會存在外部碎片;

頁式虛擬存儲系統存在內部碎片段式虛擬存儲系統,存在外部碎片

為了有效的利用內存,使內存產生更少的碎片,要對內存分頁,內存以頁為單位來使用,最后一頁往往裝不滿,于是形成了內部碎片。

為了共享要分段,在段的換入換出時形成外部碎片,比如5K的段換出后,有一個4k的段進來放到原來5k的地方,于是形成1k的外部碎片。

頁面置換算法

操作系統將內存按照頁面進行管理,在需要的時候才把進程相應的部分調入內存。當產生缺頁中斷時,需要選擇一個頁面寫入。如果要換出的頁面在內存中被修改過,變成了“臟”頁面,那就需要先寫會到磁盤。頁面置換算法,就是要選出最合適的一個頁面,使得置換的效率最高。

一、最優頁面置換算法

最理想的狀態下,我們給頁面做個標記,挑選一個最遠才會被再次用到的頁面調出。當然,這樣的算法不可能實現,因為不確定一個頁面在何時會被用到。

二、先進先出頁面置換算法(FIFO)及其改進

這種算法的思想和隊列是一樣的,該算法總是淘汰最先進入內存的頁面,即選擇在內存中駐留時間最久的頁面予淘汰。

實現:把一個進程已調入內存的頁面按先后次序鏈接成一個隊列,并且設置一個指針總是指向最老的頁面。缺點:對于有些經常被訪問的頁面如含有全局變量、常用函數、例程等的頁面,不能保證這些不被淘汰。

三、最近最少使用頁面置換算法LRU(Least Recently Used)

根據頁面調入內存后的使用情況做出決策。LRU置換算法是選擇最近最久未使用的頁面進行淘汰。

  1. 為每個在內存中的頁面配置一個移位寄存器。(P165)定時信號將每隔一段時間將寄存器右移一位。最小數值的寄存器對應頁面就是最久未使用頁面。
  2. 利用一個特殊的棧保存當前使用的各個頁面的頁面號。每當進程訪問某頁面時,便將該頁面的頁面號從棧中移出,將它壓入棧頂。因此,棧頂永遠是最新被訪問的頁面號,棧底是最近最久未被訪問的頁面號。

鏈接:分頁內存管理(把虛擬內存空間和物理內存空間均劃分為大小相同的頁面等內容)

鏈接:分段內存管理

四、最近不常使用算法(Not Recently Used Replacement Algorithm)

這種算法給每個頁一個標志位,R表示最近被訪問過,M表示被修改過。定期對R進行清零。這個算法的思路是首先淘汰那些未被訪問過R=0的頁,其次是被訪問過R=1,未被修改過M=0的頁,最后是R=1,M=1的頁。

五、時鐘算法,改進時鐘算法

雖然改進型FIFO算法避免置換出常用的頁,但由于需要經常移動頁,效率并不高。因此在改進型FIFO算法的基礎上,將隊列首位相連形成一個環路,當缺頁中斷產生時,從當前位置開始找R=0的頁,而所經過的R=1的頁被置0,并不需要移動頁

六、工作集置換算法

七、缺頁率置換算法

顛簸(抖動)

  顛簸本質上是指頻繁的頁調度行為,具體來講,進程發生缺頁中斷,這時,必須置換某一頁。然而,其他所有的頁都在使用,它置換一個頁,但又立刻再次需要這個頁。因此,會不斷產生缺頁中斷,導致整個系統的效率急劇下降,這種現象稱為顛簸(抖動)。

  內存顛簸的解決策略包括:

  • 如果是因為頁面替換策略失誤,可以修改替換算法來解決這個問題;
  • 如果是因為運行的程序太多,造成程序無法同時將所有頻繁訪問的頁面調入內存,則要降低多道程序的數量;
  • 否則,還剩下兩個辦法:終止該進程或增加物理內存容量。

Belady現象:

對有的頁面置換算法,頁錯誤率可能會隨著分配幀數增加而增加。

FIFO會產生Belady異常。

棧式算法無Belady異常,LRU,LFU(最不經常使用),OPT都屬于棧式算法。

局部性原理

  • 時間上的局部性:最近被訪問的頁在不久的將來還會被訪問;
  • 空間上的局部性:內存中被訪問的頁周圍的頁也很可能被訪問。

什么是緩沖區溢出?有什么危害?其原因是什么?

緩沖區為暫時置放輸出或輸入資料的內存。

緩沖區溢出是指當計算機向緩沖區填充數據時超出了緩沖區本身的容量,溢出的數據覆蓋在合法數據上。

緩沖區溢出是一種非常普遍、非常危險的漏洞,在很多軟件與操作系統都存在這種問題。

舉個例子幫助理解其本質,好比你往一個100ml容量的燒杯里倒200ml濃硫酸,實際情況下你肯定不會這么做,但是溢出的問題就在于有人想這么做了,而且這個倒的行為沒有被提前檢查并阻止。于是100ml的濃硫酸溢出燒杯,并且流到燒杯外的周圍區域,造成破壞。而哪怕你倒的不是濃硫酸(非惡意程序),只是普通的水,由于水的溢出,也會對周圍的環境造成意料外的沖刷(對臨近數據的覆蓋)。

計算機中,緩沖區溢出會造成的危害主要有以下兩點:

  • 程序崩潰,導致拒絕服務
  • 跳轉并且執行一段惡意代碼

造成緩沖區溢出的主要原因是程序中沒有仔細檢查用戶輸入是否合理。


中斷與系統調用

所謂的中斷就是在計算機執行程序的過程中,由于出現了某些特殊事情,使得CPU暫停對程序的執行,轉而去執行處理這一事件的程序。等這些特殊事情處理完之后再回去執行之前的程序。中斷一般分為三類:

  • 由計算機硬件異常或故障引起的中斷,稱為內部異常中斷
  • 由程序中執行了引起中斷的指令而造成的中斷,稱為軟中斷(這也是和我們將要說明的系統調用相關的中斷);
  • 由外部設備請求引起的中斷,稱為外部中斷。簡單來說,對中斷的理解就是對一些特殊事情的處理。

與中斷緊密相連的一個概念就是中斷處理程序了。當中斷發生的時候,系統需要去對中斷進行處理,對這些中斷的處理是由操作系統內核中的特定函數進行的,這些處理中斷的特定的函數就是我們所說的中斷處理程序了。

另一個與中斷緊密相連的概念就是中斷的優先級。中斷的優先級說明的是當一個中斷正在被處理的時候,處理器能接受的中斷的級別。中斷的優先級也表明了中斷需要被處理的緊急程度。每個中斷都有一個對應的優先級,當處理器在處理某一中斷的時候,只有比這個中斷優先級高的中斷可以被處理器接受并且被處理。優先級比這個當前正在被處理的中斷優先級要低的中斷將會被忽略。

典型的中斷優先級如下所示:

  • 機器錯誤 > 時鐘 > 磁盤 > 網絡設備 > 終端 > 軟件中斷

在講系統調用之前,先說下進程的執行在系統上的兩個級別:用戶級和核心級,也稱為用戶態和系統態(user mode and kernel mode)

? ? ? ? ? ?用戶空間就是用戶進程所在的內存區域,相對的,系統空間就是操作系統占據的內存區域。用戶進程和系統進程的所有數據都在內存中。處于用戶態的程序只能訪問用戶空間,而處于內核態的程序可以訪問用戶空間和內核空間。

用戶態切換到內核態的方式如下:

  • 系統調用:程序的執行一般是在用戶態下執行的,但當程序需要使用操作系統提供的服務時,比如說打開某一設備、創建文件、讀寫文件(這些均屬于系統調用)等,就需要向操作系統發出調用服務的請求,這就是系統調用。
  • 異常:當CPU在執行運行在用戶態下的程序時,發生了某些事先不可知的異常,這時會觸發由當前運行進程切換到處理此異常的內核相關程序中,也就轉到了內核態,比如缺頁異常。
  • 外圍設備的中斷:當外圍設備完成用戶請求的操作后,會向CPU發出相應的中斷信號,這時CPU會暫停執行下一條即將要執行的指令轉而去執行與中斷信號對應的處理程序,如果先前執行的指令是用戶態下的程序,那么這個轉換的過程自然也就發生了由用戶態到內核態的切換。比如硬盤讀寫操作完成,系統會切換到硬盤讀寫的中斷處理程序中執行后續操作等。

用戶態和核心態(內核態)之間的區別是什么呢?

權限不一樣。

  • 用戶態的進程能存取它們自己的指令和數據,但不能存取內核指令和數據(或其他進程的指令和數據)
  • 核心態下的進程能夠存取內核和用戶地址某些機器指令是特權指令,在用戶態下執行特權指令會引起錯誤。在系統中內核并不是作為一個與用戶進程平行的估計的進程的集合。

Linux 的系統調用主要有以下這些:

TaskCommands
進程控制fork(); exit(); wait();
進程通信pipe(); shmget(); mmap();
文件操作open(); read(); write();
設備操作ioctl(); read(); write();
信息維護getpid(); alarm(); sleep();
安全chmod(); umask(); chown();

一個程序從開始運行到結束的完整過程(四個過程)

  1. 預處理:條件編譯,頭文件包含,宏替換的處理,生成.i文件。
  2. 編譯:將預處理后的文件轉換成匯編語言,生成.s文件
  3. 匯編:匯編變為目標代碼(機器代碼)生成.o的文件
  4. 鏈接:連接目標代碼,生成可執行程序

鏈接

內存池、進程池、線程池。(c++程序員必須掌握)

??  首先介紹一個概念“池化技術 ”。池化技術就是:提前保存大量的資源,以備不時之需以及重復使用。池化技術應用廣泛,如內存池,線程池,連接池等等。內存池相關的內容,建議看看Apache、Nginx等開源web服務器的內存池實現。
??  由于在實際應用當做,分配內存、創建進程、線程都會設計到一些系統調用,系統調用需要導致程序從用戶態切換到內核態,是非常耗時的操作。因此,當程序中需要頻繁的進行內存申請釋放,進程、線程創建銷毀等操作時,通常會使用內存池、進程池、線程池技術來提升程序的性能。
??  線程池:線程池的原理很簡單,類似于操作系統中的緩沖區的概念,它的流程如下:先啟動若干數量的線程,并讓這些線程都處于睡眠狀態,當需要一個開辟一個線程去做具體的工作時,就會喚醒線程池中的某一個睡眠線程,讓它去做具體工作,當工作完成后,線程又處于睡眠狀態,而不是將線程銷毀。
??  進程池與線程池同理。
??  內存池:內存池是指程序預先從操作系統申請一塊足夠大內存,此后,當程序中需要申請內存的時候,不是直接向操作系統申請,而是直接從內存池中獲取;同理,當程序釋放內存的時候,并不真正將內存返回給操作系統,而是返回內存池。當程序退出(或者特定時間)時,內存池才將之前申請的內存真正釋放。

動態鏈接庫與靜態鏈接庫的區別

靜態庫

  • 靜態庫是一個外部函數與變量的集合體。靜態庫的文件內容,通常包含一堆程序員自定的變量與函數,其內容不像動態鏈接庫那么復雜,在編譯期間由編譯器與鏈接器將它集成至應用程序內,并制作成目標文件以及可以獨立運作的可執行文件。而這個可執行文件與編譯可執行文件的程序,都是一種程序的靜態創建(static build)。

動態庫

  • 靜態庫很方便,但是如果我們只是想用庫中的某一個函數,卻仍然得把所有的內容都鏈接進去。一個更現代的方法則是使用共享庫,避免了在文件中靜態庫的大量重復。
  • 動態鏈接可以在首次載入的時候執行(load-time linking),這是 Linux 的標準做法,會由動態鏈接器ld-linux.so 完成,比方標準 C 庫(libc.so) 通常就是動態鏈接的,這樣所有的程序可以共享同一個庫,而不用分別進行封裝。
  • 動態鏈接也可以在程序開始執行的時候完成(run-time linking),在 Linux 中使用 dlopen()接口來完成(會使用函數指針),通常用于分布式軟件,高性能服務器上。而且共享庫也可以在多個進程間共享。
  • 鏈接使得我們可以用多個對象文件構造我們的程序。可以在程序的不同階段進行(編譯、載入、運行期間均可),理解鏈接可以幫助我們避免遇到奇怪的錯誤

區別:

  1. 使用靜態庫的時候,靜態鏈接庫要參與編譯,在生成執行文件之前的鏈接過程中,要將靜態鏈接庫的全部指令直接鏈接入可執行文件中。而動態庫提供了一種方法,使進程可以調用不屬于其可執行代碼的函數。函數的可執行代碼位于一個.dll文件中,該dll包含一個或多個已被編譯,鏈接并與使用它們的進程分開儲存的函數。
  2. 靜態庫中不能再包含其他動態庫或靜態庫,而在動態庫中還可以再包含其他動態或者靜態庫。
  3. 靜態庫在編譯的時候,就將庫函數裝在到程序中去了,而動態庫函數必須在運行的時候才被裝載,所以使用靜態庫速度快一些。

鏈接

系統調用與庫函數的區別

  • 系統調用(System call)是程序向系統內核請求服務的方式。可以包括硬件相關的服務(例如,訪問硬盤等),或者創建新進程,調度其他進程等。系統調用是程序和操作系統之間的重要接口。
  • 庫函數:把一些常用的函數編寫完放到一個文件里,編寫應用程序時調用,這是由第三方提供的,發生在用戶地址空間
  • 移植性方面,不同操作系統的系統調用一般是不同的,移植性差;而在所有的ANSI C編譯器版本中,C庫函數是相同的。
  • 調用開銷方面,系統調用需要在用戶空間和內核環境間切換,開銷較大;而庫函數調用屬于“過程調用”,開銷較小。

IO多路復用

IO多路復用是指內核一旦發現進程指定的一個或者多個IO條件準備讀取,它就通知該進程。IO多路復用適用如下場合:

  • 當客戶處理多個描述字時(一般是交互式輸入和網絡套接口),必須使用I/O復用。
  • 當一個客戶同時處理多個套接口時,而這種情況是可能的,但很少出現。
  • 如果一個TCP服務器既要處理監聽套接口,又要處理已連接套接口,一般也要用到I/O復用。
  • 如果一個服務器即要處理TCP,又要處理UDP,一般要使用I/O復用。
  • 如果一個服務器要處理多個服務或多個協議,一般要使用I/O復用。
  • 與多進程和多線程技術相比,I/O多路復用技術的最大優勢是系統開銷小,系統不必創建進程/線程,也不必維護這些進程/線程,從而大大減小了系統的開銷。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:
http://www.pswp.cn/news/454107.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/454107.shtml
英文地址,請注明出處:http://en.pswp.cn/news/454107.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

mysql新增列并同時增加數據_圖解MySQL | [原理解析] MySQL 為表添加列 是怎么quot;立刻quot;完成的...

在上一期圖解 圖解MySQL | MySQL DDL為什么成本高?中,我們介紹了:傳統情況下,為表添加列需要對表進行重建騰訊團隊為 MySQL 引入了 Instant Add Column 的方案(以下稱為 "立刻加列" 功能)可以快速完成 為表添加列 的任務…

GCC for Win32開發環境介紹

GCC for Win32開發環境介紹(1) 第一章 在視窗操作系統下的GCC 第一節GCC家族概覽 GCC是一個原本用于Unix-like系統下編程的編譯器。不過,現在GCC也有了許多Win32下的移植版本。所以,也許對于許多Windows開發者來說,GCC還是一個比較陌生的東西…

包裝函數

function wrap(object,method,wrapper){ //object:包裝方法所屬對象 method:方法名 wrapper:替換函數var fn object[method];return object[method] function(){return wrapper.apply(this,[fn.bind(this)].concat(Array.prototype.slice.call(arguments)));}; } 轉載于…

JAR——pinyin4j-2.5.0

簡介:將中文轉為拼音; 使用: 123//返回的是字符串String pinyin[] PinyinHelper.toHanyuPinyinStringArray(chinese);//eg:你----ni3本文轉自wauoen51CTO博客,原文鏈接:http://blog.51cto.com/7183397/1605894&#…

Android高效加載大圖、多圖解決方案,有效避免程序OOM

http://blog.csdn.net/guolin_blog/article/details/9316683轉載于:https://www.cnblogs.com/jianglijs/p/7827524.html

Flask 上下文源碼解析

簡單來說,上下文包括request_ctx(封裝了request和session),app_request(封裝了app和g),兩個ctx都儲存在一個叫做Local的數據結構中,這個結構的作用就是會自動根據不同的線程id返回對應的數據,然后通過一個叫做 LocalStark 的結構把…

reg型變量怎么賦值_UiPath變量介紹和使用

1 變量變量主要用于存儲數據,它在RPA中扮演重要的數據傳遞角色,是RPA編程不可或缺的一部分。它包括變量名稱和變量的值,變量的值支持多種數據類型,包括從通用值,文本,數字,數據表,時…

gcc 使用教程

gcc 使用教程 目 錄 gcc makefile寫法 gcc_egcs使用 gdb使用 gcc常用選項對代碼的影響 一般情況 -O 編譯選項 -O2 編譯選項 -fomit-frame-pointer 編譯選項-fomit-frame-pointer && -O2-fPIC 編譯選項 -static 編譯選項 AT&T的匯編格式 x86內聯匯編 簡述 內聯匯編…

Struts2教程9:實現自已的攔截器

在上一篇中介紹了Struts2攔截器的原理,在這一篇中我們將學習一下如何編寫自己的攔截器。一、攔截器的實現實現一個攔截器非常簡單。實際上,一個攔截器就是一個普通的類,只是這個類必須實現com.opensymphony.xwork2.interceptor.Interceptor接…

標準C程序設計七---66

Linux應用 編程深入 語言編程標準C程序設計七---經典C11程序設計 以下內容為閱讀: 《標準C程序設計》(第7版) 作者:E. Balagurusamy(印), 李周芳譯 清華大學出版社…

深度學習之概述

深度學習的應用場景 1、圖像應用: 1.1 大規模(大數據量)圖片識別(聚類/分類),如人臉識別,車牌識別,OCR等。人臉識別算法:① faceID ② faceNet 1.2 以圖搜圖,圖像分割 1.3 目標檢測&#xff0…

如何根據對象獲取到對應的表名_Excel VBA 常用對象二

下面繼續講解上一節中未講完的內容:Excel VBA編程中常常使用的那些對象到底是什么,如何在代碼中表示它們。Worksheet對象Worksheet對象代表工作表。工作簿中的每個工作表都是一個Worksheet對象,所有Worksheet對象構成了Worksheets集合。我們使…

PIX525故障一例,求解

IDC機房網絡拓樸如下:IDC核心交換機-----通過一條網線-------機柜D-LNKI交換機------PIX 525------CISCO交換機------各WEB服務器。其中D-LINK交換機的IP為192.168.2.11,也就是下面日志中的IP。另外,之所以IDC和PIX之間再加一臺DLINK是因為有…

gcc教程(轉)

gcc 目 錄 gcc makefile寫法 gcc_egcs使用 gdb使用 gcc常用選項對代碼的影響 一般情況 -O 編譯選項 -O2 編譯選項 -fomit-frame-pointer 編譯選項 -fomit-frame-pointer && -O2 -fPIC 編譯選項 -static 編譯選項 AT&T的匯編格式 x86內聯匯編 簡述 內聯匯編 程序模…

深度學習之 BP 算法

神經網絡的一種求解W的算法,分為信號“正向傳播(FP)”求損失,“反向傳播(BP)”回傳誤差;根據誤差值修改每層的權重,繼續迭代。 BP算法也叫做δ算法。以三層的感知器為例(假定現在隱層和輸出層均存在相同類型的激活函數…

python自帶的解釋器叫做_python學習

一、PYTHON中的元素1.基本元素運算符: - * / %等等除法:" / " 表示浮點數除法,返回浮點結果;" // " 表示整數除法,返回不大于結果的一個最大的整數運算順序:先乘除 再加減 括號最優先變量:就是一…

IE打印空白

今天碰到HR經理碰到一個問題,就是windows 7 64位操作系統,但是打印空白,打印出來像白紙一樣!經過查看和總結,確認是:由于保護模式下 %Temp%\Low 文件夾工作不正常引起的!故障打印白紙下面會出現…

Python Matplotlib.plot Update image Questions

1. 最近在測試一款設備,采集了一些設備后需要一幀一幀顯示圖像,經常使用Python,所以選用了Matplotlib進行圖像操作 數據結構: timesatamp polar_distance horizontal_angle refelectivity_intensity,所有數據類型都是 float,儲存在…

深度學習之 RBF神經網絡

RBF神經網絡通常只有三層,即輸入層、中間層和輸出層。其中中間層主要計算輸入x和樣本矢量c(記憶樣本)之間的歐式距離的Radial Basis Function (RBF)的值,輸出層對其做一個線性的組合。 徑向基函數: RBF神經網絡的訓練…

redis 隊列_Redis與Rabbitmq消息隊列的區別

將redis發布訂閱模式用做消息隊列和rabbitmq的區別:可靠性 redis :沒有相應的機制保證消息的可靠消費,如果發布者發布一條消息,而沒有對應的訂閱者的話,這條消息將丟失,不會存在內存中;rabbit…