軟件開發搞定操作系統

1. 前言

操作系統是管理計算機硬件和軟件資源的計算機程序。比如管理配置內存、決定資源供需順序、控制輸入輸出設備,提供用戶交互等。

操作系統並不侷限於存在在計算機,手機也是有操作系統的。常見的操作系統有:Android、iOS、Windos、Linux、MacOS。

我們不可能直接操作計算機硬件,而且像以前的計算機中編寫的程序並不是可以在任何計算機上可以運行,所以就需要一個通用的環境,提出了操作系統。有了操作系統,普通人也可以方便使用。

在這裏插入圖片描述

2. 操作系統的演進

1.無操作系統的時代

  • 人工操作:就是人工把編寫的代碼或數據轉成二進制給計算機,然後使用穿孔紙帶(帶孔的設爲1,無孔設爲0),經過光電掃描輸入電腦。要是有一個錯誤或者想改寫代碼/數據就得重新打印紙帶。
    在這裏插入圖片描述
    其實那時候一般都是專業程序員搞的,並不是普通人就可以玩的。而且每次讀取一條紙帶就得佔用一臺計算機。CPU等人工操作。每次紙帶讀取完還得人工去拿下一條紙帶給它讀取,人不能離開計算機。資源利用率很低。

2.批處理系統的時代:

  • 批處理是指用戶將一批作業提交給操作系統後就不再幹預,由操作系統控制它們自動運行,再輸出結果,從而減少作業建立和結束過程中的時間浪費。也就是無需等待人工操作,計算機這時可以自動、成批的執行程序。
  • 批量輸入任務。數據可以批量輸入到計算機中,但是並不是一次性把批量輸入都處理了,早期的計算機的操作系統可以稱爲單道批處理系統,即內存中只允許存放一個作業。
  • 資源利用率相對於之前有提高。但是一個作業單獨進入內存並獨佔系統資源,直到運行結束後下一個作業才能進入內存,當作業進行I/O操作時,CPU只能處於等待狀態,所以CPU利用率還是較低,尤其是對於I/O操作時間較長的作業。

多道程序設計。後來才提出的,此時的操作系統可以稱爲:多道批處理系統。即在內存中可同時存在若干道作業,說白了就是讓批處理系統可以一次性處理多個作業,但其實是快速的穿插運行,使得看起來可以處理多個作業,因此CPU的利用率顯著地提高了。

在這裏插入圖片描述
3.分時系統的時代:

  • 人-機交互,也就是人工可以干預計算機,及時調試程序。
  • 多用戶共享計算機資源。
  • 資源利用率大幅度提高.

3. 操作系統相關概念

  • 併發性:指的是兩個或多個事件可以在同一個時間間隔發送。
  • 並行性:指的是兩個或多個事件可以在同一個時刻發送。

在這裏插入圖片描述

當提到單處理器時實際上指的是併發性,提到多處理器指的是並行性。

在這裏插入圖片描述

  • 共享性:操作系統的資源可以供給多個併發的程序共同使用。共享性可分爲:互斥共享(當資源被程序A佔用時,其他程序就只能等待程序A使用完釋放後纔可以使用),同時訪問(某種資源在一段時間內併發地被多個程序訪問,可以宏觀地看該資源可以被同時訪問,因爲很快)

  • 虛擬性:把一個物理實體轉變爲若干個邏輯實體,物理實體是真實存在的,比如硬件設備,邏輯實體是虛擬存在的,虛擬的技術主要有時分複用技術和空分複用技術。

    • 時分複用技術:資源在時間上進行復用,不同程序併發使用。
      • 虛擬處理技術:藉助多道程序設計,爲每個程序建立進程,然後多個進程分時複用處理器。
      • 虛擬設備技術:物理設備虛擬爲多個邏輯設備(能進行邏輯運算的設備),每個程序佔用一個邏輯設備,多個程序通過邏輯設備併發訪問。比如後面要學的spooling用的就是虛擬設備技術。
    • 空分複用技術:來實現虛擬磁盤、虛擬內存等。
      • 虛擬硬盤技術:物理磁盤虛擬爲邏輯磁盤,比如把一個機械硬盤分區。
      • 虛擬內存技術:在邏輯上擴大程序的存儲容量,使用比實際內存更大的容量,提升編程效率。
  • 異步性:在多道程序設計的環境下,允許多個進程併發執行,進程在使用資源時可能需要等待或放棄。比如上面的互斥共享。進程的執行並不是一下子對程序的代碼執行到底,而是以走走停停的形式推進。比如進程A釋放資源,進程B,C目前在搶奪資源,此時B或者C誰能搶到是不知道的,這也體現異步性。

  • 操作系統的五大功能:進程管理、存儲管理、作業管理、文件管理、設備管理。

4. 進程管理

4.1 進程

進程(Process):是系統進行資源分配和調度的基本單位。進程作爲程序獨立運行的載體,保證了程序正常執行。我們的桌面應用當點擊啓動時就會變成進程。

爲什麼需要進程?早期沒操作系統,那麼資源就只能分配給當前運行的程序;當有了操作系統,引入多道程序設計後,進程就出現,來合理隔離每個程序佔用的資源。

進程在內存是一段連續的內存空間,這段空間稱爲進程控制塊(PCB,一個數據結構),每一個進程一定有一個PCB,而進程控制塊的構成如下:

在這裏插入圖片描述

解釋:

  • 標識符:唯一標識一個進程,用於區別其他進程。
  • 狀態:狀態一共有三種(運行,等待,阻塞,當然也可以說5種,還有創建和終止)。有一張很重要的進程狀態轉換圖,後續再說。
  • 優先級:表示獲得CPU控制權的優先級大小
  • 程序計數器(PC):進程即將被執行的下一條指令的地址。
  • 內存指針:可能是程序代碼、或進程數據相關指針。
  • 上下文數據:進程執行時處理器存儲的數據。
  • IO狀態信息:被進程IO操作所佔用的文件列表。像要刪除某個文件夾,發現刪不了,一看原來是文件夾裏面的文件沒有關閉,一直佔用。
  • 記賬信息:進程使用CPU時間、時鐘數總和等。

而上面可以總結分成四個種:進程標識符、處理機狀態、進程調度信息、進程控制信息。

因爲PCB是操作系統進行調度經常被讀取的信息,所以PCB是常駐內存的,系統中有專門開闢一塊PCB區域來存儲所有進程的PCB。

4.2 線程

線程(Thread):是系統進行資源分配和調度的最小單位。進程是由系統分配,線程是由CPU分配,線程包含在進程中的,是進程中實際運行工作的單位,比如計算一個數據,是利用線程來進行計算。一個進程至少有一個線程,每當啓動程序就先創建進程再創建一個線程。

一個進程可以併發多個線程,每個線程執行不同的任務。進程中的線程共享進程的資源,比如一個線程計算出的結果可以保存再進程中某個區域,然後另一個線程可以從進程中拿去到這個結果。

總結進程和線程的區別:

在這裏插入圖片描述

4.3 進程狀態

也稱爲進程的生命週期。

1.就緒狀態

  • 當進程被分配到除CPU以外的所有必要資源後,只要再獲得CPU的使用權,就可以立即運行。
  • 在一個系統中多個處於就緒狀態的進程通常排成一個隊列,稱爲就緒隊列。

2.運行(執行)狀態:

  • 進程獲取CPU,其進程就稱爲運行狀態。
  • 在單處理器中,在某個時刻只能有一個進程是處於運行狀態(併發)。

3.阻塞狀態:

  • 進程因某種原因,如其他設備未就緒而無法繼續執行。從而放棄CPU的狀態就稱爲阻塞狀態。
  • 像我們去打印時,在進程中已經向打印機發出打印請求,但發現打印機打印的可能還不是我們的,此時我們的進程就可以稱爲阻塞狀態或等待中。
  • 系統中可能有一個或多個阻塞進程,所以也有阻塞隊列這個概念。

4.創建狀態:創建進程時擁有PCB但其他資源尚未就緒的狀態稱爲創建狀態。

5.終止狀態:進程結束由系統清理或者歸還PCB的狀態稱爲終止狀態。

進程狀態轉換圖:

在這裏插入圖片描述

4.4 同步

引入生產者-消費者問題:有一羣生產者進程在生產產品,並將這些產品提供給消費者進程進行消費,生產者進程和消費者進程可以併發執行,在兩者之間設置了一個具有n可緩衝區的緩衝池,生產者進程需要將所生產的產品放到一個緩衝區中,消費者進程可以從緩衝區取走產品消費。

在這裏插入圖片描述

當生產者生產一個產品,緩衝區就+1,當消費者拿取一個產品,緩衝區就-1。

來到計算機,一般一個操作緩衝需要三個步驟:

register = count; // 先從緩衝區拿取產品數
register = register + 1; // 然後生產,對其加1
count = register; // 把更新的值歸還到緩衝區

但是在計算機中這樣是有問題的,因爲計算機中是併發執行的,比如:

在這裏插入圖片描述

可以代碼:

/*
* 剛開始倉庫數爲0
* 現在要生產10次,消費10次,那麼理想中最終倉庫數應該爲0
* 使用多線程來併發模仿一個生產,一個消費
*/
class Store {
    private int num = 0;
    public void producer() {
        int times = 10;
        while(times-- != 0) {
            try {
                    this.num = this.num + 1;
                System.out.println("生產後,產品數爲:" + this.num);
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public void comsumer() {
        int times = 10;
        while(times-- != 0) {
            try {
                this.num = this.num - 1;
                System.out.println("消費後,產品數爲:" + this.num);
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    public int getNum() {
        return num;
    }
}
/*
* 消費者
*/
class Comsumer implements Runnable {

    private Store store;

    Comsumer(Store store) {

        this.store = store;
    }

    @Override
    public void run() {
        store.comsumer();
    }
}
/*
* 生產者
*/
class Producer implements Runnable {

    private Store store;

    Producer(Store store) {
        this.store = store;
    }

    @Override
    public void run() {
        store.producer();
    }
}
public class Main {

    public static void main(String[] args) {
        Store store = new Store();
        Producer producer = new Producer(store);
        Comsumer comsumer = new Comsumer(store);
        Thread t1 = new Thread(producer);
        Thread t2 = new Thread(comsumer);
        t1.start();
        t2.start();
        // join 等待線程執行完才執行下面
        try {
            t1.join();
            t2.join();
            System.out.println("最終倉庫剩餘數:" + store.getNum());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

結果不唯一:我的量設置小了,可以設置大點。

最終倉庫剩餘數:3
最終倉庫剩餘數:5
最終倉庫剩餘數:0
最終倉庫剩餘數:-1

上面是我運行幾次的結果,錯的也有對的也有,這就是線程不同步的表現。

以上的根源問題就是:彼此間沒有通信。比如現在倉庫爲空,生產者沒有去通知消費者我目前正在生產,你先別消費,等我生產完再來消費。

線程同步:這個應該是我們聽得最多的,進程也有同步那麼線程更是有同步,因爲進程的線程共享進程資源,像上面的代碼案例就是一個線程不同步的表現(異步)。

線程同步的方法:(待詳講)

  • 互斥量
  • 讀寫鎖
  • 自旋鎖
  • 條件變量

臨界資源:指的是一些雖作爲共享資源卻又無法同時被多個線程共同訪問的共享資源。當有進程在使用臨界資源時,其他進程必須依據操作系統的同步機制等待佔用進程釋放該共享資源纔可重新競爭使 用共享資源。像上面的倉庫就是一個臨界資源。

進程同步:對競爭資源在多進程間進行使用有序的協調,使得併發執行的多個進程之間可以有效使用資源和相互合作。

爲了對臨界資源進行約束,提出了進程/線程間同步的四個原則

  • 空閒讓進:資源無佔用,允許使用。
  • 忙則等待:資源有佔用,請求進程等待。
  • 有限等待:保證有限等待時間能夠使用資源。
  • 讓權等待:等待時,進程需要讓出CPU。

進程同步的方法:(待詳講)

  • 消息隊列
  • 共享存儲
  • 信號量

4.5 Linux進程

進程類型:

  • 前臺進程:就是具有終端shell,可以與用戶交互的進程。
  • 後臺進程:沒有佔用shell,不可以與用戶交互的進程。如果要以後臺運行,在末尾添加:"&"
  • 守護進程:很多守護進程在系統啓動的時候就啓動了,一直運行到系統關閉。Linux中,一般以“d”結尾的是守護進程,典型的守護進程有:crond,httpd,sshd,mysqld。

進程ID:唯一標識,ID是一個非負數,最大值由操作系統限定。ID爲0的進程爲idle進程,是系統創建的第一個進程。ID爲1的進程爲init進程,是0號進程的子進程,完成系統初始化。init進程是所有用戶進程的祖先進程。

linux進程狀態:

在這裏插入圖片描述

命令:

  • 進程中有父子進程,它們的關係通過pstree命令查看。
  • ps命令常用於顯示當前進程的狀態。配合aux參數或者ef參數和grep命令檢索特定進程。
ps
ps -aux // 打印進程的詳細信息 
ps -u root // 查看用戶root的進程
ps -aux | grep 'xxx' // 查看特定進程
ps -ef --forest // 打印進程的父子關係
ps -aux --sort=-pcpu // 按照cpu的頻率排序
ps -aux --sort=-pmen // 按照內存排序
  • kill命令發送指定信號給進程。kill -l 可以查看操作系統支持的信號。kill -9 進程ID:可以無條件終止進程。
  • top命令查看進程的狀態。

5. 作業管理

5.1 進程調度

進程調度指的是計算機通過決策決定哪個就緒進程可以獲得CPU使用權。主要有兩個步驟:

  1. 保留舊進程的運行信息,請出舊進程。
  2. 選擇新進程,準備運行環境並分配CPU。

爲了實現這兩個步驟就要了解三種機制:

  1. 就緒隊列的排隊機制:將就緒進程按照一定的方式排成隊列,以便調度程序可以最快找到就緒隊列。
  2. 選擇隊列進程的委派機制:調度程序以一定策略選擇就緒進程,將CPU資源分配給它。
  3. 新老進程的上下文切換機制:保留舊進程的上下文信息,將新進程的上下文調度到CPU中準備好環境讓新的進程可以運行起來。

要是進程調度時,舊進程還沒執行完,此時也有兩種進程調度方法:

  1. 非搶佔式的調度:處理器一旦分配給某個進程,就讓該進程一直執行下去。調度程序不以任何原因搶佔正在被使用的處理器,直到進程完成工作或因爲IO阻塞纔會讓出處理器。

  2. 搶佔式的調度:允許調度程序以一定的策略暫停當前正在運行的進程,然後保留舊進程的上下文信息,分配處理器給新的進程

在這裏插入圖片描述

進程調度的算法:

  1. 先來先服務調度算法:就緒隊列中按照誰先來的方式先調度到CPU中
  2. 短進程優先調度算法:調度程序優先選擇就緒隊列中估計運行時間最短的進程。但是對於較長運行時間的作業的調度非常不公平,有可能永遠都沒能運行。
  3. 高優先權優先調度算法:進程附帶優先權,調度進程優先選擇優先權高的進程。這使得緊急的任務可以優先被處理。
  4. 時間片輪轉調度算法:按照先來先服務的原則排列就緒隊列,每次從隊列頭部取出待執行的進程,分配一個時間片執行。該算法相對公平,但不能保證及時響應用戶。最古老的算法。

除了時間片輪轉調度算法屬於搶佔式的調度,其他三個屬於非搶佔式的調度。

看道題目:

在這裏插入圖片描述

解:

在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
其實可以發現,如果沒有多個進程在等待時,使用什麼算法都是一樣的。

5.2 死鎖

死鎖是指兩個或兩個以上的進程在執行過程中,由於競爭資源或者由於彼此通信而造成的一種阻塞的現象,若無外力作用,它們都將無法推進下去。此時稱系統處於死鎖狀態或系統產生了死鎖,這些永遠在互相等待的進程稱爲死鎖進程。

產生死鎖的根源就是:

  1. 競爭的資源不夠。如下,進程1佔用傳真機,進程2佔用打印機,此時進程1想佔用打印機,進程2想佔用傳真機,但是目前打印機和傳真機正在被佔用,必須等待釋放,所以造成死鎖。

在這裏插入圖片描述

  1. 進程的調度順序不當:

在這裏插入圖片描述

只要滿足以下四個必要條件就一定發生死鎖:

  1. 互斥條件:進程對資源的使用是排他性的使用,即某資源只能由一個進程使用,其他進程需要使用只能等待。
  2. 請求保持條件:進程至少保持一個資源,又提出新的資源請求,新資源被佔用,請求被阻塞,被阻塞的進程不釋放自己保持的資源。
  3. 不可剝奪條件:進程獲得的資源在未完成使用前不能被剝奪,獲得的資源只能由進程自身釋放。
  4. 環路等待條件:發生死鎖時,必然存在進程-資源環形鏈,比如A進程正在等待B進程佔用的資源,B進程正在等待C進程佔用的資源,C進程正在等待A進程佔用的資源。

如何預防死鎖?只需要把上面四個條件中**除了互斥條件(這個破壞不了,因爲有些資源根本就不能同時訪問,比如打印機)**任意一個破壞即可:

  1. 破壞請求保持條件:系統規定進程運行之前,一次性申請所有需要的資源,進程在運行期間不會提出資源請求,從而破壞請求保持條件。
  2. 破壞不可剝奪條件:當一個進程請求新的資源得不到滿足時,必須釋放佔有的資源,進程運行時佔有的資源可以被釋放,意味着可以被剝奪。
  3. 破壞環路等待條件:將系統資源進行統一排隊,賦予不同編號;要求進程申請資源時,必須按照資源序號遞增的順序提出。線性申請不再形成環路,從而破壞了環路等待條件。比如:假設有1,2兩個進程,1進程佔用A資源需要使用B資源,2進程佔用B資源需要使用A資源,這樣會導致死鎖,如果把資源按照A=>B的排序,並按順序申請,那麼對於B它必須先申請A資源,再申請B資源。

如何避免死鎖?提出了銀行家算法:以銀行借貸系統分配策略爲基礎的算法,客戶申請的貸款是有限的,每次申請需聲明最大資金量,銀行家在能夠滿足貸款時,都應該給用戶貸款,客戶在使用貸款後,能夠及時歸還貸款。

一般有三個數據結構:已分配資源表,可分配資源表,所需資源表。

來看例子,假設三張表的數據如下:

在這裏插入圖片描述

根據兩張表,求個還需分配資源表:

在這裏插入圖片描述

下面就來操作:

在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
可能有時候遇到資源分配不了的情況,一般會等待或者強行殺掉進程,極端的話就是藍屏。

6. 存儲管理

6.1 內存分配與回收

  1. 單一連續分配:屬於最簡單的一種,只能在單用戶、單進程的操作系統中使用。

在這裏插入圖片描述

  1. 固定分區分配:固定分區分配是支持多道程序的最簡單存儲分配方式,內存空間被劃分爲若干固定大小的區域,每個分區只提供給一個程序使用,互不干擾。

在這裏插入圖片描述

  1. 動態分區分配:根據進程實際需要,動態分配內存空間。會涉及到相關數據結構、分配算法。

動態分區分配相關數據結構:

  1. 動態分區空閒表數據結構:

在這裏插入圖片描述

  1. 動態分區空閒鏈數據結構:常用這種。

在這裏插入圖片描述

動態分區的分配算法:

  1. 首次適應算法(FF算法):分配內存時從開始順序查找適合內存區,若沒有合適的空閒區,則該次分配失敗。但是這就有個缺點,因爲每次從頭部開始,要是此時較頭部的剛好有一個很大容量的空閒區可以分配,就會分配給它,然後再對這個很大容量的空閒區劃分(因爲一部分被佔用了),也就是大材小用,一個可以分配給大容量的內存被一次一次劃分,導致下次遇到一個大容量的可能分配不了。
  2. 最佳適應算法(BF算法):最佳適應算法要求空閒區鏈表按照容量大小排序,遍歷空閒區鏈表找到最佳合適空閒區。這樣可以避免大材小用的情況。比如:

在這裏插入圖片描述

  1. 快速適應算法(QF算法):要求有多個空閒區鏈表,每個空閒區鏈表存儲一種容量的空閒區。

在這裏插入圖片描述

6.2 內存回收

一共有以下4種情況:

在這裏插入圖片描述

對於第一種和第二種其實都是一樣的:

在這裏插入圖片描述

對於第三種:

在這裏插入圖片描述

對於第四種:

在這裏插入圖片描述

6.3 內外碎片

頁內碎片/內碎片/內部碎片:內部碎片是已經被分配出去的內存空間(能明確指出屬於哪個進程)大於請求所需的內存空間,不能被利用的內存空間就是內部碎片。

頁外碎片/外碎片/外部碎片:外部碎片是指還沒有分配出去的內存空間(不屬於任何進程),但是由於大小而無法分配給申請內存空間的新進程的內存空閒塊。

看圖:

在這裏插入圖片描述

單一連續分配算法可能產生內碎片。

固定分區分配算法可能產生內碎片和外碎片。

動態分區分配算法可能產生外碎片。

6.4 存儲的管理

上面是分配內存和回收內存,現在我們需要知道操作系統是如何管理進程的空間,比如內存滿了又來一個新程序要怎麼辦等問題。有三種方法:頁式存儲管理,段式存儲管理,段頁式存儲管理。

6.4.1 頁式存儲管理

首先需要知道頁面這個單位還有塊。

  • :對邏輯地址分頁。對象是進程,頁由頁號和頁內偏移地址組成,頁號是一個標識,頁內偏移是虛擬地址的劃分,指向程序中的某一頁。
  • :對物理地址分塊。對象是內存,塊由塊號和塊內偏移地址組成,塊號是一個標識,塊內偏移是實際地址的劃分,指向內存空間中某一個物理塊。

頁和塊的大小相等,只是爲了區別計算機內存和進程而提出塊和頁。

頁式存儲管理:將進程邏輯空間等分成若干大小的頁面(等分劃分),並編號;相應的把物理內存空間分成與頁面相同大小的物理塊,並編號;以頁面爲單位把進程空間裝進物理內存中分散的物理塊。

在這裏就有頁表這個概念,頁表記錄進程邏輯空間與內存物理空間的映射。如圖:

在這裏插入圖片描述

在現代計算機中,可以支持非常大的邏輯地址空間,所以頁表就變得非常大,要佔用非常大的內存空間,就提出了多級頁表,由頂級頁表(首頁)與其他頁表(二級頁表、三級頁表等)的映射,而且是按需拿取,比如現在需要某一頁,但該頁沒有在內存中,所以通過頂級頁表的映射關係把該頁加載到內存中,其他不用的就不加載。所以相對來說節省空間,當然這是一種時間換空間的算法,所以比較耗時。

頁式存儲管理如果有一段連續的邏輯分佈在多個頁面中,將大大降低執行效率,所以提出了段式存儲管理。

6.4.2 段式存儲管理

段式存儲管理:將進程邏輯空間劃分成若干段(非等分)並編號,每段都定義了一組邏輯信息,例如主函數MAIN、子程序段X、子函數Y等。段的長度由邏輯信息組的長度(連續邏輯)決定。

段由段號和段內偏移地址組成,也有段表,存儲段號、物理地址的起始地址(基址)和段長的表,如下:

在這裏插入圖片描述

段式存儲和頁式存儲的比較:

  • 相同:段式存儲和頁式存儲都離散地管理了進程的邏輯空間。
  • 不同:
    • 頁是相對於物理空間去劃分(這裏跟前面說的不一樣,前面是頁和字塊的比較,頁和字塊密切相關(頁大小等於塊大小),這裏是頁和段的比較,段比頁更加接近邏輯空間劃分),段是相對於邏輯空間去劃分。
    • 分頁是爲了合理利用空間,分段是滿足用戶要求。
    • 頁大小由硬件固定,段長度可動態變化。
    • 表的結構不同。

6.4.3 段頁式存儲管理

分頁可以有效提高內存利用率(雖然說存在內碎片),分段可以更好滿足用戶需求,兩者結合,形成段頁式存儲管理。

段頁式存儲管理:先將邏輯空間按段式管理分成若干段,再把段內空間按頁式管理等分成若干頁

段頁式的地址結構:段號、段內頁號、頁內地址(通過塊號找到內存的物理地址)。

如下圖,基址是每一段的起始地址,表可能還有其他內容,這裏忽略。

在這裏插入圖片描述

6.5 虛擬內存

有些進程實際需要的內存很大,超過物理內存的容量,而且多道程序設計(一個內存運行多個進程),使得每個進程可用物理內存更加稀缺,但是不可能無限增加物理內存,物理內存總有不夠的時候,所以提出虛擬內存。

虛擬內存的實現是基於局部性原理的。局部性原理是指CPU訪問存儲器時,無論是存取指令還是存取數據,所訪問的存儲單元都趨於聚集在一個較小的連續區域中

所以程序運行時,無需全部裝入內存,裝載部分即可,如果訪問頁不在內存,則發出缺頁中斷,發起頁面置換算法。從用戶層面看,程序擁有很大的空間,即是虛擬內存。虛擬內存實際是對物理內存的補充,速度接近於內存,成本接近於輔存

虛擬內存:把程序使用的內存劃分,將部分暫時不使用的內存放置在輔存。

在這裏插入圖片描述

上面提到置換算法,在虛擬內存中有三種置換算法:(這些置換算法也在組成原理的高速緩存中也有,但缺了OPT,當時說的是字塊,現在說的是頁面,其實都是一樣的)

  • 先進先出算法(FIFO):顧名思義,優先移除最先進入內存的頁面,後來的頁面添加到隊尾。
  • 最不經常使用算法(LFU):優先淘汰(移除)掉最不經常使用的頁面。所以需要額外的空間來記錄字塊的訪問次數。
  • 最近最少使用算法(LRU):優先淘汰一段時間內沒有使用的頁面,有多種實現方法,一般使用雙向鏈表。每次使用的字塊都會被拉到鏈表前(保證鏈表頭是最近使用的),太久沒使用的最終會被放到最後,然後當鏈表滿了要添加數據時就會把鏈表尾的淘汰掉。
  • 最佳置換算法(OPT,理想的置換算法):發生缺頁時,有些頁面在內存中,其中有一頁將很快被訪問(也包含緊接着的下一條指令的那頁),而其他頁面則可能要到10、100或者1000條指令後纔會被訪問,每個頁面都可 以用在該頁面首次被訪問前所要執行的指令數進行標記。該算法實現不了,因爲計算機不知道當缺頁發生時,操作系統無法知道各個頁面下一次是在什麼時候被訪問。

看題:

在這裏插入圖片描述

解:

在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
大型遊戲中,有時候走近一個場景發現場景的東西才慢慢加載出來,這就是觸發了置換策略;黑暗之魂中打BOSS前的雨門,當我們走進雨門就觸發置換策略,把BOSS場景的東西加載出來。

在組成原理中說到高速緩存和主存的置換:

在這裏插入圖片描述

現在,說到主存和輔存的置換:

在這裏插入圖片描述

替換策略發生在Cache-主存層次、主存-輔存層次,Cache-主存層次的替換策略主要是爲了解決速度問題,主存-輔存層次主要是爲了解決容量問題。

6.6 Linux存儲管理

6.6.1 Buddy內存分配算法

Buddy內存分配算法:在Linux中爲了努力讓內存分配與相鄰內存合併能快速進行(解決外碎片),算法基於計算機處理二進制的優勢具有極高的效率,以向上取整爲2的冪大小作爲內存分配原則,比如一個進程佔70k,那麼就會分配一個128k的內存給它。Buddy翻譯過來是“夥伴”,具體指的是內存的“夥伴”,一片連續內存的“夥伴”是相鄰的另一片大小一樣的連續內存,看圖理解:

在這裏插入圖片描述

Buddy內存的分配,會創建一系列空閒塊鏈表,每一種都是2的冪次方。

在這裏插入圖片描述

來看下Buddy內存分配的例子:

  1. 假設存儲空間有1M大小:也就是隻有1MB的那塊空閒區域鏈表有一個節點。

在這裏插入圖片描述

  1. 現在分配100k內存:

在這裏插入圖片描述

  1. 當運行完,回收時:

在這裏插入圖片描述

Buddy內存分配算法把外碎片變成內碎片。

6.6.2 Linux交換空間

交換空間(Swap)是磁盤的一個分區,Linux物理內存滿時,會把一些內存交換至Swap空間,Swap空間是初始化系統時配置的。可以在Linux中查看,使用top命令:
在這裏插入圖片描述

交換空間的作用:

  • 冷啓動內存依賴:比如對於一些大型的應用程序,在啓動的時候需要使用大量內存,但是這些內存只是在啓動的時候用一下後續在運行時很少用或不會用,所以系統就可以將這些不怎麼用的內存放到交換空間去,從而釋放更多的物理內存,提供給系統使用。
  • 系統睡眠依賴:當Linux要睡眠的時候,把內存中的所有數據保存到交換空間中,當下次喚醒Linux時再重新加載到內存中,加快系統的啓動速度。
  • 大進程空間依賴:有些內存確實需要使用到很多的內存空間,但是物理內存不夠使用,所以將進程需要使用的數據暫時保存到交換空間去,使得讓大進程可以運行。

交換空間跟我們前面說到虛擬空間類似,下面是它們的比較:

在這裏插入圖片描述

7. 文件管理

7.1 文件邏輯結構

在這裏插入圖片描述

無結構文件:也稱爲流式文件,文件內容長度以字節爲單位,比如exe文件、dll文件、so文件等。

有結構文件:文件內容由定長記錄和可變長記錄組成,定長記錄存儲文件格式、文件描述等結構化數據項,可變長記錄存儲文件具體內容。

在這裏插入圖片描述

有以下幾種形式實現有結構文件:

  1. 順序文件:順序文件是指按順序存放在存儲介質中的文件,磁帶的存儲特性使得磁帶文件只能存儲順序文件,順序文件是所有邏輯文件當中存儲效率最高的。但是對於順序文件,如果要進行增刪改,比如在文件中間添加數據,那麼文件中間後面的數據都得往後移,學過數據結構與算法的都知道,這個做法是很費時的,所以**對於順序結構的增刪改的效率是非常差的。**所以提出索引文件。
  2. 索引文件:可變長文件不適合使用順序文件格式存儲,索引文件是爲了解決可變長文件存儲而發明的一種文件格式,索引文件需要配合索引表完成存儲的操作。

在這裏插入圖片描述

  1. 索引順序文件:順序文件和索引文件結合的產物,各記錄本身在介質上也是順序排列的,它包含了直接處理和修改記錄的能力。

7.2 輔存的存儲空間分配

7.2.1 輔存分配方式

輔存分配方式指的是如何在輔存(磁盤)上存儲數據。

在這裏插入圖片描述

1.連續分配:順序讀取文件內容非常容易,速度很快,對存儲要求高,要求滿足容量的連續存儲空間。如圖:

在這裏插入圖片描述

2.鏈接分配:鏈接分配可以將文件存儲在離散的盤塊中,需要額外的存儲空間存儲文件的盤塊鏈接順序。

  • 隱式鏈接如圖:

在這裏插入圖片描述

  • 顯式鏈接如圖:使用一張表來存儲,一個鏈接和該鏈接的下一個鏈接。但是顯示鏈接分配不支持高效的直接存儲(FAT記錄項多,所以需要去找出空閒區域),檢索時FAT表佔用較大的存儲空間(需要將整個FAT加載到內存)。

在這裏插入圖片描述

3.索引分配:把文件的所有盤塊集中存儲(索引),讀取某個文件時,將文件索引讀取進內存即可。每個文件擁有一個索引塊,記錄所有盤塊信息。索引分配方式支持直接訪問盤塊,文件較大時,索引分配方式具有明顯優勢。所以現在常用這個來分配輔存。

在這裏插入圖片描述

7.2.2 存儲空間管理

存儲空間管理就是磁盤的空間管理,比如記錄磁盤哪裏是空閒的、哪裏是已經存放東西的。

在這裏插入圖片描述

1.空閒表:空閒盤區的分配與內存分配類似,比如首次適應算法、循環適應算法等,回收過程也與內存回收類似。如圖:

在這裏插入圖片描述

2.空閒鏈表:空閒鏈表把所有空閒盤區組成一個空閒鏈表,每個鏈表節點存儲空閒盤塊和空閒的數目。分配和回收同上。

3.位示圖:位示圖維護成本很低,位示圖可以非常容易找到空閒盤塊,位示圖使用0/1比特位,佔用空間很小。主要使用這個,如圖:

在這裏插入圖片描述

7.3 目錄管理

在這裏插入圖片描述

目錄樹:任何文件或目錄都只有唯一路徑。

7.4 Linux文件

在這裏插入圖片描述
在這裏插入圖片描述
需要知道:在Linux中,一切皆爲文件(進程也是文件,進程以數字命名,存放在/proc目錄下)。

絕對路徑:從根目錄開始的路徑,比如:/home/aa/b 這個就是絕對路徑。

相對路徑:相對於當前的文件路徑,比如:…/aa(…/ 返回上一級目錄),/aa/bb(當前目錄中的aa文件夾中的bb文件)

關於文件操作的一些指令:

  • touch 文件名(絕對路徑+文件名 也可以):創建一個文件
  • vim 文件名(絕對路徑+文件名 也可以):vim命令可以創建並修改文件,當輸入後回車就會跳到另一個黑框,然後按下insert鍵或者i鍵就可以在該框輸入,如果寫好了想保存退出,按下ESC鍵,然後輸入 :wq。如果想查看行數,按下ESC鍵,然後輸入 :set nu。
  • cat 文件名(絕對路徑+文件名 也可以): 查看文件內容。
  • rm 文件名(絕對路徑+文件名 也可以):刪除文件。
  • mkdir 文件目錄名(絕對路徑+文件名 也可以):創建文件目錄。
  • rm -r 文件目錄名(絕對路徑+文件名 也可以):遞歸刪除文件目錄中的文件包括文件目錄。

文件類型:

在這裏插入圖片描述

使用指令:ll,來查看文件類型:第一個字母指的是文件類型,後面的到菜鳥教程看

在這裏插入圖片描述

文件系統概述:當格式化U盤時就會出現以下幾種格式化模式。

  1. FAT(File Allocation Table),FAT16、FAT32等,早期微軟Dos/Windows使用的文件系統,它使用一張表保存盤塊的信息。
  2. NTFS (New Technology File System),WindowsNT環境使用的文件系統,NTFS對FAT進行了改進,取代了舊的文件系統。win10、win7都可以使用。
  3. EXT(Extended file system):擴展文件系統,Linux上使用的文件系統,EXT2/3/4 數字表示第幾代。Linux也支持FAT、NTFS等。但是EXT不能給Windows識別。

EXT文件系統,長下面這樣:

在這裏插入圖片描述

每一個Block Group長下面這樣:

在這裏插入圖片描述

  • Inode Table:存放文件Inode的地方每一個文件(目錄)都有一個Inode,是每一個文件(目錄)的索引節點。

  • Inode:存放的內容包含:索引節點編號、文件類型、文件權限、文件物理地址、文件長度、文件連接計數、文件存取時間、文件狀態、訪問計數等。文件名不是存放在Inode節點上的,而是存放在目錄的Inode節點,爲了列出目錄文件的時候無需加載文件的Inode

  • Inode bitmap:Inode的位示圖,記錄已分配的Inode和未分配的Inode。

在這裏插入圖片描述

  • Data block:存放文件內容的地方,(中文被翻譯成)每個block都有唯一的編號,文件的block記錄在文件的Inode上。

  • Block bitmap:功能與Inode bitmap類似,記錄Data block的使用情況。

  • Superblock:記錄整個文件系統相關信息的地方,Block和Inode的使用情況、時間信息、控制信息等。

一些相關指令:(瞭解就行)

  • df -T:查看系統掛載磁盤的信息。

在這裏插入圖片描述

  • dumpe2fs (接上上面df -T查出來的Filesystem名稱),比如查詢那個ext4類型/dev/sda1的信息:dumpe2fs /dev/sda1:(如果權限不足,升級權限,使用sudo dumpe2fs /dev/sda1)輸出很多信息

在這裏插入圖片描述

把它導下來:dumpe2fs /dev/sda1 > 文件名.log,然後使用vim查看

在這裏插入圖片描述

這裏說一個vim中的查詢,輸入 /,然後加上你要搜索的字符,比如:

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-PKGQkfCY-1587525418135)(概述/78.png)]

  • stat 文件名(包括後綴,也可以加絕對路徑):Inode是一個文件唯一的編號。

在這裏插入圖片描述

  • mv oldFileName newFileName:重命名(如果有後綴,後綴也要寫上)

8. 設備管理

8.1 廣義的IO設備

廣義的IO設備:指的是對CPU而言,凡是對CPU進行數據輸入的都是輸入設備,對CPU而言,凡是CPU進行數據輸出的都是輸出設備(比如CPU處理完的數據放到內存)。可以有以下幾種分類:按使用特性分類、按設備的共享屬性分類、按信息交換的單位分類、按傳輸速率分類。如圖:

在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述

8.2 IO設備的緩衝區

組成原理中提到CPU與IO設備的速率不匹配,所以採用了存儲層次(主存-輔存層次、主存-高速緩存層次)。而IO設備的緩衝區也是爲了解決這個問題,減少CPU處理IO請求的頻率,提高CPU與IO設備之間的並行性。

在這裏插入圖片描述

然而該緩衝區只屬於特定的IO進程(專用緩衝區),所以當這樣的IO進程比較多時,對內存的消耗也很大。因此操作系統劃出可供多個進程使用的公共緩衝區,稱之爲緩衝池

在這裏插入圖片描述

當需要用到IO緩衝區時,就從緩存池取出,當使用完就把緩衝區歸還到緩衝池。達到了多個進程共同使用緩衝區的要求,也減少了內存的消耗。

8.3 SPOOLing技術

SPOOLing技術:是關於慢速字符設備如何與計算機主機交換信息的一種技術(還是跟上面一樣,解決CPU與IO設備速度不匹配的問題),利用高速共享設備將低速的獨享設備模擬爲高速的共享設備,邏輯上,系統爲每一個用戶都分配了一臺獨立的高速獨享設備,也稱爲虛擬設備技術。

在這裏插入圖片描述

作業:在輸入、輸出之間增加了排隊轉儲環節(輸入井、輸出井),SPOOLing負責輸入(出)井與低速設備之間的調度,邏輯上,進程直接與高速設備交互,減少了進程的等待時間。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章