操作系統


操作系統

操作系統(Operating System,OS)是指控制和管理整個計算機系統的硬件和軟件資源,並合理地組織調度計算機的工作和資源的分配,以提供給用戶和其他軟件方便的接口和環境,它是計算機系統中最基本的系統軟件。

操作系統特征

  1. 並發

    指兩個或多個事件在同一時間間隔內發生。這些享件宏觀上是同時發生的,但微觀上是交替發生的。

  2. 共享

    互斥共享:系統中的某些資源,雖然可以提供給多個進程使用,但一個時間段內只允許一個進程訪問該資源。

    例子:使用QQ和微信視頻。同一時間段攝像頭只能分配給其中一個。

    同時共享:系統中的某些資源,允許一個時間段內由多個進程“同時”對它們進行訪問(這個同時是宏觀上的)。

    例子:例子:使用QQ發送文件A,同時使用微信發送文件B。宏觀上看兩邊同時都在讀取並發送數據。微觀上,兩個進程是交替着訪問硬盤的。

  3. 虛擬

    概念:虛擬是指把一個物理上的實體變為若干個邏輯上的對應物。物理實體(前者)是實際存在的,而邏輯上對應物((后者)是用戶感受到的。

    虛擬技術

    空分復用技術 如:虛擬存儲器技術):如:我們在電腦上打開多個軟件,超過了電腦的內存(16GB),但這些軟件仍可以在電腦上同時運行。

    時分復用技術 (如:虛擬處理器):如一個單核CPU電腦可以打開多個程序

  4. 異步

    概念:在多道程序環境下,允許多個程序並發執行,但由於資源有限,進程的執行不是一管到底的,而是走走停停,以不可預知的速度向前推進,這就是進程的異步性。

    例如:點外賣,點完外賣之后去做其他事情,等外賣到了再去取外賣。在A事件的空閑期間,我們可以做B事件。當空閑期間結束后,我們再去執行A事件的后續操作。

操作系統的運行機制和體系結構

運行機制

兩種指令:

  1. 特權指令:如:如內存清零指令
  2. 非特權指令:如:普通的運算指令

兩種處理器狀態:

  1. 核心態(管態):特權指令、非特權指令都可以執行
  2. 用戶態(目態):此時CPU只能執行非特權指令

兩種程序

  1. 內核程序:操作系統的內核程序是系統的管理者,既可以執行特權指令,也可以執行非特權指令,運行在核心態。
  2. 應用程序:為了保證系統能安全運行,普通應用程序只能執行非特權指令,運行在用戶態

操作系統內核

  • 時鍾管理:操作系統的時鍾管理是依靠硬件定時器的(具體硬件怎么實現我也不太清楚,好像是靠硬件周期性的產生一個脈沖信號實現的)。時鍾管理相當重要,比如我們獲取時間信息進程切換等等都是要依靠時鍾管理。
  • 中斷處理
  • 原語:可以簡單理解為用來實現某個特定功能,在執行過程中不可被中斷的指令集合。原語有一個非常重要的特性,就是原子性(其運行一氣呵成,不可中斷)。
  • 對系統資源進行管理的功能:進程管理、存儲器管理、設備管理等。

中斷

  • 在程序運行過程中,系統出現了一個必須由CPU立即處理的情況,此時,CPU暫時中止程序的執行轉而處理這個新的情況的過程就叫做中斷

  • 操作系統發現中斷的信號是第一個程序的時間片(每個程序不能一直執行,CPU會給每個程序一定的執行時間,這段時間就是時間片)用完了,應該換第二個應用程序執行了

  • 切換到第2個進程后,操作系統會將CPU使用權交換給第二個應用程序,接着第二個應用程序就在用戶態下開始執行。

  • 進程2需要調用打印機資源,這時會執行一個系統調用(后面會講系統調用,這里簡單理解為需要操作系統進入核心態處理的函數),讓操作系統進入核心態,去調用打印機資源

  • 打印機開始工作,此時進程2因為要等待打印機啟動,操作系統就不等待了(等到打印機准備好了,再回來執行程序2),直接切換到第三個應用程序執行

  • 等到打印機准備好了,此時打印機通過I/O控制器會給操作系統發出一個中斷信號,操作系統又進入到核心態,發現這個中斷是因為程序2等待打印機資源,現在打印機准備好了,就切換到程序2,切換到用戶態,把CPU給程序2繼續執行。

好了,現在可以給出一個結論,就是用戶態、核心態之間的切換是怎么實現的?

  • "用戶態 ---> 核心態"是通過中斷實現的。並且中斷時唯一途徑
  • "核心態 ---> 用戶態"的切換時通過執行一個特權指令,將程序狀態的標志位設為用戶態。

中斷的分類

舉個例子,什么是內中斷和外中斷:

假如你對象上課的時候突然異想天開,回過神來已經過好好長一段時間,這是內部中斷。想着想着老師走過來,給了你對象一嘴巴,這是外部中斷

官方解釋如下:

  • 內中斷常見的情況如程序非法操作(比如你要拿的的數據的內存地址不是內存地址,是系統無法識別的地址),地址越界(比如系統給你的程序分配了一些內存,但是你訪問的時候超出了你應該訪問的內存范圍)、浮點溢出(比如系統只能表示1.1到5.1的范圍,你輸入一個100, 超出了計算機能處理的范圍),或者異常陷入trap(是指應用程序請求系統調用造成的,什么是系統調用,后面小節會舉例講)。
  • 外中斷常見的情況如I/O中斷(由I/O控制器產生,用於發送信號通知操作完成等信號,比如進程需要請求打印機資源,打印機有一個啟動准備的過程,准備好了就會給CPU一個I/O中斷,告訴它已經准備好了)、時鍾中斷(由處理器內部的計時器產生,允許操作系統以一定規程執行函數,操作系統每過大約15ms會進行一次線程調度,就是利用時鍾中斷來實現的)。

系統調用

為什么需要系統調用?

  • 比如你的程序需要讀取文件信息,可讀取文件屬於讀取硬盤里的數據,這個操作應該是CPU在內核態去完成的,我們的應用程序怎么讓CPU去幫助我們切換到內核態完成這個工作呢,這里就需要系統調用了
  • 這里就引出系統調用的概念和作用。
  • 應用程序通過系統調用請求操作系統的服務。系統中的各種共享資源都由操作系統統一管理,因此在用戶程序中,凡是與資源有關的操作(如存儲分配、I/O操作、文件管理等),都必須通過系統調用的方式向操作系統提出服務請求,由操作系統代為完成。

系統調用的分類:

需要注意的是,庫函數系統調用容易混淆。

  • 庫是可重用的模塊 處於用戶態
  • 進程通過系統調用從用戶態進入內核態, 庫函數中有很大部分是對系統調用的封裝

舉個例子:比如windowslinux中,創建進程的系統調用方法是不一樣的。 但在node中的只需要調用相同函數方法就可以創建一個進程。例如

// 引入創建子進程的模塊
const childProcess = require('child_process')
// 獲取cpu的數量
const cpuNum = require('os').cpus().length

// 創建與cpu數量一樣的子進程
for (let i = 0; i < cpuNum; ++i) {
  childProcess.fork('./worker.js')
}

進程的定義、組成、組織方式、狀態與轉換

為什么要引入進程的概念呢?

  • 早期的計算機只支持單道程序(是指所有進程一個一個排隊執行,A進程執行時,CPU、內存、I/O設備全是A進程控制的,等A進程執行完了,才換B進程,然后對應的資源比如CPU、內存這些才能換B用)。

  • 現代計算機是多道程序執行,就是同時看起來有多個程序在一起執行,那每個程序執行都需要系統分配給它資源來執行,比如CPU內存

  • 拿內存來說,操作系統要知道給A程序分配的內存有哪些,給B程序分配的內存有哪些,這些都要有小本本記錄下來,這個小本本就是進程的一部分,進程的一大職責就是記錄目前程序運行的狀態

  • 系統為每個運行的程序配置一個數據結構,稱為進程控制塊(PCB),用來描述進程的各種信息(比如代碼段放在哪)。

進程的定義

簡要的說,進程就是具有獨立功能的程序在數據集合上運行的過程。(強調動態性)

比如啟動QQ,這個程序運行過程的整體就是一個進程。

PCB有哪些組成

如下圖

  • 進程標識符PID:相當於身份證。是在進程被創建時,操作系統會為該進程分配一個唯一的、不重復的ID,用於區分不同的進程
  • 用戶標識符UID:用來表示這個進程所屬的用戶是誰。
  • 進程當前狀態和優先級下一小節會詳細介紹
  • 程序段指針:指當前進程的程序在內存的什么地方
  • 數據段指針:指當前進程的數據在內存的什么地方
  • 鍵盤和鼠標:指進程被分配得到的I/O設備
  • 各種寄存器值:指比如把程序計數器的值,比如有些計算的結果算到一半,進程切換時需要把這些值保存下來。

進程的狀態

進程是程序的一次執行。在這個執行過程中,有時進程正在被CPU處理,有時又需要等待CPU服務,可見,進程的 狀態是會有各種變化。為了方便對各個進程的管理,操作系統需要將進程合理地划分為幾種狀態。

進程的三種基本狀態:

進程的另外兩種狀態:

進程狀態的轉換

進程的狀態並不是一成不變的,在一定情況下會動態轉換。

以上的這些進程狀態的轉換是如何實現的呢,這就要引出下一個角色了,叫原語

  • 原語是不可被中斷的原子操作。我們舉一個例子看看原語是怎么保證不可中斷的。

原語采用關中斷指令開中斷指令實現。

  • 首先執行關中斷指令
  • 然后外部來了中斷信號,不予以處理
  • 等到開中斷指令執行后,其他中斷信號才有機會處理。

進程的通信

為什么需要進程間通信呢?

因為進程是分配系統資源的單位(包括內存地址空間),因此各進程擁有的內存地址空間相互獨立。

進程通信3種方法

共享存儲

因為兩個進程的存儲空間不能相互訪問,所以操作系統就提供的一個內存空間讓彼此都能訪問,這就是共享存儲的原理。

注:同一時刻只能有一個進程訪問共享空間

其中,介紹一下基於存儲區的共享。

  • 在內存中畫出一塊共享存儲區,以數據的形式、存放位置都是由進程控制,而不是操作系統。

管道

  • 管道數據是以字符流(注意不是字節流)的形式寫入管道,當管道寫滿時,寫進程的write()系統調用將被阻塞,等待讀進程將數據取走。當讀進程將數據全部取走后,管道變空,此時讀進程的read()系統調用將被阻塞。
  • 如果沒寫滿就不允許讀。如果都沒空就不允許寫。
  • 數據一旦被讀出,就從管道中被丟棄,這就意味着讀進程最多只能有一個。

消息傳遞

進程間的數據交換以格式化的消息為單位。進程通過操作系統提供的"發送消息/接收消息"兩個原語進行數據交換。

其中消息是什么意思呢?就好像你發QQ消息,消息頭的來源是你,消息體是你發的內容。如下圖:

直接通信:消息直接掛到接受進程的消息緩沖隊列上

間接通信:消息要先發送到中間實體中,因此也稱“信箱通信方式”。

線程

為什么要引入線程呢?

  • 比如你在玩QQ的時候,QQ是一個進程,如果QQ的進程里沒有多線程並發,那么QQ進程就只能同一時間做一件事情(比如QQ打字聊天)
  • 但是我們真實的場景是QQ聊天的同時,還可以發文件,還可以視頻聊天,這說明如果QQ沒有多線程並發能力,QQ能夠的實用性就大大降低了。所以我們需要線程,也就是需要進程擁有能夠並發多個事件的能力。

引入線程后帶來的變化

進程的同步和互斥

同步:是指多個進程中發生的事件存在某種先后順序。即某些進程的執行必須先於另一些進程。

例:進程B需要從緩沖區讀取進程A產生的信息,當緩沖區為空時,進程B因為讀取不到信息而被阻塞。而當進程A產生信息放入緩沖區時,進程B才會被喚醒。

互斥:是指多個進程不允許同時使用同一資源。當某個進程使用某種資源的時候,其他進程必須等待。

例:進程B需要訪問打印機,但此時進程A占有了打印機,進程B會被阻塞,直到進程A釋放了打印機資源,進程B才可以繼續執行。

信號量

信號量主要是來解決進程的同步互斥的。

在操作系統中,常用P、V信號量來實現進程間的同步互斥,我們簡單了解一下一種常用的信號量,記錄型信號量來簡單了解一下信號量本質是怎樣的。

/*記錄型信號量的定義*/
typedef struct {
    int value; // 剩余資源
    Struct process *L // 等待隊列
} semaphore

意思是信號量的結構有兩部分組成,一部分是剩余資源value,比如目前有兩台打印機空閑,那么剩余資源就是2,誰正在使用打印機,剩余資源就減1。

Struct process *L 意思是,比如2台打印機都有人在用,這時候你要用打印機,此時會把這個打印機資源的請求放入阻塞隊列,L就是阻塞隊列的地址。

/*P 操作,也就是記錄型信號量的請求資源操作*/
void wait (semaphore S) {
    S.value--;
    if (S.value < 0){
        block (S.L);
    }
}

需要注意的是,如果剩余資源數不夠,使用block原語使進程從運行態進入阻塞態,並掛到信號量S的等待隊列中。

/*V 操作,也就是記錄型信號量的釋放資源操作*/
void singal (semaphore S) {
    S.value++;
    if (S.value <= 0){
        wakeup (S.L);
    }
}

釋放資源后,若還有別的進程在等待這個資源,比如打印機資源,則使用wakeup原語喚醒等待隊列中的一個進程,該進程從阻塞態變為繼續態。

生產者消費者問題

為什么要講這個呢,主要是node流的機制,本質就是生產者消費者問題,可以簡單的看看這個問題如何解決。

如上圖,生產者的主要作用是生成一定量的數據放到緩沖區中,然后重復此過程。與此同時,消費者也在緩沖區消耗這些數據。該問題的關鍵就是要保證生產者不會在緩沖區滿時加入數據,消費者也不會在緩沖區中空時消耗數據。

內存的基礎知識和概念

為什么需要內存

內存是計算機其它硬件設備CPU溝通的橋梁、中轉站。程序執行前需要先放到內存中才能被CPU處理。

cpu如何區分執行程序的數據在內存的什么地方

  • 是通過給內存的存儲單元編址實現的。(存儲單元一般是以字節為單位)
  • 如下圖,內存的存儲單元,就像一個酒店的房間,都有編號,比如程序一的數據都在1樓,1樓1號存儲着程序里let a = 1這段代碼。

內存管理-內存空間的分配與回收

  • 內存分配分為連續分配非連續分配,連續分配是指用戶進程分配的必須是一個連續的內存空間
  • 這里我們只講連續分配中的動態分區分配
  • 什么是動態分區分配呢,這種分配方式不會預先划分內存分區,而是在進程裝入內存時,根據進程的大小動態地建立分區,並使分區的大小正好適合進程的需要。(比如,某計算機內存大小64MB,系統區8MB,用戶區56MB...,現在我們有幾個進程要裝入內存,如下圖)

  • 隨之而來的問題就是,如果此時進程1使用完了,相應在內存上的數據也被刪除了,那么空閑的區域,后面該怎么分配(也就是說隨着進程退出,會有很多空閑的內存區域出現)

我們講一種較為簡單的處理方法叫空閑分區表法來解決這個問題。如下圖,右側的表格就是一個空閑分區表。

當很多個空閑分區都能滿足需求時,應該選擇哪個分區進行分配呢,例如下圖,分別有20MB10MB4MB三個空閑分區塊,現在進程5需要4MB空閑分區,改怎么分配呢?

我們需要按照一定的動態分區分配算法,比如有首次適應算法,指的是每次都從低地址開始查找,找到第一個能滿足大小的空閑分區。還有比如最佳適應算法,指的是從空閑分區表中找到最小的適合分配的分區塊來滿足需求。

連續分配缺點很明顯,大多數情況,需要分配的進程大小,不能跟空閑分區剩下的大小完全一樣,這樣就產生很多很難利用的內存碎片

這里我們介紹一種更好的空閑分區的分配方法,基本分頁存儲。如下圖

將內存空間分為一個個大小相等的分區(比如:每個分區4KB).每個分區就是一個“頁框”。頁框號從0開始。

將用戶進程的地址空間分為與頁框大小相等的一個個區域,稱為“頁”。每個頁也是從0開始。

死鎖

什么是僵屍進程

僵屍進程是已完成且處於終止狀態,但在進程表中卻仍然存在的進程。僵屍進程通常發生在父子關系的進程中,由於父進程仍需要讀取其子進程的退出狀態所造成的。

死鎖產生的原因

死鎖產生的原因大致有兩個:資源競爭和程序執行順序不當

死鎖產生的必要條件

資源死鎖可能出現的情況主要有

  • 互斥條件:每個資源都被分配給了一個進程或者資源是可用的
  • 保持和等待條件:已經獲取資源的進程被認為能夠獲取新的資源
  • 不可搶占條件:分配給一個進程的資源不能強制的從其他進程搶占資源,它只能由占有它的進程顯示釋放
  • 循環等待:死鎖發生時,系統中一定有兩個或者兩個以上的進程組成一個循環,循環中的每個進程都在等待下一個進程釋放的資源。

死鎖類型

  1. 兩階段加鎖
  2. 通信死鎖
  3. 活鎖
  4. 飢餓鎖

死鎖的恢復方式

  1. 通過搶占進行恢復
  2. 通過回滾進行恢復
  3. 殺死進程恢復

破壞死鎖

  1. 破壞互斥條件
  2. 破壞保持等待的條件
  3. 破壞不可搶占條件
  4. 破壞循環等待條件

文件管理

文件是什么?

文件就是一組有意義的信息/數據集合。

文件的屬性

文件名、標識符、類型、位置、大小、保護信息。

文件內部數據如何組織在一起

如下圖,文件主要分為有結構文件無結構文件

文件之間如何組織起來

通過樹狀結構組織的。

文件的邏輯結構

邏輯結構是指,在用戶看來,文件內部的數據是如何組織起來的,而“物理結構”是在操作系統看來,文件是如何保存在外存,比如硬盤中的。

1.順序文件

什么是順序文件

指的是文件中的記錄一個接一個地在邏輯上是順序排列,記錄可以是定長變長,各個記錄在物理上可以順序存儲鏈式存儲

2.索引文件

3. 索引順序文件

索引順序文件是索引文件順序文件思想的結合。索引順序文件中,同樣會為文件建立一張索引表,但不同的是,並不是每個記錄對應一個索引表項,而是一組記錄對應一個索引表項。

如上圖,學生記錄按照學生姓名的開頭字母進行分組。每個分組就是一個順序文件,分組內的記錄不需要按關鍵字排序

文件目錄

一個文件對應一個FCB,一個FCB就是一個目錄項,多個FCB組成文件目錄

文件目錄的結構通常是樹狀的

  • 需要注意的是,樹狀目錄不容易實現文件共享,所以在樹形目錄結構的基礎上,增加了一些指向同一節點的有向邊(可以簡單理解為引用關系,就跟js里的對象一樣)
  • 也就是說需要為每個共享節點設置一個共享計數器,用於記錄此時有多少個地方在共享該結點。只有共享計數器減為0,才刪除該節點。

文件共享

文件共享分為兩種

  1. 基於索引結點的共享方式(硬鏈接)
  2. 基於符號鏈的共享方式(軟鏈接)
    • 軟連接可以理解為windows里的快捷方式
    • 硬鏈接可以理解為js里的引用計數,只有引用為0的時候,才會真正刪除這個文件。

文件保護

操作系統需要保護文件的安全,一般有如下3種方式:

  • 口令保護。是指為文件設置一個“口令”(比如123),用戶請求訪問該文件時必須提供對應的口令。口令一般放在文件對應的FCB或者索引結點上。
  • 加密保護。使用某個"密碼"對文件進行加密,在訪問文件時需要提供正確的“密碼”才能對文件進行正確的解密。
  • 訪問控制。在每個文件的FCB或者索引節點種增加一個訪問控制列表,該表中記錄了各個用戶可以對該文件執行哪些操作。

I/O設備

什么是I/O設備

I/O就是輸入輸出(Input/Output)的意思,計算機的外部設備,屬於計算機中的硬件部件。

I/O設備分類--按使用特性

  • 人機交互類設備,這類設備傳輸數據的速度慢。例:鼠標、鍵盤、打印機。

  • 存儲設備,這類設備傳輸數據的速度較快。例:移動硬盤、光盤等。

  • 網絡通信設備,這類設備的傳輸速度介於人機交互設備和存儲設備之間

I/O控制器

CPU無法直接控制I/O設備的機械部件,因此I/O設備還要有一個電子部件作為CPUI/O設備機械部件之間的“中介”,用於實現CPU對設備的控制。這個電子部件就是I/O控制器

主要功能:

  • 接收和識別CPU發出的指令是指,比如CPU發來讀取文件的命令,I/O控制器中會有相應的控制寄存器來存放命令和參數
  • 向cpu報告設備的狀態是指,I/O控制器會有相應的狀態寄存器,用來記錄I/O設備是否空閑或者忙碌
  • 數據交換是指I/O控制器會設置相應的數據寄存器。輸出時,數據寄存器用於暫存CPU發來的數據,之后再由控制器傳送給設備。
  • 地址識別是指,為了區分設備控制器中的各個寄存器中的各個寄存器,也需要給各個寄存器設置一個特性的“地址”。I/O控制器通過CPU提供的“地址”來判斷CPU要讀寫的是哪個寄存器

I/O控制方式

  • 這里我們只講一下目前比較先進的方式,通道控制方式。
  • 通道可以理解為一種“弱雞版CPU”。通道可以識別並執行一系列通道指令。

通道最大的優點是極大的減少了CPU的干預頻率I/O設備完成任務,通道會向CPU發出中斷,不需要輪詢來問I/O設備是否完成CPU下達的任務。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM