日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > windows >内容正文

windows

操作系统面试

發布時間:2023/12/31 windows 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 操作系统面试 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

  • 操作系統
    • 一 操作系統基礎
      • 1.1 什么是操作系統?
      • 1.2 系統調用
    • 二 進程和線程
      • 2.1 進程和線程的區別
      • 2.2 進程有哪幾種狀態?
      • 2.3 進程間的通信方式
      • 2.4 線程間的同步的方式
      • 2.5 進程的調度算法
    • 頁面置換算法
    • fork子進程發生什么
    • 僵尸進程和孤兒進程
      • 死鎖相關
    • 存儲器層次化結構
      • 程序訪問的局部性
      • 高速緩存Cache原理
      • Cache和內存映射方式
        • 直接映射
        • 全相聯映射
        • 組相聯映射
      • Cache的替換算法
      • Cache的一致性問題
        • 全寫法
        • 回寫法
      • 影響Cache的性能因素
      • 總結
      • 補充
        • 什么是用戶態和內核態?
        • **進程的PCB包含哪些信息**
          • 進程控制塊PCB的作用:
          • 進程控制塊中的信息:
    • 哪些時線程共享的,哪些是線程私有的

操作系統

一 操作系統基礎

1.1 什么是操作系統?

  • 操作系統(Operating System,簡稱 OS)是管理計算機硬件與軟件資源的程序,是計算機的基石。
  • 操作系統本質上是一個運行在計算機上的軟件程序 ,用于管理計算機硬件和軟件資源。 舉例:運行在你電腦上的所有應用程序都通過操作系統來調用系統內存以及磁盤等等硬件。
  • 操作系統存在屏蔽了硬件層的復雜性。 操作系統就像是硬件使用的負責人,統籌著各種相關事項。
  • 操作系統的內核(Kernel)是操作系統的核心部分,它負責系統的內存管理,硬件設備的管理,文件系統的管理以及應用程序的管理。 內核是連接應用程序和硬件的橋梁,決定著系統的性能和穩定性。
  • [外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-trsvy1Ab-1658936354687)(C:\Users\18458\Desktop\Kernel_Layout.png)]

    1.2 系統調用

    首先根據進程訪問資源的特點,可以把進程在系統上的運行分為兩個級別用戶態和系統態:

  • 用戶態(user mode) : 用戶態運行的進程可以直接讀取用戶程序的數據。
  • 系統態(kernel mode):可以簡單的理解系統態運行的進程或程序幾乎可以訪問計算機的任何資源,不受限制。
  • 我們運行的程序基本都是運行在用戶態,如果調用操作系統提供的系統態級別的子功能,那就需要系統調用了!

    也就是說在運行的用戶程序中,凡是與系統態級別的資源有關的操作(如文件管理、進程控制、內存管理等),都必須通過系統調用方式向操作系統提出服務請求,并由操作系統代為完成。

    這些系統調用按功能大致可分為如下幾類:

    • 設備管理。完成設備的請求或釋放,以及設備啟動等功能。
    • 文件管理。完成文件的讀、寫、創建及刪除等功能。
    • 進程控制。完成進程的創建、撤銷、阻塞及喚醒等功能。
    • 進程通信。完成進程之間的消息傳遞或信號傳遞等功能。
    • 內存管理。完成內存的分配、回收以及獲取作業占用內存區大小及地址等功能。

    二 進程和線程

    2.1 進程和線程的區別

    從 JVM 的角度來說一下線程和進程之間的關系!一個進程中可以有多個線程,多個線程共享進程的和**方法區 (JDK1.8 之后的元空間)*資源,但是每個線程有自己的*程序計數器、虛擬機棧本地方法棧

    總結: 線程是進程劃分成的更小的運行單位,一個進程在其執行的過程中可以產生多個線程。線程和進程最大的不同在于基本上各進程是獨立的,而各線程則不一定,因為同一進程中的線程極有可能會相互影響。線程執行開銷小,但不利于資源的管理和保護;而進程正相反。

    進程

    • 程序由指令和數據組成,但這些指令要運行,數據要讀寫,就必須將指令加載至 CPU,數據加載至內存。在指令運行過程中還需要用到磁盤、網絡等設備。進程就是用來加載指令、管理內存、管理 IO 的。
    • 當一個程序被運行,從磁盤加載這個程序的代碼至內存,這時就開啟了一個進程。
    • 進程就可以視為程序的一個實例。大部分程序可以同時運行多個實例進程(例如記事本、畫圖、瀏覽器 等),也有的程序只能啟動一個實例進程(例如網易云音樂、360 安全衛士等)

    線程

    • 一個進程之內可以分為一到多個線程。
    • 一個線程就是一個指令流,將指令流中的一條條指令以一定的順序交給 CPU 執行 。
    • Java 中,線程作為小調度單位,進程作為資源分配的小單位。 在 windows 中進程是不活動的,只是作 為線程的容器

    二者對比

    • 進程基本上相互獨立的,而線程存在于進程內,是進程的一個子集 進程擁有共享的資源,如內存空間等,供其內部的線程共享
      • 進程間通信較為復雜 同一臺計算機的進程通信稱為 IPC(Inter-process communication)
      • 不同計算機之間的進程通信,需要通過網絡,并遵守共同的協議,例如 HTTP
    • 線程通信相對簡單,因為它們共享進程內的內存,一個例子是多個線程可以訪問同一個共享變量 線程更輕量,線程上下文切換成本一般上要比進程上下文切換低

    進程和線程的切換

    上下文切換

    內核為每一個進程維持一個上下文。**上下文就是內核重新啟動一個被搶占的進程所需的狀態。**包括以下內容:

    • 通用目的寄存器
    • 浮點寄存器
    • 程序計數器
    • 用戶棧
    • 狀態寄存器
    • 內核棧
    • 各種內核數據結構:比如描繪地址空間的頁表,包含有關當前進程信息的進程表,以及包含進程已打開文件的信息的文件表

    進程切換和線程切換的主要區別

    最主要的一個區別在于進程切換涉及虛擬地址空間的切換而線程不會。因為每個進程都有自己的虛擬地址空間,而線程是共享所在進程的虛擬地址空間的,因此同一個進程中的線程進行線程切換時不涉及虛擬地址空間的轉換

    頁表查找是一個很慢的過程,因此通常使用cache來緩存常用的地址映射,這樣可以加速頁表查找,這個cache就是快表TLB(translation Lookaside Buffer,用來加速頁表查找)。由于每個進程都有自己的虛擬地址空間,那么顯然每個進程都有自己的頁表,那么當進程切換后頁表也要進行切換,頁表切換后TLB就失效了,cache失效導致命中率降低,那么虛擬地址轉換為物理地址就會變慢,表現出來的就是程序運行會變慢,而線程切換則不會導致TLB失效,因為線程線程無需切換地址空間,因此我們通常說線程切換要比較進程切換快

    而且還可能出現缺頁中斷,這就需要操作系統將需要的內容調入內存中,若內存已滿則還需要將不用的內容調出內存,這也需要花費時間

    為什么TLB能加快訪問速度

    快表可以避免每次都對頁號進行地址的有效性判斷。快表中保存了對應的物理塊號,可以直接計算出物理地址,無需再進行有效性檢查

    2.2 進程有哪幾種狀態?

    一般把進程大致分為 5 種狀態,和線程很像

    • 創建狀態(new) :進程正在被創建,尚未到就緒狀態。
    • 就緒狀態(ready) :進程已處于準備運行狀態,即進程獲得了除了處理器之外的一切所需資源,一旦得到處理器資源(處理器分配的時間片)即可運行。
    • 運行狀態(running) :進程正在處理器上上運行(單核 CPU 下任意時刻只有一個進程處于運行狀態)。
    • 阻塞狀態(waiting) :又稱為等待狀態,進程正在等待某一事件而暫停運行如等待某資源為可用或等待 IO 操作完成。即使處理器空閑,該進程也不能運行。
    • 結束狀態(terminated) :進程正在從系統中消失。可能是進程正常結束或其他原因中斷退出運行。

    [^訂正:下圖中 running 狀態被 interrupt 向 ready 狀態轉換的箭頭方向反了。]:

    [外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-6ouDbsYw-1658936354688)(C:\Users\18458\Desktop\d38202593012b457debbcd74994c6292.png)]

    2.3 進程間的通信方式

  • 管道(Pipes) :分為匿名管道和命名管道。對于匿名管道,它的通信范圍是存在父子關系的進程。因為管道沒有實體,也就是沒有管道文件,只能通過 fork 來復制父進程 fd 文件描述符,來達到通信的目的。對于命名管道,它可以在不相關的進程間也能相互通信。因為命令管道,提前創建了一個類型為管道的設備文件,在進程里只要使用這個設備文件,就可以相互通信。不管是匿名管道還是命名管道,進程寫入的數據都是緩存在內核中,另一個進程讀取數據時候自然也是從內核中獲取,同時通信數據都遵循先進先出原則。

  • 消息隊列(Message Queuing) :管道的通信方式是效率低的,因此管道不適合進程間頻繁地交換數據。對于這個問題,消息隊列的通信模式就可以解決。比如,A 進程要給 B 進程發送消息,A 進程把數據放在對應的消息隊列后就可以正常返回了,B 進程需要的時候再去讀取數據就可以了。同理,B 進程要給 A 進程發送消息也是如此。消息隊列是保存在內核中的消息鏈表,如果進程從消息隊列中讀取了消息體,內核就會把這個消息體刪除。消息隊列生命周期隨內核,如果沒有釋放消息隊列或者沒有關閉操作系統,消息隊列會一直存在,而前面提到的匿名管道的生命周期,是隨進程的創建而建立,隨進程的結束而銷毀。但存在兩個不足:

  • 消息隊列不適合比較大數據的傳輸,因為在內核中每個消息體都有一個最大長度的限制,同時所有隊列所包含的全部消息體的總長度也是有上限。
  • 消息隊列通信過程中,存在用戶態與內核態之間的數據拷貝開銷,因為進程寫入數據到內核中的消息隊列時,會發生從用戶態拷貝數據到內核態的過程,同理另一進程讀取內核中的消息數據時,會發生從內核態拷貝數據到用戶態的過程。
  • 共享內存(Shared memory) :消息隊列的讀取和寫入的過程,都會有發生用戶態與內核態之間的消息拷貝過程。那共享內存的方式,就很好的解決了這一問題。

    現代操作系統,對于內存管理,采用的是虛擬內存技術,也就是每個進程都有自己獨立的虛擬內存空間,不同進程的虛擬內存映射到不同的物理內存中。所以,即使進程 A 和 進程 B 的虛擬地址是一樣的,其實訪問的是不同的物理內存地址,對于數據的增刪查改互不影響。

    共享內存的機制,就是拿出一塊虛擬地址空間來,映射到相同的物理內存中。這樣這個進程寫入的東西,另外一個進程馬上就能看到了,都不需要拷貝來拷貝去,傳來傳去,大大提高了進程間通信的速度。
    [外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-iFsCRSrv-1658936354689)(C:\Users\18458\Desktop\圖片\虛擬內存.png)]

  • 信號量(Semaphores) :用了共享內存通信方式,帶來新的問題,那就是如果多個進程同時修改同一個共享內存,很有可能就沖突了。例如兩個進程都同時寫一個地址,那先寫的那個進程會發現內容被別人覆蓋了。

    為了防止多進程競爭共享資源,而造成的數據錯亂,所以需要保護機制,使得共享的資源,在任意時刻只能被一個進程訪問。正好,信號量就實現了這一保護機制。

    信號量其實是一個整型的計數器,主要用于實現進程間的互斥與同步,而不是用于緩存進程間通信的數據。

  • 信號(Signal) :信號是進程間通信機制中唯一的異步通信機制,信號可以在應用進程和內核之間直接交互,內核也可以利用信號來通知用戶空間的進程發生了哪些系統事件,信號事件的來源主要有硬件來源(如鍵盤 Cltr+C ,Ctrl+C 產生 SIGINT 信號,表示終止該進程,Ctrl+Z 產生 SIGTSTP 信號,表示停止該進程,但還未結束;)和軟件來源(如 kill 命令,進程發送 SIGKILL 信號,用來立即結束該進程)。**

  • 套接字(Sockets) : 前面提到的管道、消息隊列、共享內存、信號量和信號都是在同一臺主機上進行進程間通信,那要想跨網絡與不同主機上的進程之間通信,就需要 Socket 通信了,可根據創建 Socket 的類型不同,分為三種常見的通信方式,一個是基于 TCP 協議的通信方式,一個是基于 UDP 協議的通信方式,一個是本地進程間通信方式。

  • 2.4 線程間的同步的方式

    線程同步是兩個或多個共享關鍵資源的線程的并發執行。應該同步線程以避免關鍵的資源使用沖突。操作系統一般有下面三種線程同步的方式:

  • 互斥量(Mutex):采用互斥對象機制,只有擁有互斥對象的線程才有訪問公共資源的權限。因為互斥對象只有一個,所以可以保證公共資源不會被多個線程同時訪問。比如 Java 中的 synchronized 關鍵詞和各種 Lock 都是這種機制。
  • 信號量(Semaphore) :它允許同一時刻多個線程訪問同一資源,但是需要控制同一時刻訪問此資源的最大線程數量。
  • 事件(Event) :Wait/Notify:通過通知操作的方式來保持多線程同步,還可以方便的實現多線程優先級的比較操作。
  • 處理機
    處理機是計算機系統中存儲程序和數據,并按照程序規定的步驟執行指令的部件。程序是描述處理機完成某項任務的指令序列。指令則是處理機能直接解釋、執行的信息單位。處理機包括中央處理器(cpu),主存儲器,輸入-輸出接口。處理機加接外圍設備就構成完整的計算機系統

    cpu
    中央處理器(CPU,Central Processing Unit)是一塊超大規模的集成電路,是一臺計算機的運算核心(Core)和控制核心( Control Unit)。它的功能主要是解釋計算機指令以及處理計算機軟件中的數據。

    處理機和cpu處理器的區別
    1、指代不同
    處理機:是處理計算機系統中存儲程序和數據,并按照程序規定的步驟執行指令的部件。

    cpu處理器:作為計算機系統的運算和控制核心,是信息處理、程序運行的最終執行單元。

    2、構成不同
    處理機:包括中央處理器,主存儲器,輸入-輸出接口,加接外圍設備就構成完整的計算機系統。

    cpu處理器:主要包括兩個部分,即控制器、運算器,其中還包括高速緩沖存儲器及實現它們之間聯系的數據、控制的總線。

    2.5 進程的調度算法

    • 先到先服務(FCFS)調度算法 : 從就緒隊列中選擇一個最先進入該隊列的進程為之分配資源,使它立即執行并一直執行到完成或發生某事件而被阻塞放棄占用 CPU 時再重新調度。
      • 優先級調度 : 為每個流程分配優先級,首先執行具有最高優先級的進程,依此類推。具有相同優先級的進程以 FCFS 方式執行。可以根據內存要求,時間要求或任何其他資源要求來確定優先級。
      • 時間片輪轉調度算法 : 時間片輪轉調度是一種最古老,最簡單,最公平且使用最廣的算法,又稱 RR(Round robin)調度。每個進程被分配一個時間段,稱作它的時間片,即該進程允許運行的時間。
    • 短進程優先(SJF)的調度算法 : 從就緒隊列中選出一個估計運行時間最短的進程為之分配資源,使它立即執行并一直執行到完成或發生某事件而被阻塞放棄占用 CPU 時再重新調度。
    • 最短剩余時間優先調度算法:最短剩余時間優先調度算法是將短進程優先調度算法用于分時環境的變形。其基本思想是,讓“運行到任務完成時所需的運行時間最短”的進程優先得到處理,包括新進入系統的進程。在最短進程優先調度算法中,一個進程一旦得到處理機,就一直運行到任務完成(或等待事件)而不能被剝奪(除非主動讓出處理機)。而最短剩余時間優先調度算法允許被一個新進入系統的且其運行時間短于當前運行進程的剩余運行時間的進程所搶占。
      該算法的優點是,可以用于分時系統,保證及時響應用戶要求。缺點是,系統開銷增加。首先,要保存進程的運行情況記錄,以比較其剩余時間長短:其次,剝奪本身也要消耗處理機時間。毫無疑問,這個算法使短進程一進入系統就能立即得到服務,從而縮短進程的平均等待時間。
      最高響應比優先調度算法:每個進程都有一個動態優先數,該優先數不但是要求的服務時間的函數,而且是該進程得到服務所花費的等待時間的函數。進程的動態優先數計算公式如下:
      優先數=(等待時間+要求的服務時間)/要求的服務時間
      要求的服務時間是分母,所以對短進程是有利的,因為區的優先數高,可優先運行。另外,因為等待時間是分子,所以長進程由于其等待了較長時間,從而提高了其優先致,終于得到了處理機。進程一旦得到處理機,它就一直運行到進程完成任務(或因等待事件而主動讓出處理機),中間不被搶占。
    • 多級反饋隊列調度算法 :前面介紹的幾種進程調度的算法都有一定的局限性。如短進程優先的調度算法,僅照顧了短進程而忽略了長進程 。多級反饋隊列調度算法既能使高優先級的作業得到響應又能使短作業(進程)迅速完成。,因而它是目前被公認的一種較好的進程調度算法,UNIX 操作系統采取的便是這種調度算法。
    • 系統中有多個進程就緒隊列,每個就緒隊列對應一個調度級別。第1級隊列的優先級最高,以下各級隊列的優先級逐次降低。調度時,選擇高優先級隊列中的第1個就緒進程。各級隊列中的進程具有不同的時間片值。優先級最高的第1級隊列中的進程的時間片值最少;題看隊列級別的增高,其進程的優先級降低了,但時間片值卻增大了。通常,下放一級,其時間片值增大1倍。各級隊列均按先來先服務原則排序。

    頁面置換算法

    1、最優頁面置換算法;2、NRU最近未使用算法;3、FIFO先進先出頁面置換算法;4、第二次機會頁面置換算法;5、時鐘頁面置換算法;6、LRU最近最少使用頁面置換算法;7、NFU最不經常使用算法;8、老化算法;9、工作集頁面置換算法;10、工作集時鐘頁面置換算法

    fork子進程發生什么

    先說一下fork,fork會生成一個和當前進程相同的副本,稱為子進程。fork之后,操作系統會copy當前進程的task_struct機構體,除了id號不一樣之外,其余完全一樣。原進程的所有資源都以適當的方式復制到子進程,因此該系統調用之后,原來的進程就有了兩個獨立的實例。這兩個實例的聯系包括:同一組打開文件、同樣的工作目錄、內存中同樣的數據(兩個進程各有一份副本)。
    其實主要就這三個區別:
    1.fork之后父子進程將共享代碼文本段,但是各自擁有不同的棧段、數據段及堆段拷貝。子進程的棧、數據從fork一瞬間開始是對于父進程的完全拷貝、每個進程可以更改自己的數據,而不要擔心相互影響!
    2.fork之后父子進程同時開始從fork點向下執行代碼,具體fork之后CPU會調度到誰?不一定!
    3.執行fork之后,子進程將拷貝父進程的文件描述符副本,指向同一個文件句柄(包含了當前文件讀寫的偏移量等信息)。

    僵尸進程和孤兒進程

    孤兒進程:一個父進程退出,而它的一個或多個子進程還在運行,那么那些子進程將成為孤兒進程。孤兒進程將被init進程(進程號為1)所收養,并由init進程對它們完成狀態收集工作。
    僵尸狀態是一個比較特殊的狀態,當進程退出父進程(使用wait()系統調用)沒有讀取到子進程退出的返回代碼時就會產生僵尸進程。僵尸進程會在以終止狀態保持在進程表中,并且會一直等待父進程讀取退出狀態代碼。
    僵尸進程與孤兒進程的區別:
    孤兒進程是子進程還在運行,而父進程掛了,子進程被init進程收養。僵尸進程是父進程還在運行但是子進程掛了,但是父進程卻沒有使用wait來清理子進程的進程信息,導致子進程雖然運行實體已經消失,但是仍然在內核的進程表中占據一條記錄,這樣長期下去對于系統資源是一個浪費。僵尸進程將會導致資源浪費,而孤兒則不會

    死鎖相關

    存儲器層次化結構

    存儲器有很多種類,我們常見的有內存、磁盤,還有平時看不到的集成在CPU內部的寄存器、高速緩存等。

    正常來說,存儲器的容量和性能應該伴隨著CPU的速度和性能提升而提升,以匹配CPU的數據處理。但隨著時間的推移,CPU和存儲器在性能上的發展差異越來越大,存儲器在性能增長越來越跟不上CPU性能發展的需要。

    那怎么辦呢?

    為了縮小存儲器和CPU之間的性能差距,通常在計算機內部采用層次化的存儲器體系結構,以此來發揮出存儲器的綜合性能。

    存儲器層次化結構如下:

    最上層的是寄存器,存取時間極快,但容量小。其次是高速緩存,存取時間次之,容量比寄存器大一些。再往下就是我們常見的內存、硬盤,存取速度遞減,但容量越來越大。

    CPU在訪問數據時,數據一般在相鄰兩層之間復制傳送,且總是從慢速存儲器復制到快速存儲器,通過這種方式保證CPU的速度和存儲器的速度相匹配。

    程序訪問的局部性

    最早期的計算機,在執行一段程序時,都是把硬盤中的數據加載到內存,然后CPU從內存中取出代碼和數據執行,在把計算結果寫入內存,最終輸出結果。

    其實這么干,本身沒有什么問題,但后來程序運行越來越多,就發現一個規律:內存中某個地址被訪問后,短時間內還有可能繼續訪問這塊地址。內存中的某個地址被訪問后,它相鄰的內存單元被訪問的概率也很大。

    人們發現的這種規律被稱為程序訪問的局部性

    程序訪問的局部性包含2種:

    • 時間局部性:某個內存單元在較短時間內很可能被再次訪問
    • 空間局部性:某個內存單元被訪問后相鄰的內存單元較短時間內很可能被訪問

    出現這種情況的原因很簡單,因為程序是指令和數據組成的,指令在內存中按順序存放且地址連續,如果運行一段循環程序或調用一個方法,又或者再程序中遍歷一個數組,都有可能符合上面提到的局部性原理。

    那既然在執行程序時,內存的某些單元很可能會經常的訪問或寫入,那可否在CPU和內存之間,加一個緩存,CPU在訪問數據時,先看一下緩存中是否存在,如果有直接就讀取緩存中的數據即可。如果緩存中不存在,再從內存中讀取數據。

    事實證明利用這種方式,程序的運行效率會提高90%以上,這個緩存也叫做高速緩存Cache

    高速緩存Cache原理

    高速緩存Cache是非常小容量的存儲器,它集成在CPU芯片內。為了便于CPU、高速緩存Cache、內存之間的信息交換,內存按塊劃分,高速緩存Cache按行或槽劃分。

    CPU對內存、高速緩存Cache進行數據訪問的流程如圖:

    CPU先查詢Cache中是否有數據,如果有,直接讀取即可。

    如果Cache中沒有,則從內存中讀取數據,同時把數據放入Cache中,然后把數據返回給CPU。

    整個流程其實很簡單,但對于Cache和內存信息的交換,需要考慮一些問題:

    • 對于CPU讀取數據,如果Cache中沒有數據,從內存中讀取數據后,如何分配到Cache中?
    • 如果Cache滿了,采用什么策略替換?
    • 對于CPU寫入數據,如何保證Cache和內存數據的一致性?

    對于這3個問題,下面依次來分析是如何解決的。

    Cache和內存映射方式

    對于第一個問題,Cache中沒有命中數據時,內存數據是如何分配到Cache中的。

    由于內存的容量比Cache容量要大,兩者之間的容量不匹配,所以內存數據填充到Cache中,就需要設計一種規則來保證Cache的利用率最大,保證CPU訪問Cache的命中率最高。

    內存到Cache的映射規則有3種方式:

    • 直接映射:每個內存塊數據只映射到固定的緩存行中
    • 全相聯映射:每個內存塊數據可以映射到任意緩存行中
    • 組相聯映射:每個內存塊數據可以映射到固定組任意緩存行中

    下面我們分別來看這3種映射方式。

    直接映射

    訪問內存數據會給出一個內存地址,首先把這個內存地址,按位劃分為3個字段:標記、Cache行號、塊內地址,如圖:

    然后根據第2個字段的二進制位進行取模運算,得到對應的Cache行號。

    找到對應的Cache號后,校驗Cache的有效位,如果有效,再比較內存第1個字段的標記與Cache的標記是否一致,如果一致,直接獲取Cache中的數據即可。

    如果有效位無效,或有效位有效但內存第1個字段的標記與Cache的標記不一致,那么根據內存地址去內存獲取數據,然后把對應的Cache行有效位設置為有效,標記設置為與內存標記一致,并在Cache中記錄內存的數據,以便下次獲取。

    具體映射關系如圖:

    可見Cache與內存的映射可能是一對多的,即不同內存塊可能映射到同一Cache行。

    這種映射方式比較簡單粗暴,如果緩存不命中或內存和Cache標識不一致,就會替換Cache行中的數據。這就可能導致同一Cache行在短時間內被頻繁替換,命中率不高。

    全相聯映射

    全相聯映射與直接映射方式不同的是,它把內存分成2個字段:標記、塊內地址,沒有了Cache行號這個字段。

    在訪問數據時,直接根據內存地址中的標記,去直接遍歷對比每一個Cache行,直到找到一致的標記的Cache行,然后訪問Cache中的數據即可。

    如果遍歷完Cache行后,沒有找到一致的標記,那么會從內存中獲取數據,然后找到空閑的Cache行,直接寫入標記和數據即可。

    也就是說,這種映射方式,就是哪里有空閑的Cache行,我就把內存塊映射到這個Cache行中。在訪問時,依次遍歷Cache行,直到找到標記一直的Cache行,然后讀取數據。

    這種方式雖然在空間利用率上保證最大化,但其缺點在于要在Cache中尋找符合標識一致的行的時間要比直接映射的時間久,效率較低。

    那有什么方式能集合上面2種方式,發揮各自的優勢呢?這就是下面要說的組相聯映射方式。

    組相聯映射

    組相聯映射方式把內存也分為3個字段:標記、Cache組號、塊內地址

    注意,與直接映射不同的是,第2個字段是組號而不是行號。這種方式把Cache行先進行分組,然后每個分組中包含多個Cache行,如圖:

    在訪問數據時,先根據內存地址中的Cache組號,定位到Cache的分組,然后在這個組內,依次遍歷每個行,尋找標記一致的Cache行,如果標記一致則獲取數據,不一致則從內存中獲取數據后寫入當前組內空閑的任意一個Cache行中。

    這種方式兼顧了訪問速度和空間利用率,使用前2種方式結合的方案,保證緩存命中率最大化。在現實中實際上采用的這種映射方式。

    Cache的替換算法

    對于上面提的第2個問題,如果Cache滿了,如何進行替換?

    Cache容量比內存小,所以內存數據映射到Cache時,必然會導致Cache滿的情況,那之后的內存映射要替Cache中的哪些行呢?這就需要制定一種策略。

    常見的替換算法有如下幾種:

    • 先進先出算法(FIFO):總是把最早裝入Cache的行替換掉,這種算法實現簡單,但不能正確反映程序的訪問局部性,命中率不高
    • 最近最少使用算法(LRU):總是選擇最近最少使用的Cache行替換,這種這種算法稍微復雜一些,但可以正確反映程序訪問的局部性,命中率最高
    • 最不經常使用算法(LFU):總是替換掉Cache中引用次數最少的行,與LRU類似,但沒有LRU替換方式更精準
    • 隨機替換算法(Random):隨機替換掉Cache中的行,與使用情況無關,命中率不高

    現實使用最多的是最近最少使用算法(LRU)進行Cache行的替換方案,這種方案使得緩存的命中率最高。

    Cache的一致性問題

    上面提的第3個問題,對于寫入的數據,如何保證Cache和內存數據的一致性?

    試想,如果CPU想要修改某個內存的數據,這塊內存的數據剛好在Cache中存在,那么是不是要同時更新Cache中的數據?

    這個寫入數據的過程,通常采用2種方式:

    • 全寫法(通寫法/直寫法/寫直達法)
    • 回寫法(寫回法)

    全寫法

    在寫操作時,如果Cache命中,則同時寫Cache和內存。

    如果Cache中不命中,則分為以下2種情況:

    • 寫分配法:先更新內存數據,然后再寫入空閑的Cache行中,保證Cache有數據,提高了緩存命中率,但增加了寫入Cache的開銷
    • 非寫分配法:只更新內存數據,不寫入Cache,只有等訪問不命中時,再進行緩存寫入

    另外,這種方式為了減少內存的寫入開銷,一般會在Cache和內存之間加一個寫緩沖隊列,在CPU寫入Cache的同時,也會寫入緩沖隊列,然后由存儲控制器將緩沖隊列寫入內存。

    如果在寫操作不頻繁的情況下,效果很好。但如果寫操作頻繁,則會導致寫緩沖隊列飽和而發生阻塞。

    回寫法

    這種方式在寫操作時,如果Cache命中,則只更新Cache而不更新內存。

    如果Cache不命中,則從內存中讀取內容,寫入Cache并更新為最新內容。

    這種方式不會主動更新內存,只有在Cache被再次修改時,才將內容一次性寫入內存。這樣做的好處是減少了寫內存的次數,大大降低內存帶寬需求。但有可能在某個時間點,Cache和內存中的數據會出現不一致的情況。

    影響Cache的性能因素

    既然Cache在CPU訪問數據時提升的效率這么高,那決定Cache性能的因素有哪些?

    決定訪問性能的重要因素之一就是Cache的命中率,它與許多因素有關,具體涉及如下:

    • Cache容量:容量越大,緩存數據越多,命中率越高
    • 內存塊大小:大的內存交換單位能更好地利用空間局部性,但過大也會導致命中率降低,必須適中

    除此之外,如何設計Cache也會影響到它的性能:

    • 多級Cache:現在的CPU會采用3級Cache,最大程度的提升命中率
    • 內存、總線、Cache連接結構:設計一個效率高的傳輸通道,能夠提升Cache的訪問速度
    • 內存結構與Cache配合:在訪問不命中時,會去訪問內存,設計效率高的傳輸通道與Cache配合也可以提升Cache的性能

    總結

    本篇文章主要介紹了高速緩存Cache的重點知識,總結如下:

    • 程序運行有訪問局部性的規律:時間局部性、空間局部性
    • 內存與Cache的映射方式有3種:直接映射、全相聯映射、組相聯映射,其中組相聯映射方式命中率最高
    • Cache的替換算法有4種:先進先出(FIFO)、最近最少使用(LRU)、最不經常使用(LFU)、隨機(Random),其中最近最少使用算法的命中率最高
    • 保證內存與Cache的一致性方案有2種:全寫法、回寫法
    • 影響Cache的性能因素有:容量、內存塊大小、Cache組合、內存結構與傳輸通道設計等

    補充

    什么是用戶態和內核態?

    Kernel 運行在超級權限模式(Supervisor Mode)下,所以擁有很高的權限。按照權限管理的原則,多數應用程序應該運行在最小權限下。因此,很多操作系統,將內存分成了兩個區域:內核空間(Kernal Space),這個空間只有內核程序可以訪問;用戶空間(User Space),這部分內存專門給應用程序使用。

    用戶態和內核態
    用戶空間中的代碼被限制了只能使用一個局部的內存空間,我們說這些程序在用戶態(User Mode) 執行。
    內核空間中的代碼可以訪問所有內存,我們稱這些程序在內核態(Kernal Mode) 執行。

    用戶態線程
    用戶態線程也稱作用戶級線程(User Level Thread)。操作系統內核并不知道它的存在,它完全是在用戶空間中創建。

    完全建立在用戶空間,內核無法感知線程的存在,內核只對線程所屬的進程進行控制。優點是不依賴內核實現,但用戶需要在用戶態實現線程創建和調度功能,一般只在不支持多線程的操作系統上使用,實現并發。

    用戶級線程有很多優勢,比如。

    管理開銷小:創建、銷毀不需要系統調用。

    切換成本低:用戶空間程序可以自己維護,不需要走操作系統調度。

    但是這種線程也有很多的缺點。

    與內核協作成本高:比如這種線程完全是用戶空間程序在管理,當它進行 I/O 的時候,無法利用到內核的優勢,需要頻繁進行用戶態到內核態的切換。

    線程間協作成本高:設想兩個線程需要通信,通信需要 I/O,I/O 需要系統調用,因此用戶態線程需要支付額外的系統調用成本。

    無法利用多核優勢:比如操作系統調度的仍然是這個線程所屬的進程,所以無論每次一個進程有多少用戶態的線程,都只能并發執行一個線程,因此一個進程的多個線程無法利用多核的優勢。

    操作系統無法針對線程調度進行優化:當一個進程的一個用戶態線程阻塞(Block)了,操作系統無法及時發現和處理阻塞問題,它不會更換執行其他線程,從而造成資源浪費。

    內核態線程
    內核態線程也稱作內核級線程(Kernel Level Thread)。這種線程執行在內核態,可以通過系統調用創造一個內核級線程。

    由內核通過調度器來完成線程切換,優點是每個線程是獨立的調度單元,線程之間互不影響,但缺點是線程創建和切換開銷大

    內核級線程有很多優勢

    可以利用多核 CPU 優勢:內核擁有較高權限,因此可以在多個 CPU 核心上執行內核線程。

    操作系統級優化:內核中的線程操作 I/O 不需要進行系統調用;一個內核線程阻塞了,可以立即讓另一個執行。

    當然內核線程也有一些缺點。

    創建成本高:創建的時候需要系統調用,也就是切換到內核態。

    擴展性差:由一個內核程序管理,不可能數量太多。

    切換成本較高:切換的時候,也同樣存在需要內核操作,需要切換內核態。

    用戶態和內核態的區別
    用戶態線程工作在用戶空間,內核態線程工作在內核空間。用戶態線程調度完全由進程負責,通常就是由進程的主線程負責。相當于進程主線程的延展,使用的是操作系統分配給進程主線程的時間片段。內核線程由內核維護,由操作系統調度。

    用戶態線程無法跨核心,一個進程的多個用戶態線程不能并發,阻塞一個用戶態線程會導致進程的主線程阻塞,直接交出執行權限。這些都是用戶態線程的劣勢。內核線程可以獨立執行,操作系統會分配時間片段。因此內核態線程更完整,也稱作輕量級進程。內核態線程創建成本高,切換成本高,創建太多還會給調度算法增加壓力,因此不會太多。

    混合實現

    即存在用戶線程,也存在輕量級進程,支持大規模的用戶線程并發,同時使用內核線程進行調度和處理映射。用戶線程和內核線程的映射有 1:1,M:N 兩種形式,Java 線程為 1:1 的形式,Go 的 goroutine 為 M:N 的形式

    用戶態線程創建成本低,問題明顯,不可以利用多核。內核態線程,創建成本高,可以利用多核,切換速度慢。因此通常我們會在內核中預先創建一些線程,并反復利用這些線程。將用戶態線程附著在內核態線程中執行

    進程的PCB包含哪些信息

    為了使參與并發執行的每個程序,包含數據都能獨立地運行,在操作系統中必須為之配置一個專門的數據結構,稱為進程控制塊(PCB,Process Control Block)。進程與PCB是一一對應的,用戶進程不能修改。

    進程控制塊PCB的作用:

    為了便于系統描述和管理進程的運行,在OS的核心為每個進程專門定義了一個數據結構——進程控制塊PCB(Process Control Block)。PCB作為進程實體的一部分,記錄了操作系統所需的,用于描述進程的當前情況以及管理進程運行的全部信息,是操作系統中最重要的記錄型數據結構。PCB的作用是使一個在多道程序環境下不能獨立運行的程序(含數據)成為一個能獨立運行的基本單位,一個能與其他進程并發執行的進程。
    **(1)PCB作為獨立運行基本單位的標志。**當一個程序(含數據)配置了PCB后,就表示它已經是一個能在多道程序環境下獨立運行的、合法的基本單位,也就具有取得OS服務的權力,如打開文件系統中的文件,請求獲得系統中的I/O設備,以及與其它相關進程的進行通信等。因此,當系統創建一個新進程時,就為它建立了一個PCB。進程結束時又回收其PCB,進程于是也隨之消亡。系統是通過PCB感知進程的存在的。事實上,PCB已成為進程存在于系統中的唯一標志。
    (2)PCB能實現間斷性運行方式。在多道程序環境下,程序是采用停停走走間斷性的運行方式運行的。當進程因阻塞而暫停運行時,它必須保留自己運行時的CPU現場信息。在有了PCB后,系統就可以將CPU現場信息保存在被中斷進程的PCB中,供該進程再次被調度執行時恢復CPU現場時使用。由此,可再次明確,在多道程序環境下,作為傳統意義上的靜態程序,因其并不具有保護或保存自己運行現場的手段,無法保證其運行結果的可再現性,從而失去運行的意義。
    **(3)PCB提供進程管理所需要的信息。**當調度程序調度到某進程運行時,只能根據該進程PCB中記錄的程序和數據在內存或外存中的始址指針,找到相應的程序和數據;在進程運行過程中,當需要訪問文件系統中的文件或I/O設備時,也都需要借助于PCB中的信息。另外,還可根據PCB中的資源清單了解到該進程所需的全部資源等。可見,在進程的整個生命周期中,操作系統總是根據PCB實施對進程的控制和管理。
    (4)PCB提供進程調度所需要的信息。只有處于就緒狀態的進程才能被調度執行,而在PCB中就提供了進程出于何種狀態的信息。如果進程處于就緒狀態,系統便將它插入到進程就緒隊列中,等待著調度程序的調度;另外在進行調度時往往還需要了解進程的其他信息,如在優先級調度算法中,就需要知道進程的優先級。在有些較為公平的調度算法中,還需要知道進程的等待時間和已執行過的事件等。
    (5)PCB實現與其他進程的同步與通信。進程同步機制是用于實現諸進程的協調運行的,在采用信號量機制時,它要求在每個進程中都設置有相應的用于同步的信號量。在PCB中還具有用于實現進程通信的區域或通信隊列指針等。

    進程控制塊中的信息:

    為了實現進程模型,操作系統維護著進程表。改表項包含了進程狀態的重要信息,包括程序計數器、堆棧指針、內存分配狀況、所打開文件的狀態、賬號和調度信息,以及其他進程由運行態轉換到就緒態或阻塞態時必須保存的信息,從而保證該進程隨后能再次啟動,就像從未被中斷一樣。

    哪些時線程共享的,哪些是線程私有的

    在進程控制塊中,主要包括以下幾個方面的信息:
    (1)進程標識符:進程標識符用于唯一的表示一個進程。一個進程通常有兩種標識符:①外部標識符。為了方便用戶進程對進程的訪問,須為每一個進程設置一個外部標識符。它是由創建者提供的,通常由字母、數字組成。為了描述進程的家族關系,還應設置父進程標識及子進程標識。此外,還可設置用戶標識,以指示擁有該進程的用戶。②內部標識符。為了方便系統對進程的使用,在OS中又為進程設置了內部標識符,即賦予每一個進程一個唯一的數字標識符,它通常是一個進程的序號。
    (2)處理機狀態:處理機狀態信息也稱為處理機的上下文,主要是由處理機的各種寄存器中的內容組成的。這些寄存器包括:①通用寄存器,又稱為用戶可視寄存器,它們是用戶程序可以訪問的,用于暫存信息,在大多數處理機中,有8~32個通用寄存器,在RISC結構的計算機中可超過100個;②指令計數器,其中存放了要訪問的下一條指令的地址;③程序狀態字PSW,其中含有狀態信息,如條件碼、執行方式、中斷屏蔽標志等;④用戶棧指針,指每個用戶進程都有一個或若干個與之相關的系統棧,用于存放過程和系統調用參數及調用地址。棧指針指向該棧的棧頂。處理機處于執行狀態時,正在處理的許多信息都是放在寄存器中。當進程被切換時,處理機狀態信息都必須保存在相應的PCB中,以便在該進程重新執行是能再從斷點繼續執行。
    (3)進程調度信息:在OS進行調度時,必須了解進程的狀態及有關進程調度的信息,這些信息包括:①進程狀態,指明進程的當前狀態,它是作為進程調度和對換時的依據;②進程優先級,是用于描述進程使用處理機的優先級別的一個整數,優先級高的進程應優先獲得處理機;③進程調度所需的其他信息,它們與所采用的進程調度算法有關,比如,進程已等待CPU的時間總和、進程已執行的時間總和等等;④事件,是指進程由執行態轉變為阻塞狀態所等待發生的事件,即阻塞原因。
    (4)進程控制信息:是指用于進程控制所必須的信息,它包括:①程序和數據的地址,進程實體中的程序和數據的內存或外存地址,以便再調度到該進程執行時,能從PCB中找到其程序和數據;②進程同步和通信機制,這是實現同步和進程通信時必需的機制,如消息隊列指針、信號量等,它們可能全部或部分地放在PCB中;③資源清單,在該清單中列出了進程在運行期間所需的全部資源(除CPU),另外還有一張已分配到該進程的資源的清單;④鏈接指針,它給出了本進程(PCB)所在隊列中的下一個進程地PCB的首地址。

    總結

    以上是生活随笔為你收集整理的操作系统面试的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。