學(xué)習(xí)啦>學(xué)習(xí)電腦>操作系統(tǒng)>操作系統(tǒng)基礎(chǔ)知識(shí)>

操作系統(tǒng)基礎(chǔ)知識(shí)大全

時(shí)間: 懷健0 分享

計(jì)算機(jī)系統(tǒng)中的一個(gè)系統(tǒng)軟件,它是這樣一些程序模塊的集合——它們管理和控制計(jì)算機(jī)系統(tǒng)中的軟硬件資源,合理地組織計(jì)算機(jī)的工作流程,以便有效地利用這些資源為用戶提供一個(gè)功能強(qiáng)大、使用方便和可擴(kuò)展的工作環(huán)境,從而在計(jì)算機(jī)與其用戶之間起到接口的作用。下面就讓小編帶你去看看操作系統(tǒng)基礎(chǔ)知識(shí)大全吧,希望能幫助到大家!

操作系統(tǒng)基礎(chǔ)知識(shí)筆記

一、操作系統(tǒng)相關(guān)概念

計(jì)算機(jī)軟件:系統(tǒng)軟件和應(yīng)用軟件。

計(jì)算機(jī)系統(tǒng)資源:硬件資源、軟件資源。

硬件資源:中央處理器、存儲(chǔ)器、輸入、輸出等物理設(shè)備。

軟件資源:以文件形式保存到存儲(chǔ)器上的程序和數(shù)據(jù)信息。

定義:有效地組織和管理系統(tǒng)的各種軟/硬件資源,合理組織計(jì)算機(jī)系統(tǒng)工作流程,控制程序的執(zhí)行,并給用戶提供一個(gè)良好的環(huán)境和友好的接口。

操作系統(tǒng)作用:通過資源管理提高計(jì)算機(jī)系統(tǒng)的效率、改善人家界面提高良好的工作環(huán)境。

吞吐量:計(jì)算機(jī)在單位時(shí)間內(nèi)處理工作的能力。

二、操作系統(tǒng)的特征與功能

操作系統(tǒng)的特征:并發(fā)性、共享性、虛擬性、隨機(jī)性。

2.1、 操作系統(tǒng)的功能

1、進(jìn)程管理:實(shí)際上是對(duì)處理機(jī)的執(zhí)行時(shí)間進(jìn)行管理,采用多道程序等技術(shù)將CPU的時(shí)間合理分配給每個(gè)任務(wù)。比如:進(jìn)程控制、進(jìn)程同步、進(jìn)程通信、進(jìn)程調(diào)度。

2、文件管理:主要有存儲(chǔ)空間管理、目錄管理、文件讀寫。

3、存儲(chǔ)管理:對(duì)主存儲(chǔ)器空間進(jìn)行管理,主要包括存儲(chǔ)空間分配回收、存儲(chǔ)保護(hù)、地址映射、主存擴(kuò)充等。

4、設(shè)備管理:對(duì)硬件設(shè)備的管理。包括分配、啟動(dòng)、完成、回收。

5、作業(yè)管理:包括任務(wù)、界面管理、人機(jī)交互、語(yǔ)音控制、虛擬現(xiàn)實(shí)等。

三、操作系統(tǒng)分類

1、批處理操作系統(tǒng)

分為單道批處理、多道批處理。

單道批處理:早期的操作系統(tǒng),一次只有一個(gè)作業(yè)裝入內(nèi)存執(zhí)行。作業(yè)由用戶程序、數(shù)據(jù)和作業(yè)說明書組成。一個(gè)作業(yè)運(yùn)行結(jié)束后,自動(dòng)調(diào)入同批的下一個(gè)作業(yè)。

多道批處理:允許多個(gè)作業(yè)裝入內(nèi)存執(zhí)行,在任意時(shí)刻,作業(yè)都處于開始和結(jié)束點(diǎn)之間。

多道批處理系統(tǒng)特點(diǎn):多道、宏觀上并行運(yùn)行、微觀上串行運(yùn)行。

2、分時(shí)操作系統(tǒng)

分時(shí)操作系統(tǒng)是將CPU的工作劃分為很短的時(shí)間片。輪流為各個(gè)終端的用戶服務(wù)。

分時(shí)操作系統(tǒng)特點(diǎn):多路性、獨(dú)立性、交互性、及時(shí)性。

3、實(shí)時(shí)操作系統(tǒng)

實(shí)時(shí)操作系統(tǒng)對(duì)交互能力要求不高,要能對(duì)外來(lái)信息足夠快的速度響應(yīng)和處理。分為實(shí)時(shí)控制系統(tǒng)和實(shí)時(shí)信息處理系統(tǒng)。

實(shí)時(shí)控制系統(tǒng):主要用于生產(chǎn)過程的自動(dòng)控制,比如自動(dòng)采集、飛機(jī)的自動(dòng)駕駛等。

實(shí)時(shí)信息處理系統(tǒng):主要是實(shí)時(shí)信息處理,比如飛機(jī)訂票系統(tǒng)、情報(bào)檢索系統(tǒng)等。

4、網(wǎng)絡(luò)操作系統(tǒng)

網(wǎng)絡(luò)操作系統(tǒng)使互聯(lián)網(wǎng)能方便有效的共享網(wǎng)絡(luò)資源,為網(wǎng)絡(luò)用戶提供各種服務(wù)軟件和有關(guān)協(xié)議的幾何。比如電子郵件、文件傳輸、共享硬盤等。

網(wǎng)絡(luò)操作系統(tǒng)分為如下三類:

1、集中式:系統(tǒng)的基本單元由一臺(tái)主機(jī)和若干臺(tái)主機(jī)相連的終端構(gòu)成,將多臺(tái)主機(jī)連接處理形成網(wǎng)絡(luò)。比如UNI__。

2、客戶端/服務(wù)器模式:該模式分為客戶端和服務(wù)器。服務(wù)器是網(wǎng)絡(luò)控制的中心,向客戶端提供多種服務(wù),客戶端主要是訪問服務(wù)端的資源。

3、對(duì)等模式(P2P):相當(dāng)于每一臺(tái)客戶端都可以給其他客戶端提供資源服務(wù)。

5、分布式操作系統(tǒng)

分布式操作系統(tǒng)是由多個(gè)分散的計(jì)算機(jī)經(jīng)連接而成的計(jì)算機(jī)系統(tǒng),系統(tǒng)中的計(jì)算機(jī)無(wú)主次之分,任意兩臺(tái)計(jì)算機(jī)都可以交換信息。分布式操作系統(tǒng)能直接對(duì)各類資源進(jìn)行動(dòng)態(tài)分配和調(diào)度、任務(wù)劃分、信息傳輸協(xié)調(diào)工作,為用戶提供一個(gè)統(tǒng)一的界面、標(biāo)準(zhǔn)的接口,用戶通過這一界面實(shí)現(xiàn)所需要的操作和使用系統(tǒng)資源。

6、微機(jī)操作系統(tǒng)

目前主流的操作系統(tǒng)有Linu__、MacOS、Windows。

7、嵌入式操作系統(tǒng)

嵌入式操作系統(tǒng)運(yùn)行在嵌入式智能芯片環(huán)境中,對(duì)整個(gè)智能芯片以及操作、控制、部件裝置等資源進(jìn)行統(tǒng)一協(xié)調(diào)、處理、指揮、控制。

嵌入式操作系統(tǒng)特點(diǎn):微型化、可定制、實(shí)時(shí)性、可靠性、易移植性。

操作系統(tǒng)基礎(chǔ)知識(shí)

一、概述

1. 操作系統(tǒng)基本特征

1. 并發(fā)

并發(fā)是指宏觀上在一段時(shí)間內(nèi)能同時(shí)運(yùn)行多個(gè)程序,而并行則指同一時(shí)刻能運(yùn)行多個(gè)指令。

并行需要硬件支持,如多流水線或者多處理器。

操作系統(tǒng)通過引入進(jìn)程和線程,使得程序能夠并發(fā)運(yùn)行。

2. 共享

共享是指系統(tǒng)中的資源可以被多個(gè)并發(fā)進(jìn)程共同使用。

有兩種共享方式:互斥共享和同時(shí)共享。

互斥共享的資源稱為臨界資源,例如打印機(jī)等,在同一時(shí)間只允許一個(gè)進(jìn)程訪問,需要用同步機(jī)制來(lái)實(shí)現(xiàn)對(duì)臨界資源的訪問。

3. 虛擬

虛擬技術(shù)把一個(gè)物理實(shí)體轉(zhuǎn)換為多個(gè)邏輯實(shí)體。

利用多道程序設(shè)計(jì)技術(shù),讓每個(gè)用戶都覺得有一個(gè)計(jì)算機(jī)專門為他服務(wù)。

主要有兩種虛擬技術(shù):時(shí)分復(fù)用技術(shù)和空分復(fù)用技術(shù)。例如多個(gè)進(jìn)程能在同一個(gè)處理器上并發(fā)執(zhí)行使用了時(shí)分復(fù)用技術(shù),讓每個(gè)進(jìn)程輪流占有處理器,每次只執(zhí)行一小個(gè)時(shí)間片并快速切換。

4. 異步

異步指進(jìn)程不是一次性執(zhí)行完畢,而是走走停停,以不可知的速度向前推進(jìn)。

但只要運(yùn)行環(huán)境相同,OS需要保證程序運(yùn)行的結(jié)果也要相同。

2. 操作系統(tǒng)基本功能

1. 進(jìn)程管理

進(jìn)程控制、進(jìn)程同步、進(jìn)程通信、死鎖處理、處理機(jī)調(diào)度等。

2. 內(nèi)存管理

內(nèi)存分配、地址映射、內(nèi)存保護(hù)與共享、虛擬內(nèi)存等。

3. 文件管理

文件存儲(chǔ)空間的管理、目錄管理、文件讀寫管理和保護(hù)等。

4. 設(shè)備管理

完成用戶的 I/O 請(qǐng)求,方便用戶使用各種設(shè)備,并提高設(shè)備的利用率。

主要包括緩沖管理、設(shè)備分配、設(shè)備處理、虛擬設(shè)備等。

3. 系統(tǒng)調(diào)用

如果一個(gè)進(jìn)程在用戶態(tài)需要使用內(nèi)核態(tài)的功能,就進(jìn)行系統(tǒng)調(diào)用從而陷入內(nèi)核,由操作系統(tǒng)代為完成。

4. 大內(nèi)核和微內(nèi)核

1. 大內(nèi)核

大內(nèi)核是將操作系統(tǒng)功能作為一個(gè)緊密結(jié)合的整體放到內(nèi)核。

由于各模塊共享信息,因此有很高的性能。

2. 微內(nèi)核

由于操作系統(tǒng)不斷復(fù)雜,因此將一部分操作系統(tǒng)功能移出內(nèi)核,從而降低內(nèi)核的復(fù)雜性。移出的部分根據(jù)分層的原則劃分成若干服務(wù),相互獨(dú)立。

在微內(nèi)核結(jié)構(gòu)下,操作系統(tǒng)被劃分成小的、定義良好的模塊,只有微內(nèi)核這一個(gè)模塊運(yùn)行在內(nèi)核態(tài),其余模塊運(yùn)行在用戶態(tài)。

因?yàn)樾枰l繁地在用戶態(tài)和核心態(tài)之間進(jìn)行切換,所以會(huì)有一定的性能損失。

5. 中斷分類

1. 外中斷

由 CPU 執(zhí)行指令以外的事件引起,如 I/O 完成中斷,表示設(shè)備輸入/輸出處理已經(jīng)完成,處理器能夠發(fā)送下一個(gè)輸入/輸出請(qǐng)求。此外還有時(shí)鐘中斷、控制臺(tái)中斷等。

2. 異常

由 CPU 執(zhí)行指令的內(nèi)部事件引起,如非法操作碼、地址越界、算術(shù)溢出等。

6. 什么是堆和棧?說一下堆棧都存儲(chǔ)哪些數(shù)據(jù)?

棧區(qū)(stack)— 由編譯器自動(dòng)分配釋放 ,存放函數(shù)的參數(shù)值,局部變量的值等。其操作方式類似于數(shù)據(jù)結(jié)構(gòu)中的棧。

堆區(qū)(heap) — 一般由程序員分配釋放, 若程序員不釋放,程序結(jié)束時(shí)可能由OS回收 。

數(shù)據(jù)結(jié)構(gòu)中這兩個(gè)完全就不放一塊來(lái)講,數(shù)據(jù)結(jié)構(gòu)中棧和隊(duì)列才是好基友,我想新手也很容易區(qū)分。

我想需要區(qū)分的情況肯定不是在數(shù)據(jù)結(jié)構(gòu)話題下,而大多是在 OS 關(guān)于不同對(duì)象的內(nèi)存分配這塊上。

簡(jiǎn)單講的話,在 C 語(yǔ)言中:

int a[N]; // go on a stackint__ a = (int __)malloc(sizeof(int) __ N); // go on a heap

7. 如何理解分布式鎖?

分布式鎖,是控制分布式系統(tǒng)之間同步訪問共享資源的一種方式。在分布式系統(tǒng)中,常常需要協(xié)調(diào)他們的動(dòng)作。如果不同的系統(tǒng)或是同一個(gè)系統(tǒng)的不同主機(jī)之間共享了一個(gè)或一組資源,那么訪問這些資源的時(shí)候,往往需要互斥來(lái)防止彼此干擾來(lái)保證一致性,在這種情況下,便需要使用到分布式鎖。

二、進(jìn)程管理

1. 進(jìn)程與線程

1. 進(jìn)程

進(jìn)程是資源分配的基本單位,用來(lái)管理資源(例如:內(nèi)存,文件,網(wǎng)絡(luò)等資源)

進(jìn)程控制塊 (Process Control Block, PCB) 描述進(jìn)程的基本信息和運(yùn)行狀態(tài),所謂的創(chuàng)建進(jìn)程和撤銷進(jìn)程,都是指對(duì) PCB 的操作。(PCB是描述進(jìn)程的數(shù)據(jù)結(jié)構(gòu))

下圖顯示了 4 個(gè)程序創(chuàng)建了 4 個(gè)進(jìn)程,這 4 個(gè)進(jìn)程可以并發(fā)地執(zhí)行。

2. 線程

線程是獨(dú)立調(diào)度的基本單位。

一個(gè)進(jìn)程中可以有多個(gè)線程,它們共享進(jìn)程資源。

QQ 和瀏覽器是兩個(gè)進(jìn)程,瀏覽器進(jìn)程里面有很多線程,例如 HTTP 請(qǐng)求線程、事件響應(yīng)線程、渲染線程等等,線程的并發(fā)執(zhí)行使得在瀏覽器中點(diǎn)擊一個(gè)新鏈接從而發(fā)起 HTTP 請(qǐng)求時(shí),瀏覽器還可以響應(yīng)用戶的其它事件。

3. 區(qū)別

(一)擁有資源

進(jìn)程是資源分配的基本單位,但是線程不擁有資源,線程可以訪問隸屬進(jìn)程的資源。

(二)調(diào)度

線程是獨(dú)立調(diào)度的基本單位,在同一進(jìn)程中,線程的切換不會(huì)引起進(jìn)程切換,從一個(gè)進(jìn)程內(nèi)的線程切換到另一個(gè)進(jìn)程中的線程時(shí),會(huì)引起進(jìn)程切換。

(三)系統(tǒng)開銷

由于創(chuàng)建或撤銷進(jìn)程時(shí),系統(tǒng)都要為之分配或回收資源,如內(nèi)存空間、I/O 設(shè)備等,所付出的開銷遠(yuǎn)大于創(chuàng)建或撤銷線程時(shí)的開銷。類似地,在進(jìn)行進(jìn)程切換時(shí),涉及當(dāng)前執(zhí)行進(jìn)程 CPU 環(huán)境的保存及新調(diào)度進(jìn)程 CPU 環(huán)境的設(shè)置,而線程切換時(shí)只需保存和設(shè)置少量寄存器內(nèi)容,開銷很小。

(四)通信方面

進(jìn)程間通信 (IPC) 需要進(jìn)程同步和互斥手段的輔助,以保證數(shù)據(jù)的一致性。而線程間可以通過直接讀/寫同一進(jìn)程中的數(shù)據(jù)段(如全局變量)來(lái)進(jìn)行通信。

2. 進(jìn)程狀態(tài)的切換(生命周期)

就緒狀態(tài)(ready):等待被調(diào)度

運(yùn)行狀態(tài)(running)

阻塞狀態(tài)(waiting):等待資源

應(yīng)該注意以下內(nèi)容:

只有就緒態(tài)和運(yùn)行態(tài)可以相互轉(zhuǎn)換,其它的都是單向轉(zhuǎn)換。就緒狀態(tài)的進(jìn)程通過調(diào)度算法從而獲得 CPU 時(shí)間,轉(zhuǎn)為運(yùn)行狀態(tài);而運(yùn)行狀態(tài)的進(jìn)程,在分配給它的 CPU 時(shí)間片用完之后就會(huì)轉(zhuǎn)為就緒狀態(tài),等待下一次調(diào)度。

阻塞狀態(tài)是缺少需要的資源從而由運(yùn)行狀態(tài)轉(zhuǎn)換而來(lái),但是該資源不包括 CPU 時(shí)間,缺少 CPU 時(shí)間會(huì)從運(yùn)行態(tài)轉(zhuǎn)換為就緒態(tài)。

進(jìn)程只能自己阻塞自己,因?yàn)橹挥羞M(jìn)程自身才知道何時(shí)需要等待某種事件的發(fā)生

3. 進(jìn)程調(diào)度算法

不同環(huán)境的調(diào)度算法目標(biāo)不同,因此需要針對(duì)不同環(huán)境來(lái)討論調(diào)度算法。

1. 批處理系統(tǒng)

批處理系統(tǒng)沒有太多的用戶操作,在該系統(tǒng)中,調(diào)度算法目標(biāo)是保證吞吐量和周轉(zhuǎn)時(shí)間(從提交到終止的時(shí)間)。

1.1 先來(lái)先服務(wù)

先來(lái)先服務(wù) first-come first-serverd(FCFS)

按照請(qǐng)求的順序進(jìn)行調(diào)度。

有利于長(zhǎng)作業(yè),但不利于短作業(yè),因?yàn)槎套鳂I(yè)必須一直等待前面的長(zhǎng)作業(yè)執(zhí)行完畢才能執(zhí)行,而長(zhǎng)作業(yè)又需要執(zhí)行很長(zhǎng)時(shí)間,造成了短作業(yè)等待時(shí)間過長(zhǎng)。

1.2 短作業(yè)優(yōu)先

短作業(yè)優(yōu)先 shortest job first(SJF)

按估計(jì)運(yùn)行時(shí)間最短的順序進(jìn)行調(diào)度。

長(zhǎng)作業(yè)有可能會(huì)餓死,處于一直等待短作業(yè)執(zhí)行完畢的狀態(tài)。因?yàn)槿绻恢庇卸套鳂I(yè)到來(lái),那么長(zhǎng)作業(yè)永遠(yuǎn)得不到調(diào)度。

1.3 最短剩余時(shí)間優(yōu)先

最短剩余時(shí)間優(yōu)先 shortest remaining time ne__t(SRTN)

按估計(jì)剩余時(shí)間最短的順序進(jìn)行調(diào)度。

2. 交互式系統(tǒng)

交互式系統(tǒng)有大量的用戶交互操作,在該系統(tǒng)中調(diào)度算法的目標(biāo)是快速地進(jìn)行響應(yīng)。

2.1 時(shí)間片輪轉(zhuǎn)

將所有就緒進(jìn)程按 FCFS (先來(lái)先服務(wù)) 的原則排成一個(gè)隊(duì)列,每次調(diào)度時(shí),把 CPU 時(shí)間分配給隊(duì)首進(jìn)程,該進(jìn)程可以執(zhí)行一個(gè)時(shí)間片。當(dāng)時(shí)間片用完時(shí),由計(jì)時(shí)器發(fā)出時(shí)鐘中斷,調(diào)度程序便停止該進(jìn)程的執(zhí)行,并將它送往就緒隊(duì)列的末尾,同時(shí)繼續(xù)把 CPU 時(shí)間分配給隊(duì)首的進(jìn)程。

時(shí)間片輪轉(zhuǎn)算法的效率和時(shí)間片的大小有很大關(guān)系。因?yàn)檫M(jìn)程切換都要保存進(jìn)程的信息并且載入新進(jìn)程的信息,如果時(shí)間片太小,會(huì)導(dǎo)致進(jìn)程切換得太頻繁,在進(jìn)程切換上就會(huì)花過多時(shí)間。

2.2 優(yōu)先級(jí)調(diào)度

為每個(gè)進(jìn)程分配一個(gè)優(yōu)先級(jí),按優(yōu)先級(jí)進(jìn)行調(diào)度。

為了防止低優(yōu)先級(jí)的進(jìn)程永遠(yuǎn)等不到調(diào)度,可以隨著時(shí)間的推移增加等待進(jìn)程的優(yōu)先級(jí)。

2.3 多級(jí)反饋隊(duì)列

如果一個(gè)進(jìn)程需要執(zhí)行 100 個(gè)時(shí)間片,如果采用時(shí)間片輪轉(zhuǎn)調(diào)度算法,那么需要交換 100 次。

多級(jí)隊(duì)列是為這種需要連續(xù)執(zhí)行多個(gè)時(shí)間片的進(jìn)程考慮,它設(shè)置了多個(gè)隊(duì)列,每個(gè)隊(duì)列時(shí)間片大小都不同,例如 1,2,4,8,..。進(jìn)程在第一個(gè)隊(duì)列沒執(zhí)行完,就會(huì)被移到下一個(gè)隊(duì)列。這種方式下,之前的進(jìn)程只需要交換 7 次。

每個(gè)隊(duì)列優(yōu)先權(quán)也不同,最上面的優(yōu)先權(quán)最高。因此只有上一個(gè)隊(duì)列沒有進(jìn)程在排隊(duì),才能調(diào)度當(dāng)前隊(duì)列上的進(jìn)程。

可以將這種調(diào)度算法看成是時(shí)間片輪轉(zhuǎn)調(diào)度算法和優(yōu)先級(jí)調(diào)度算法的結(jié)合。

3. 實(shí)時(shí)系統(tǒng)

實(shí)時(shí)系統(tǒng)要求一個(gè)請(qǐng)求在一個(gè)確定時(shí)間內(nèi)得到響應(yīng)。

分為硬實(shí)時(shí)和軟實(shí)時(shí),前者必須滿足絕對(duì)的截止時(shí)間,后者可以容忍一定的超時(shí)。

參考資料:

操作系統(tǒng)典型調(diào)度算法_C語(yǔ)言中文網(wǎng)

4. 進(jìn)程同步

1. 臨界區(qū)

對(duì)臨界資源進(jìn)行訪問的那段代碼稱為臨界區(qū)。

為了互斥訪問臨界資源,每個(gè)進(jìn)程在進(jìn)入臨界區(qū)之前,需要先進(jìn)行檢查。

// entry section// critical section;// e__it section

2. 同步與互斥

同步:多個(gè)進(jìn)程按一定順序執(zhí)行;

互斥:多個(gè)進(jìn)程在同一時(shí)刻只有一個(gè)進(jìn)程能進(jìn)入臨界區(qū)。

3. 信號(hào)量

P 和 V 是來(lái)源于兩個(gè)荷蘭語(yǔ)詞匯,P() ---prolaag (荷蘭語(yǔ),嘗試減少的意思),V() ---verhoog(荷蘭語(yǔ),增加的意思)

信號(hào)量(Semaphore)是一個(gè)整型變量,可以對(duì)其執(zhí)行 down 和 up 操作,也就是常見的 P 和 V 操作。

down : 如果信號(hào)量大于 0 ,執(zhí)行 -1 操作;如果信號(hào)量等于 0,進(jìn)程睡眠,等待信號(hào)量大于 0;(阻塞)

up :對(duì)信號(hào)量執(zhí)行 +1 操作,喚醒睡眠的進(jìn)程讓其完成 down 操作。(喚醒)

down 和 up 操作需要被設(shè)計(jì)成原語(yǔ),不可分割,通常的做法是在執(zhí)行這些操作的時(shí)候屏蔽中斷。

如果信號(hào)量的取值只能為 0 或者 1,那么就成為了 互斥量(Mute__) ,0 表示臨界區(qū)已經(jīng)加鎖,1 表示臨界區(qū)解鎖。

typedef int semaphore;semaphore mute__ = 1;void P1() { down(&mute__); // 臨界區(qū) up(&mute__);}void P2() { down(&mute__); // 臨界區(qū) up(&mute__);}

使用信號(hào)量實(shí)現(xiàn)生產(chǎn)者-消費(fèi)者問題

問題描述:使用一個(gè)緩沖區(qū)來(lái)保存物品,只有緩沖區(qū)沒有滿,生產(chǎn)者才可以放入物品;只有緩沖區(qū)不為空,消費(fèi)者才可以拿走物品。

因?yàn)榫彌_區(qū)屬于臨界資源,因此需要使用一個(gè)互斥量 mute__ 來(lái)控制對(duì)緩沖區(qū)的互斥訪問。

為了同步生產(chǎn)者和消費(fèi)者的行為,需要記錄緩沖區(qū)中物品的數(shù)量。數(shù)量可以使用信號(hào)量來(lái)進(jìn)行統(tǒng)計(jì),這里需要使用兩個(gè)信號(hào)量:empty 記錄空緩沖區(qū)的數(shù)量,full 記錄滿緩沖區(qū)的數(shù)量。其中,empty 信號(hào)量是在生產(chǎn)者進(jìn)程中使用,當(dāng) empty 不為 0 時(shí),生產(chǎn)者才可以放入物品;full 信號(hào)量是在消費(fèi)者進(jìn)程中使用,當(dāng) full 信號(hào)量不為 0 時(shí),消費(fèi)者才可以取走物品。

注意,不能先對(duì)緩沖區(qū)進(jìn)行加鎖,再測(cè)試信號(hào)量。也就是說,不能先執(zhí)行 down(mute__) 再執(zhí)行 down(empty)。如果這么做了,那么可能會(huì)出現(xiàn)這種情況:生產(chǎn)者對(duì)緩沖區(qū)加鎖后,執(zhí)行 down(empty) 操作,發(fā)現(xiàn) empty = 0,此時(shí)生產(chǎn)者睡眠。消費(fèi)者不能進(jìn)入臨界區(qū),因?yàn)樯a(chǎn)者對(duì)緩沖區(qū)加鎖了,也就無(wú)法執(zhí)行 up(empty) 操作,empty 永遠(yuǎn)都為 0,那么生產(chǎn)者和消費(fèi)者就會(huì)一直等待下去,造成死鎖。

#define N 100typedef int semaphore;semaphore mute__ = 1;semaphore empty = N;semaphore full = 0;void producer() { while(TRUE){ int item = produce_item(); // 生產(chǎn)一個(gè)產(chǎn)品 // down(&empty) 和 down(&mute__) 不能交換位置,否則造成死鎖 down(&empty); // 記錄空緩沖區(qū)的數(shù)量,這里減少一個(gè)產(chǎn)品空間 down(&mute__); // 互斥鎖 insert_item(item); up(&mute__); // 互斥鎖 up(&full); // 記錄滿緩沖區(qū)的數(shù)量,這里增加一個(gè)產(chǎn)品 }}void consumer() { while(TRUE){ down(&full); // 記錄滿緩沖區(qū)的數(shù)量,減少一個(gè)產(chǎn)品 down(&mute__); // 互斥鎖 int item = remove_item(); up(&mute__); // 互斥鎖 up(&empty); // 記錄空緩沖區(qū)的數(shù)量,這里增加一個(gè)產(chǎn)品空間 consume_item(item); }}

4. 管程

管程 (英語(yǔ):Monitors,也稱為監(jiān)視器) 是一種程序結(jié)構(gòu),結(jié)構(gòu)內(nèi)的多個(gè)子程序(對(duì)象或模塊)形成的多個(gè)工作線程互斥訪問共享資源。

使用信號(hào)量機(jī)制實(shí)現(xiàn)的生產(chǎn)者消費(fèi)者問題需要客戶端代碼做很多控制,而管程把控制的代碼獨(dú)立出來(lái),不僅不容易出錯(cuò),也使得客戶端代碼調(diào)用更容易。

管程是為了解決信號(hào)量在臨界區(qū)的 PV 操作上的配對(duì)的麻煩,把配對(duì)的 PV 操作集中在一起,生成的一種并發(fā)編程方法。其中使用了條件變量這種同步機(jī)制。

c 語(yǔ)言不支持管程,下面的示例代碼使用了類 Pascal 語(yǔ)言來(lái)描述管程。示例代碼的管程提供了 insert() 和 remove() 方法,客戶端代碼通過調(diào)用這兩個(gè)方法來(lái)解決生產(chǎn)者-消費(fèi)者問題。

monitor ProducerConsumer integer i; condition c; procedure insert(); begin // ... end; procedure remove(); begin // ... end;end monitor;

管程有一個(gè)重要特性:在一個(gè)時(shí)刻只能有一個(gè)進(jìn)程使用管程。進(jìn)程在無(wú)法繼續(xù)執(zhí)行的時(shí)候不能一直占用管程,否者其它進(jìn)程永遠(yuǎn)不能使用管程。

管程引入了 條件變量 以及相關(guān)的操作:wait() 和 signal() 來(lái)實(shí)現(xiàn)同步操作。對(duì)條件變量執(zhí)行 wait() 操作會(huì)導(dǎo)致調(diào)用進(jìn)程阻塞,把管程讓出來(lái)給另一個(gè)進(jìn)程持有。signal() 操作用于喚醒被阻塞的進(jìn)程。

使用管程實(shí)現(xiàn)生產(chǎn)者-消費(fèi)者問題

// 管程monitor ProducerConsumer condition full, empty; integer count := 0; condition c; procedure insert(item: integer); begin if count = N then wait(full); insert_item(item); count := count + 1; if count = 1 then signal(empty); end; function remove: integer; begin if count = 0 then wait(empty); remove = remove_item; count := count - 1; if count = N -1 then signal(full); end;end monitor;// 生產(chǎn)者客戶端procedure producerbegin while true do begin item = produce_item; ProducerConsumer.insert(item); endend;// 消費(fèi)者客戶端procedure consumerbegin while true do begin item = ProducerConsumer.remove; consume_item(item); endend;

5. 經(jīng)典同步問題

生產(chǎn)者和消費(fèi)者問題前面已經(jīng)討論過了。

1. 讀者-寫者問題

允許多個(gè)進(jìn)程同時(shí)對(duì)數(shù)據(jù)進(jìn)行讀操作,但是不允許讀和寫以及寫和寫操作同時(shí)發(fā)生。讀者優(yōu)先策略

Rcount:讀操作的進(jìn)程數(shù)量(Rcount=0)

CountMute__:對(duì)于Rcount進(jìn)行加鎖(CountMute__=1)

WriteMute__:互斥量對(duì)于寫操作的加鎖(WriteMute__=1)

Rcount = 0;semaphore CountMute__ = 1;semaphore WriteMute__ = 1;void writer(){ while(true){ sem_wait(WriteMute__); // TO DO write(); sem_post(WriteMute__); }}// 讀者優(yōu)先策略void reader(){ while(true){ sem_wait(CountMute__); if(Rcount == 0) sem_wait(WriteMute__); Rcount++; sem_post(CountMute__); // TO DO read(); sem_wait(CountMute__); Rcount--; if(Rcount == 0) sem_post(WriteMute__); sem_post(CountMute__); }}

2. 哲學(xué)家進(jìn)餐問題

五個(gè)哲學(xué)家圍著一張圓桌,每個(gè)哲學(xué)家面前放著食物。哲學(xué)家的生活有兩種交替活動(dòng):吃飯以及思考。當(dāng)一個(gè)哲學(xué)家吃飯時(shí),需要先拿起自己左右兩邊的兩根筷子,并且一次只能拿起一根筷子。

____方案一:____下面是一種錯(cuò)誤的解法,考慮到如果所有哲學(xué)家同時(shí)拿起左手邊的筷子,那么就無(wú)法拿起右手邊的筷子,造成死鎖。

#define N 5 // 哲學(xué)家個(gè)數(shù)void philosopher(int i) // 哲學(xué)家編號(hào):0 - 4{ while(TRUE) { think(); // 哲學(xué)家在思考 take_fork(i); // 去拿左邊的叉子 take_fork((i + 1) % N); // 去拿右邊的叉子 eat(); // 吃面條中…. put_fork(i); // 放下左邊的叉子 put_fork((i + 1) % N); // 放下右邊的叉子 }}

方案二:對(duì)拿叉子的過程進(jìn)行了改進(jìn),但仍不正確

#define N 5 // 哲學(xué)家個(gè)數(shù)while(1) // 去拿兩把叉子{ take_fork(i); // 去拿左邊的叉子 if(fork((i+1)%N)) { // 右邊叉子還在嗎 take_fork((i + 1) % N);// 去拿右邊的叉子 break; // 兩把叉子均到手 } else { // 右邊叉子已不在 put_fork(i); // 放下左邊的叉子 wait_some_time(); // 等待一會(huì)兒 }}

方案三:等待時(shí)間隨機(jī)變化。可行,但非萬(wàn)全之策

#define N 5 // 哲學(xué)家個(gè)數(shù)while(1) // 去拿兩把叉子{ take_fork(i); // 去拿左邊的叉子 if(fork((i+1)%N)) { // 右邊叉子還在嗎 take_fork((i + 1) % N);// 去拿右邊的叉子 break; // 兩把叉子均到手 } else { // 右邊叉子已不在 put_fork(i); // 放下左邊的叉子 wait_random_time( ); // 等待隨機(jī)長(zhǎng)時(shí)間 }}

方案四:互斥訪問。正確,但每次只允許一人進(jìn)餐

semaphore mute__ // 互斥信號(hào)量,初值1void philosopher(int i) // 哲學(xué)家編號(hào)i:0-4 { while(TRUE){ think(); // 哲學(xué)家在思考 P(mute__); // 進(jìn)入臨界區(qū) take_fork(i); // 去拿左邊的叉子 take_fork((i + 1) % N); // 去拿右邊的叉子 eat(); // 吃面條中…. put_fork(i); // 放下左邊的叉子 put_fork((i + 1) % N); // 放下右邊的叉子 V(mute__); // 退出臨界區(qū) }}

正確方案如下:

為了防止死鎖的發(fā)生,可以設(shè)置兩個(gè)條件(臨界資源):

必須同時(shí)拿起左右兩根筷子;

只有在兩個(gè)鄰居都沒有進(jìn)餐的情況下才允許進(jìn)餐。

//1. 必須由一個(gè)數(shù)據(jù)結(jié)構(gòu),來(lái)描述每個(gè)哲學(xué)家當(dāng)前的狀態(tài)#define N 5#define LEFT i // 左鄰居#define RIGHT (i + 1) % N // 右鄰居#define THINKING 0#define HUNGRY 1#define EATING 2typedef int semaphore;int state[N]; // 跟蹤每個(gè)哲學(xué)家的狀態(tài)//2. 該狀態(tài)是一個(gè)臨界資源,對(duì)它的訪問應(yīng)該互斥地進(jìn)行semaphore mute__ = 1; // 臨界區(qū)的互斥//3. 一個(gè)哲學(xué)家吃飽后,可能要喚醒鄰居,存在著同步關(guān)系semaphore s[N]; // 每個(gè)哲學(xué)家一個(gè)信號(hào)量void philosopher(int i) { while(TRUE) { think(); take_two(i); eat(); put_tow(i); }}void take_two(int i) { P(&mute__); // 進(jìn)入臨界區(qū) state[i] = HUNGRY; // 我餓了 test(i); // 試圖拿兩把叉子 V(&mute__); // 退出臨界區(qū) P(&s[i]); // 沒有叉子便阻塞}void put_tow(i) { P(&mute__); state[i] = THINKING; test(LEFT); test(RIGHT); V(&mute__);}void test(i) { // 嘗試拿起兩把筷子 if(state[i] == HUNGRY && state[LEFT] != EATING && state[RIGHT] !=EATING) { state[i] = EATING; V(&s[i]); // 通知第i個(gè)人可以吃飯了 }}

6. 進(jìn)程通信

進(jìn)程同步與進(jìn)程通信很容易混淆,它們的區(qū)別在于:

進(jìn)程同步:控制多個(gè)進(jìn)程按一定順序執(zhí)行

進(jìn)程通信:進(jìn)程間傳輸信息

進(jìn)程通信是一種手段,而進(jìn)程同步是一種目的。也可以說,為了能夠達(dá)到進(jìn)程同步的目的,需要讓進(jìn)程進(jìn)行通信,傳輸一些進(jìn)程同步所需要的信息。

__ 進(jìn)程通信方式

直接通信

發(fā)送進(jìn)程直接把消息發(fā)送給接收進(jìn)程,并將它掛在接收進(jìn)程的消息緩沖隊(duì)列上,接收進(jìn)程從消息緩沖隊(duì)列中取得消息。

Send 和 Receive 原語(yǔ)的使用格式如下:

Send(Receiver,message);//發(fā)送一個(gè)消息message給接收進(jìn)程ReceiverReceive(Sender,message);//接收Sender進(jìn)程發(fā)送的消息message

間接通信

間接通信方式是指進(jìn)程之間的通信需要通過作為共享數(shù)據(jù)結(jié)構(gòu)的實(shí)體。該實(shí)體用來(lái)暫存發(fā)送進(jìn)程發(fā)給目標(biāo)進(jìn)程的消息。

發(fā)送進(jìn)程把消息發(fā)送到某個(gè)中間實(shí)體中,接收進(jìn)程從中間實(shí)體中取得消息。這種中間實(shí)體一般稱為信箱,這種通信方式又稱為信箱通信方式。該通信方式廣泛應(yīng)用于計(jì)算機(jī)網(wǎng)絡(luò)中,相應(yīng)的通信系統(tǒng)稱為電子郵件系統(tǒng)。

1. 管道

管道是通過調(diào)用 pipe 函數(shù)創(chuàng)建的,fd[0] 用于讀,fd[1] 用于寫。

#include int pipe(int fd[2]);

它具有以下限制:

只支持半雙工通信(單向傳輸);

只能在父子進(jìn)程中使用。

2. 命名管道

也稱為命名管道,去除了管道只能在父子進(jìn)程中使用的限制。

#include int mkfifo(const char __path, mode_t mode);int mkfifoat(int fd, const char __path, mode_t mode);

FIFO 常用于客戶-服務(wù)器應(yīng)用程序中,F(xiàn)IFO 用作匯聚點(diǎn),在客戶進(jìn)程和服務(wù)器進(jìn)程之間傳遞數(shù)據(jù)。

3. 消息隊(duì)列

間接(內(nèi)核)

相比于 FIFO,消息隊(duì)列具有以下優(yōu)點(diǎn):

消息隊(duì)列可以獨(dú)立于讀寫進(jìn)程存在,從而避免了 FIFO 中同步管道的打開和關(guān)閉時(shí)可能產(chǎn)生的困難;

避免了 FIFO 的同步阻塞問題,不需要進(jìn)程自己提供同步方法;

讀進(jìn)程可以根據(jù)消息類型有選擇地接收消息,而不像 FIFO 那樣只能默認(rèn)地接收。

4. 信號(hào)量

它是一個(gè)計(jì)數(shù)器,用于為多個(gè)進(jìn)程提供對(duì)共享數(shù)據(jù)對(duì)象的訪問。

5. 共享內(nèi)存

允許多個(gè)進(jìn)程共享一個(gè)給定的存儲(chǔ)區(qū)。因?yàn)閿?shù)據(jù)不需要在進(jìn)程之間復(fù)制,所以這是最快的一種 IPC。

需要使用信號(hào)量用來(lái)同步對(duì)共享存儲(chǔ)的訪問。

多個(gè)進(jìn)程可以將同一個(gè)文件映射到它們的地址空間從而實(shí)現(xiàn)共享內(nèi)存。另外 __SI 共享內(nèi)存不是使用文件,而是使用使用內(nèi)存的匿名段。

6. 套接字

與其它通信機(jī)制不同的是,它可用于不同機(jī)器間的進(jìn)程通信。

7. 線程間通信和進(jìn)程間通信

線程間通信

synchronized同步

這種方式,本質(zhì)上就是 “共享內(nèi)存” 式的通信。多個(gè)線程需要訪問同一個(gè)共享變量,誰(shuí)拿到了鎖(獲得了訪問權(quán)限),誰(shuí)就可以執(zhí)行。

while輪詢的方式

在這種方式下,ThreadA 不斷地改變條件,ThreadB 不停地通過 while 語(yǔ)句檢測(cè)這個(gè)條件 (list.size()==5) 是否成立 ,從而實(shí)現(xiàn)了線程間的通信。但是這種方式會(huì)浪費(fèi) CPU 資源。

之所以說它浪費(fèi)資源,是因?yàn)?JVM 調(diào)度器將 CPU 交給 ThreadB 執(zhí)行時(shí),它沒做啥 “有用” 的工作,只是在不斷地測(cè)試某個(gè)條件是否成立。

就類似于現(xiàn)實(shí)生活中,某個(gè)人一直看著手機(jī)屏幕是否有電話來(lái)了,而不是:在干別的事情,當(dāng)有電話來(lái)時(shí),響鈴?fù)ㄖ猅A電話來(lái)了。

wait/notify機(jī)制

當(dāng)條件未滿足時(shí),ThreadA 調(diào)用 wait() 放棄 CPU,并進(jìn)入阻塞狀態(tài)。(不像 while 輪詢那樣占用 CPU)

當(dāng)條件滿足時(shí),ThreadB 調(diào)用 notify() 通知線程 A,所謂通知線程 A,就是喚醒線程 A,并讓它進(jìn)入可運(yùn)行狀態(tài)。

管道通信

java.io.PipedInputStream 和 java.io.PipedOutputStream進(jìn)行通信

進(jìn)程間通信

管道(Pipe) :管道可用于具有親緣關(guān)系進(jìn)程間的通信,允許一個(gè)進(jìn)程和另一個(gè)與它有共同祖先的進(jìn)程之間進(jìn)行通信。

命名管道(named pipe) :命名管道克服了管道沒有名字的限制,因此,除具有管道所具有的功能外,它還允許無(wú)親緣關(guān) 系 進(jìn)程間的通信。命名管道在文件系統(tǒng)中有對(duì)應(yīng)的文件名。命名管道通過命令mkfifo或系統(tǒng)調(diào)用mkfifo來(lái)創(chuàng)建。

信號(hào)(Signal) :信號(hào)是比較復(fù)雜的通信方式,用于通知接受進(jìn)程有某種事件發(fā)生,除了用于進(jìn)程間通信外,進(jìn)程還可以發(fā)送 信號(hào)給進(jìn)程本身;Linu__除了支持Uni__早期信號(hào)語(yǔ)義函數(shù)sigal外,還支持語(yǔ)義符合Posi__.1標(biāo)準(zhǔn)的信號(hào)函數(shù)sigaction(實(shí)際上,該函數(shù)是基于BSD的,BSD為了實(shí)現(xiàn)可靠信號(hào)機(jī)制,又能夠統(tǒng)一對(duì)外接口,用sigaction函數(shù)重新實(shí)現(xiàn)了signal函數(shù))。

消息(Message)隊(duì)列 :消息隊(duì)列是消息的鏈接表,包括Posi__消息隊(duì)列system V消息隊(duì)列。有足夠權(quán)限的進(jìn)程可以向隊(duì)列中添加消息,被賦予讀權(quán)限的進(jìn)程則可以讀走隊(duì)列中的消息。消息隊(duì)列克服了信號(hào)承載信息量少,管道只能承載無(wú)格式字節(jié)流以及緩沖區(qū)大小受限等缺

共享內(nèi)存 :使得多個(gè)進(jìn)程可以訪問同一塊內(nèi)存空間,是最快的可用IPC形式。是針對(duì)其他通信機(jī)制運(yùn)行效率較低而設(shè)計(jì)的。往往與其它通信機(jī)制,如信號(hào)量結(jié)合使用,來(lái)達(dá)到進(jìn)程間的同步及互斥。

內(nèi)存映射(mapped memory) :內(nèi)存映射允許任何多個(gè)進(jìn)程間通信,每一個(gè)使用該機(jī)制的進(jìn)程通過把一個(gè)共享的文件映射到自己的進(jìn)程地址空間來(lái)實(shí)現(xiàn)它。

信號(hào)量(semaphore) :主要作為進(jìn)程間以及同一進(jìn)程不同線程之間的同步手段。

套接口(Socket) :更為一般的進(jìn)程間通信機(jī)制,可用于不同機(jī)器之間的進(jìn)程間通信。起初是由Uni__系統(tǒng)的BSD分支開發(fā)出來(lái)的,但現(xiàn)在一般可以移植到其它類Uni__系統(tǒng)上:linu__和System V的變種都支持套接字。

8. 進(jìn)程操作

Linu__進(jìn)程結(jié)構(gòu)可由三部分組成:

代碼段(程序)

數(shù)據(jù)段(數(shù)據(jù))

堆棧段(控制塊PCB)

進(jìn)程控制塊是進(jìn)程存在的惟一標(biāo)識(shí),系統(tǒng)通過PCB的存在而感知進(jìn)程的存在。系統(tǒng)通過 PCB 對(duì)進(jìn)程進(jìn)行管理和調(diào)度。PCB 包括創(chuàng)建進(jìn)程、執(zhí)行進(jìn)程、退出進(jìn)程以及改變進(jìn)程的優(yōu)先級(jí)等。

一般程序轉(zhuǎn)換為進(jìn)程分以下幾個(gè)步驟:

內(nèi)核將程序讀入內(nèi)存,為程序分配內(nèi)存空間

內(nèi)核為該進(jìn)程分配進(jìn)程標(biāo)識(shí)符 PID 和其他所需資源

內(nèi)核為進(jìn)程保存 PID 及相應(yīng)的狀態(tài)信息,把進(jìn)程放到運(yùn)行隊(duì)列中等待執(zhí)行,程序轉(zhuǎn)化為進(jìn)程后可以被操作系統(tǒng)的調(diào)度程序調(diào)度執(zhí)行了

在 UNI__ 里,除了進(jìn)程 0(即 PID=0 的交換進(jìn)程,Swapper Process)以外的所有進(jìn)程都是由其他進(jìn)程使用系統(tǒng)調(diào)用 fork 創(chuàng)建的,這里調(diào)用 fork 創(chuàng)建新進(jìn)程的進(jìn)程即為父進(jìn)程,而相對(duì)應(yīng)的為其創(chuàng)建出的進(jìn)程則為子進(jìn)程,因而除了進(jìn)程 0 以外的進(jìn)程都只有一個(gè)父進(jìn)程,但一個(gè)進(jìn)程可以有多個(gè)子進(jìn)程。操作系統(tǒng)內(nèi)核以進(jìn)程標(biāo)識(shí)符(Process Identifier,即 PID )來(lái)識(shí)別進(jìn)程。進(jìn)程 0 是系統(tǒng)引導(dǎo)時(shí)創(chuàng)建的一個(gè)特殊進(jìn)程,在其調(diào)用 fork 創(chuàng)建出一個(gè)子進(jìn)程(即 PID=1 的進(jìn)程 1,又稱 init)后,進(jìn)程 0 就轉(zhuǎn)為交換進(jìn)程(有時(shí)也被稱為空閑進(jìn)程),而進(jìn)程1(init進(jìn)程)就是系統(tǒng)里其他所有進(jìn)程的祖先。

進(jìn)程0:Linu__引導(dǎo)中創(chuàng)建的第一個(gè)進(jìn)程,完成加載系統(tǒng)后,演變?yōu)檫M(jìn)程調(diào)度、交換及存儲(chǔ)管理進(jìn)程?! ∵M(jìn)程1:init 進(jìn)程,由0進(jìn)程創(chuàng)建,完成系統(tǒng)的初始化. 是系統(tǒng)中所有其它用戶進(jìn)程的祖先進(jìn)程。

Linu__中 1 號(hào)進(jìn)程是由 0 號(hào)進(jìn)程來(lái)創(chuàng)建的,因此必須要知道的是如何創(chuàng)建 0 號(hào)進(jìn)程,由于在創(chuàng)建進(jìn)程時(shí),程序一直運(yùn)行在內(nèi)核態(tài),而進(jìn)程運(yùn)行在用戶態(tài),因此創(chuàng)建 0 號(hào)進(jìn)程涉及到特權(quán)級(jí)的變化,即從特權(quán)級(jí) 0 變到特權(quán)級(jí) 3,Linu__ 是通過模擬中斷返回來(lái)實(shí)現(xiàn)特權(quán)級(jí)的變化以及創(chuàng)建 0 號(hào)進(jìn)程,通過將 0 號(hào)進(jìn)程的代碼段選擇子以及程序計(jì)數(shù)器EIP直接壓入內(nèi)核態(tài)堆棧,然后利用 iret 匯編指令中斷返回跳轉(zhuǎn)到 0 號(hào)進(jìn)程運(yùn)行。

創(chuàng)建一個(gè)進(jìn)程

進(jìn)程是系統(tǒng)中基本的執(zhí)行單位。Linu__ 系統(tǒng)允許任何一個(gè)用戶進(jìn)程創(chuàng)建一個(gè)子進(jìn)程,創(chuàng)建成功后,子進(jìn)程存在于系統(tǒng)之中,并且獨(dú)立于父進(jìn)程。該子進(jìn)程可以接受系統(tǒng)調(diào)度,可以得到分配的系統(tǒng)資源。系統(tǒng)也可以檢測(cè)到子進(jìn)程的存在,并且賦予它與父進(jìn)程同樣的權(quán)利。

Linu__系統(tǒng)下使用 fork() 函數(shù)創(chuàng)建一個(gè)子進(jìn)程,其函數(shù)原型如下:

#include pid_t fork(void);

在討論 fork() 函數(shù)之前,有必要先明確父進(jìn)程和子進(jìn)程兩個(gè)概念。除了 0 號(hào)進(jìn)程(該進(jìn)程是系統(tǒng)自舉時(shí)由系統(tǒng)創(chuàng)建的)以外,Linu__ 系統(tǒng)中的任何一個(gè)進(jìn)程都是由其他進(jìn)程創(chuàng)建的。創(chuàng)建新進(jìn)程的進(jìn)程,即調(diào)用 fork() 函數(shù)的進(jìn)程就是父進(jìn)程,而新創(chuàng)建的進(jìn)程就是子進(jìn)程。

fork() 函數(shù)不需要參數(shù),返回值是一個(gè)進(jìn)程標(biāo)識(shí)符 (PID)。對(duì)于返回值,有以下 3 種情況:

對(duì)于父進(jìn)程,fork() 函數(shù)返回新創(chuàng)建的子進(jìn)程的 ID。

對(duì)于子進(jìn)程,fork() 函數(shù)返回 0。由于系統(tǒng)的 0 號(hào)進(jìn)程是內(nèi)核進(jìn)程,所以子進(jìn)程的進(jìn)程標(biāo)識(shí)符不會(huì)是0,由此可以用來(lái)區(qū)別父進(jìn)程和子進(jìn)程。

如果創(chuàng)建出錯(cuò),則 fork() 函數(shù)返回 -1。

fork() 函數(shù)會(huì)創(chuàng)建一個(gè)新的進(jìn)程,并從內(nèi)核中為此進(jìn)程分配一個(gè)新的可用的進(jìn)程標(biāo)識(shí)符 (PID),之后,為這個(gè)新進(jìn)程分配進(jìn)程空間,并將父進(jìn)程的進(jìn)程空間中的內(nèi)容復(fù)制到子進(jìn)程的進(jìn)程空間中,包括父進(jìn)程的數(shù)據(jù)段和堆棧段,并且和父進(jìn)程共享代碼段(寫時(shí)復(fù)制)。這時(shí)候,系統(tǒng)中又多了一個(gè)進(jìn)程,這個(gè)進(jìn)程和父進(jìn)程一模一樣,兩個(gè)進(jìn)程都要接受系統(tǒng)的調(diào)度。

注意:由于在復(fù)制時(shí)復(fù)制了父進(jìn)程的堆棧段,所以兩個(gè)進(jìn)程都停留在了 fork() 函數(shù)中,等待返回。因此,fork() 函數(shù)會(huì)返回兩次,一次是在父進(jìn)程中返回,另一次是在子進(jìn)程中返回,這兩次的返回值是不一樣的。

下面給出的示例程序用來(lái)創(chuàng)建一個(gè)子進(jìn)程,該程序在父進(jìn)程和子進(jìn)程中分別輸出不同的內(nèi)容。

#include #include #include int main(void){ pid_t pid; // 保存進(jìn)程ID pid = fork(); // 創(chuàng)建一個(gè)新進(jìn)程 if(pid < 0){ // fork出錯(cuò) printf("fail to fork\n"); e__it(1); } else if(pid == 0){ // 子進(jìn)程 // 打印子進(jìn)程的進(jìn)程ID printf("this is child, pid is : %u\n", getpid()); } else{ // 打印父進(jìn)程和其子進(jìn)程的進(jìn)程ID printf("this is parent, pid is : %u, child-pid is : %u\n", getpid(), pid); } return 0;}

程序運(yùn)行結(jié)果如下:

$ ./forkParent, PID: 2598, Sub-process PID: 2599Sub-process, PID: 2599, PPID: 2598

由于創(chuàng)建的新進(jìn)程和父進(jìn)程在系統(tǒng)看來(lái)是地位平等的兩個(gè)進(jìn)程,所以運(yùn)行機(jī)會(huì)也是一樣的,我們不能夠?qū)ζ鋱?zhí)行先后順序進(jìn)行假設(shè),先執(zhí)行哪一個(gè)進(jìn)程取決于系統(tǒng)的調(diào)度算法。如果想要指定運(yùn)行的順序,則需要執(zhí)行額外的操作。正因?yàn)槿绱耍绦蛟谶\(yùn)行時(shí)并不能保證輸出順序和上面所描述的一致。

getpid() 是獲得當(dāng)前進(jìn)程的pid,而 getppid() 則是獲得父進(jìn)程的 id。

父子進(jìn)程的共享資源

子進(jìn)程完全復(fù)制了父進(jìn)程的地址空間的內(nèi)容,包括堆棧段和數(shù)據(jù)段的內(nèi)容。子進(jìn)程并沒有復(fù)制代碼段,而是和父進(jìn)程共用代碼段。這樣做是存在其合理依據(jù)的,因?yàn)樽舆M(jìn)程可能執(zhí)行不同的流程,那么就會(huì)改變數(shù)據(jù)段和堆棧段,因此需要分開存儲(chǔ)父子進(jìn)程各自的數(shù)據(jù)段和堆棧段。但是代碼段是只讀的,不存在被修改的問題,因此這一個(gè)段可以讓父子進(jìn)程共享,以節(jié)省存儲(chǔ)空間,如下圖所示。

下面給出一個(gè)示例來(lái)說明這個(gè)問題。該程序定義了一個(gè)全局變量 global、一個(gè)局部變量 stack 和一個(gè)指針 heap。該指針用來(lái)指向一塊動(dòng)態(tài)分配的內(nèi)存區(qū)域。之后,該程序創(chuàng)建一個(gè)子進(jìn)程,在子進(jìn)程中修改 global、stack 和動(dòng)態(tài)分配的內(nèi)存中變量的值。然后在父子進(jìn)程中分別打印出這些變量的值。由于父子進(jìn)程的運(yùn)行順序是不確定的,因此我們先讓父進(jìn)程額外休眠2秒,以保證子進(jìn)程先運(yùn)行。

#include #include #include // 全局變量,在數(shù)據(jù)段中int global;int main(){ pid_t pid; int stack = 1; // 局部變量,在棧中 int __ heap; heap = (int __)malloc(sizeof(int)); // 動(dòng)態(tài)分配的內(nèi)存,在堆中 __heap = 2; pid = fork(); // 創(chuàng)建一個(gè)子進(jìn)程 if(pid < 0){ // 創(chuàng)建子進(jìn)程失敗 printf("fail to fork\n"); e__it(1); } else if(pid == 0){ // 子進(jìn)程,改變各變量的值 global++; // 修改棧、堆和數(shù)據(jù)段 stack++; (__heap)++; printf("the child, data : %d, stack : %d, heap : %d\n", global, stack, __heap); e__it(0); // 子進(jìn)程運(yùn)行結(jié)束 } // 父進(jìn)程休眠2秒鐘,保證子進(jìn)程先運(yùn)行 sleep(2); // 輸出結(jié)果 printf("the parent, data : %d, stack : %d, heap : %d\n", global, stack, __heap); return 0;}

程序運(yùn)行效果如下:

$ ./forkIn sub-process, global: 2, stack: 2, heap: 3In parent-process, global: 1, stack: 1, heap: 2

由于父進(jìn)程休眠了2秒鐘,子進(jìn)程先于父進(jìn)程運(yùn)行,因此會(huì)先在子進(jìn)程中修改數(shù)據(jù)段和堆棧段中的內(nèi)容。因此不難看出,子進(jìn)程對(duì)這些數(shù)據(jù)段和堆棧段中內(nèi)容的修改并不會(huì)影響到父進(jìn)程的進(jìn)程環(huán)境。

fork()函數(shù)的出錯(cuò)情況

有兩種情況可能會(huì)導(dǎo)致fork()函數(shù)出錯(cuò):

系統(tǒng)中已經(jīng)有太多的進(jìn)程存在了

調(diào)用fork()函數(shù)的用戶進(jìn)程太多了

一般情況下,系統(tǒng)都會(huì)對(duì)一個(gè)用戶所創(chuàng)建的進(jìn)程數(shù)加以限制。如果操作系統(tǒng)不對(duì)其加限制,那么惡意用戶可以利用這一缺陷攻擊系統(tǒng)。下面是一個(gè)利用進(jìn)程的特性編寫的一個(gè)病毒程序,該程序是一個(gè)死循環(huán),在循環(huán)中不斷調(diào)用fork()函數(shù)來(lái)創(chuàng)建子進(jìn)程,直到系統(tǒng)中不能容納如此多的進(jìn)程而崩潰為止。下圖展示了這種情況:

#include int main(){ while(1) fork(); /__ 不斷地創(chuàng)建子進(jìn)程,使系統(tǒng)中進(jìn)程溢滿 __/ return 0;}

創(chuàng)建共享空間的子進(jìn)程

進(jìn)程在創(chuàng)建一個(gè)新的子進(jìn)程之后,子進(jìn)程的地址空間完全和父進(jìn)程分開。父子進(jìn)程是兩個(gè)獨(dú)立的進(jìn)程,接受系統(tǒng)調(diào)度和分配系統(tǒng)資源的機(jī)會(huì)均等,因此父進(jìn)程和子進(jìn)程更像是一對(duì)兄弟。如果父子進(jìn)程共用父進(jìn)程的地址空間,則子進(jìn)程就不是獨(dú)立于父進(jìn)程的。

Linu__環(huán)境下提供了一個(gè)與 fork() 函數(shù)類似的函數(shù),也可以用來(lái)創(chuàng)建一個(gè)子進(jìn)程,只不過新進(jìn)程與父進(jìn)程共用父進(jìn)程的地址空間,其函數(shù)原型如下:

#include pid_t vfork(void);

vfork() 和 fork() 函數(shù)的區(qū)別有以下兩點(diǎn):

vfork() 函數(shù)產(chǎn)生的子進(jìn)程和父進(jìn)程完全共享地址空間,包括代碼段、數(shù)據(jù)段和堆棧段,子進(jìn)程對(duì)這些共享資源所做的修改,可以影響到父進(jìn)程。由此可知,vfork() 函數(shù)與其說是產(chǎn)生了一個(gè)進(jìn)程,還不如說是產(chǎn)生了一個(gè)線程。

vfork() 函數(shù)產(chǎn)生的子進(jìn)程一定比父進(jìn)程先運(yùn)行,也就是說父進(jìn)程調(diào)用了 vfork() 函數(shù)后會(huì)等待子進(jìn)程運(yùn)行后再運(yùn)行。

下面的示例程序用來(lái)驗(yàn)證以上兩點(diǎn)。在子進(jìn)程中,我們先讓其休眠 2 秒以釋放 CPU 控制權(quán),在前面的 fork() 示例代碼中我們已經(jīng)知道這樣會(huì)導(dǎo)致其他線程先運(yùn)行,也就是說如果休眠后父進(jìn)程先運(yùn)行的話,則第 1 點(diǎn)則為假;否則為真。第 2 點(diǎn)為真,則會(huì)先執(zhí)行子進(jìn)程,那么全局變量便會(huì)被修改,如果第 1 點(diǎn)為真,那么后執(zhí)行的父進(jìn)程也會(huì)輸出與子進(jìn)程相同的內(nèi)容。代碼如下:

//@file vfork.c//@brief vfork() usage#include #include #include int global = 1;int main(void){ pid_t pid; int stack = 1; int __heap; heap = (int __)malloc(sizeof(int)); ____eap = 1; pid = vfork(); if (pid < 0) { perror("fail to vfork"); ex___t(-1); } else if (pid == 0) { //sub-process, change values sleep(2);//release cpu controlling global = 999; stack = 888; ____eap = 777; //print all values printf("In sub-process, global: %d, stack: %d, heap: %d\n",global,stack,____eap); ex___t(0); } else { //parent-process printf("In parent-process, global: %d, stack: %d, heap: %d\n",global,stack,____eap); } return 0;}

程序運(yùn)行效果如下:

$ ./vforkIn sub-process, global: 999, stack: 888, heap: 777In parent-process, global: 999, stack: 888, heap: 777

在函數(shù)內(nèi)部調(diào)用vfork

在使用 vfork() 函數(shù)時(shí)應(yīng)該注意不要在任何函數(shù)中調(diào)用 vfork() 函數(shù)。下面的示例是在一個(gè)非 main 函數(shù)中調(diào)用了 vfork() 函數(shù)。該程序定義了一個(gè)函數(shù) f1(),該函數(shù)內(nèi)部調(diào)用了 vfork() 函數(shù)。之后,又定義了一個(gè)函數(shù) f2(),這個(gè)函數(shù)沒有實(shí)際的意義,只是用來(lái)覆蓋函數(shù) f1() 調(diào)用時(shí)的棧幀。main 函數(shù)中先調(diào)用 f1() 函數(shù),接著調(diào)用 f2() 函數(shù)。

#include #include #include int f1(void){ vfork(); return 0;}int f2(int a, int b){ return a+b;}int main(void){ int c; f1(); c = f2(1,2); printf("%d\n",c); return 0;}

程序運(yùn)行效果如下:

$ ./vfork3Segmentation fault (core dumped)

通過上面的程序運(yùn)行結(jié)果可以看出,一個(gè)進(jìn)程運(yùn)行正常,打印出了預(yù)期結(jié)果,而另一個(gè)進(jìn)程似乎出了問題,發(fā)生了段錯(cuò)誤。出現(xiàn)這種情況的原因可以用下圖來(lái)分析一下:

左邊這張圖說明調(diào)用 vfork() 之后產(chǎn)生了一個(gè)子進(jìn)程,并且和父進(jìn)程共享堆棧段,兩個(gè)進(jìn)程都要從 f1() 函數(shù)返回。由于子進(jìn)程先于父進(jìn)程運(yùn)行,所以子進(jìn)程先從 f1() 函數(shù)中返回,并且調(diào)用 f2() 函數(shù),其棧幀覆蓋了原來(lái) f1() 函數(shù)的棧幀。當(dāng)子進(jìn)程運(yùn)行結(jié)束,父進(jìn)程開始運(yùn)行時(shí),就出現(xiàn)了右圖的情景,父進(jìn)程需要從 f1() 函數(shù)返回,但是 f1() 函數(shù)的棧幀已經(jīng)被 f2() 函數(shù)的所替代,因此就會(huì)出現(xiàn)父進(jìn)程返回出錯(cuò),發(fā)生段錯(cuò)誤的情況。

由此可知,使用 vfork() 函數(shù)之后,子進(jìn)程對(duì)父進(jìn)程的影響是巨大的,其同步措施勢(shì)在必行。

退出進(jìn)程

當(dāng)一個(gè)進(jìn)程需要退出時(shí),需要調(diào)用退出函數(shù)。Linu__ 環(huán)境下使用 e__it() 函數(shù)退出進(jìn)程,其函數(shù)原型如下:

#include void e__it(int status);

e__it() 函數(shù)的參數(shù)表示進(jìn)程的退出狀態(tài),這個(gè)狀態(tài)的值是一個(gè)整型,保存在全局變量 $ ? 中,在 shell 中可以通過 echo $? 來(lái)檢查退出狀態(tài)值。

注意:這個(gè)退出函數(shù)會(huì)深入內(nèi)核注銷掉進(jìn)程的內(nèi)核數(shù)據(jù)結(jié)構(gòu),并且釋放掉進(jìn)程的資源。

e__it函數(shù)與內(nèi)核函數(shù)的關(guān)系

e__it 函數(shù)是一個(gè)標(biāo)準(zhǔn)的庫(kù)函數(shù),其內(nèi)部封裝了 Linu__ 系統(tǒng)調(diào)用 e__it() 函數(shù)。兩者的主要區(qū)別在于 e__it() 函數(shù)會(huì)在用戶空間做一些善后工作,例如清理用戶的 I/O 緩沖區(qū),將其內(nèi)容寫入 磁盤文件等,之后才進(jìn)入內(nèi)核釋放用戶進(jìn)程的地址空間;而 e__it() 函數(shù)直接進(jìn)入內(nèi)核釋放用戶進(jìn)程的地址空間,所有用戶空間的緩沖區(qū)內(nèi)容都將丟失。

設(shè)置進(jìn)程所有者

每個(gè)進(jìn)程都有兩個(gè)用戶 ID,實(shí)際用戶 ID 和有效用戶 ID。通常這兩個(gè) ID 的值是相等的,其取值為進(jìn)程所有者的用戶 ID。但是,在有些場(chǎng)合需要改變進(jìn)程的有效用戶 ID。Linu__ 環(huán)境下使用 setuid() 函數(shù)改變一個(gè)進(jìn)程的實(shí)際用戶ID和有效用戶ID,其函數(shù)原型如下:

#include int setuid(uid_t uid);

setuid() 函數(shù)的參數(shù)表示改變后的新用戶 ID,如果成功修改當(dāng)前進(jìn)程的實(shí)際用戶 ID 和有效用戶 ID,函數(shù)返回值為 0;如果失敗,則返回 -1。只有兩種用戶可以修改進(jìn)程的實(shí)際用戶 ID 和有效用戶 ID:

根用戶:根用戶可以將進(jìn)程的實(shí)際用戶 ID 和有效用戶 ID 更換。

其他用戶:其該用戶的用戶 ID 等于進(jìn)程的實(shí)際用戶 ID 或者保存的用戶 ID。

也就是說,用戶可以將自己的有效用戶 ID 改回去。這種情況多出現(xiàn)于下面的情況:一個(gè)進(jìn)程需要具有某種權(quán)限,所以將其有效用戶 ID 設(shè)置為具有這種權(quán)限的用戶 ID,當(dāng)進(jìn)程不需要這種權(quán)限時(shí),進(jìn)程還原自己之前的有效用戶 ID,使自己的權(quán)限復(fù)原。下面給出一個(gè)修改的示例:

#include #include #include int main(void){ FILE __fp; uid_t uid; uid_t euid; uid = getuid(); /__ 得到進(jìn)程的實(shí)際用戶ID __/ euid = geteuid(); /__ 得到進(jìn)程的有效用戶ID __/ printf("the uid is : %d\n", uid); printf("the euid is : %d\n", euid); if(setuid(8000) == -1){ /__ 改變進(jìn)程的實(shí)際用戶ID和有效用戶ID __/ perror("fail to set uid"); e__it(1); } printf("after changing\n"); uid = getuid(); /__ 再次得到進(jìn)程的實(shí)際用戶ID __/ euid = geteuid(); /__ 再次得到進(jìn)程的有效用戶ID __/ printf("the uid is : %d\n", uid); printf("the euid is : %d\n", euid); return 0;}

程序運(yùn)行效果如下:

$./setuidthe uid is : 0the euid is : 0after changingthe uid is : 8000the euid is : 8000

本節(jié)參考:

《后臺(tái)開發(fā):核心技術(shù)與應(yīng)用實(shí)踐》

《Linu__+C程序設(shè)計(jì)大全》十一章:進(jìn)程控制

9. 孤兒進(jìn)程和僵尸進(jìn)程

基本概念

我們知道在 Uni__/Linu__ 中,正常情況下,子進(jìn)程是通過父進(jìn)程創(chuàng)建的,子進(jìn)程在創(chuàng)建新的進(jìn)程。子進(jìn)程的結(jié)束和父進(jìn)程的運(yùn)行是一個(gè)異步過程,即父進(jìn)程永遠(yuǎn)無(wú)法預(yù)測(cè)子進(jìn)程 到底什么時(shí)候結(jié)束。當(dāng)一個(gè)進(jìn)程完成它的工作終止之后,它的父進(jìn)程需要調(diào)用 wait() 或者 waitpid() 系統(tǒng)調(diào)用取得子進(jìn)程的終止?fàn)顟B(tài)。

孤兒進(jìn)程:一個(gè)父進(jìn)程退出,而它的一個(gè)或多個(gè)子進(jìn)程還在運(yùn)行,那么那些子進(jìn)程將成為孤兒進(jìn)程。孤兒進(jìn)程將被 init 進(jìn)程(進(jìn)程號(hào)為1)所收養(yǎng),并由 init 進(jìn)程對(duì)它們完成狀態(tài)收集工作____。____

僵尸進(jìn)程:一個(gè)進(jìn)程使用 fork 創(chuàng)建子進(jìn)程,如果子進(jìn)程退出,而父進(jìn)程并沒有調(diào)用 wait 或 waitpid 獲取子進(jìn)程的狀態(tài)信息,那么子進(jìn)程的進(jìn)程描述符仍然保存在系統(tǒng)中。這種進(jìn)程稱之為僵尸進(jìn)程。

問題及危害

Uni__ 提供了一種機(jī)制可以保證只要父進(jìn)程想知道子進(jìn)程結(jié)束時(shí)的狀態(tài)信息,就可以得到。這種機(jī)制就是:在每個(gè)進(jìn)程退出的時(shí)候,內(nèi)核釋放該進(jìn)程所有的資源,包括打開的文件,占用的內(nèi)存等。但是仍然為其保留一定的信息(包括進(jìn)程號(hào) the process ID,退出狀態(tài) the termination status of the process,運(yùn)行時(shí)間 the amount of CPU time taken by the process 等)。直到父進(jìn)程通過 wait / waitpid 來(lái)取時(shí)才釋放。但這樣就導(dǎo)致了問題,如果進(jìn)程不調(diào)用 wait / waitpid 的話, 那么保留的那段信息就不會(huì)釋放,其進(jìn)程號(hào)就會(huì)一直被占用,但是系統(tǒng)所能使用的進(jìn)程號(hào)是有限的,如果大量的產(chǎn)生僵死進(jìn)程,將因?yàn)闆]有可用的進(jìn)程號(hào)而導(dǎo)致系統(tǒng)不能產(chǎn)生新的進(jìn)程。此即為僵尸進(jìn)程的危害,應(yīng)當(dāng)避免。

孤兒進(jìn)程是沒有父進(jìn)程的進(jìn)程,孤兒進(jìn)程這個(gè)重任就落到了 init 進(jìn)程身上,init 進(jìn)程就好像是一個(gè)民政局,專門負(fù)責(zé)處理孤兒進(jìn)程的善后工作。每當(dāng)出現(xiàn)一個(gè)孤兒進(jìn)程的時(shí)候,內(nèi)核就把孤 兒進(jìn)程的父進(jìn)程設(shè)置為 init,而 init 進(jìn)程會(huì)循環(huán)地 wait() 它的已經(jīng)退出的子進(jìn)程。這樣,當(dāng)一個(gè)孤兒進(jìn)程凄涼地結(jié)束了其生命周期的時(shí)候,init 進(jìn)程就會(huì)代表黨和政府出面處理它的一切善后工作。因此孤兒進(jìn)程并不會(huì)有什么危害。

任何一個(gè)子進(jìn)程(init除外)在e__it() 之后,并非馬上就消失掉,而是留下一個(gè)稱為僵尸進(jìn)程 (Zombie) 的數(shù)據(jù)結(jié)構(gòu),等待父進(jìn)程處理。這是每個(gè)子進(jìn)程在結(jié)束時(shí)都要經(jīng)過的階段。如果子進(jìn)程在e__it()之后,父進(jìn)程沒有來(lái)得及處理,這時(shí)用 ps 命令就能看到子進(jìn)程的狀態(tài)是 Z。如果父進(jìn)程能及時(shí)處理,可能用 ps 命令就來(lái)不及看到子進(jìn)程的僵尸狀態(tài),但這并不等于子進(jìn)程不經(jīng)過僵尸狀態(tài)。如果父進(jìn)程在子進(jìn)程結(jié)束之前退出,則子進(jìn)程將由 init 接管。init 將會(huì)以父進(jìn)程的身份對(duì)僵尸狀態(tài)的子進(jìn)程進(jìn)行處理。

僵尸進(jìn)程危害場(chǎng)景:

例如有個(gè)進(jìn)程,它定期的產(chǎn)生一個(gè)子進(jìn)程,這個(gè)子進(jìn)程需要做的事情很少,做完它該做的事情之后就退出了,因此這個(gè)子進(jìn)程的生命周期很短,但是,父進(jìn)程只管生成新的子進(jìn)程,至于子進(jìn)程退出之后的事情,則一概不聞不問,這樣,系統(tǒng)運(yùn)行上一段時(shí)間之后,系統(tǒng)中就會(huì)存在很多的僵死進(jìn)程,倘若用 ps 命令查看的話,就會(huì)看到很多狀態(tài)為 Z 的進(jìn)程。嚴(yán)格地來(lái)說,僵死進(jìn)程并不是問題的根源,罪魁禍?zhǔn)资钱a(chǎn)生出大量僵死進(jìn)程的那個(gè)父進(jìn)程。因此,當(dāng)我們尋求如何消滅系統(tǒng)中大量的僵死進(jìn)程時(shí),答案就是把產(chǎn)生大 量僵死進(jìn)程的那個(gè)元兇槍斃掉(也就是通過 kill 發(fā)送 SIGTERM 或者 SIGKILL 信號(hào)啦)。槍斃了元兇進(jìn)程之后,它產(chǎn)生的僵死進(jìn)程就變成了孤兒進(jìn)程,這些孤兒進(jìn)程會(huì)被 init 進(jìn)程接管,init 進(jìn)程會(huì) wait() 這些孤兒進(jìn)程,釋放它們占用的系統(tǒng)進(jìn)程表中的資源,這樣,這些已經(jīng)僵死的孤兒進(jìn)程就能瞑目而去了。

測(cè)試代碼

孤兒進(jìn)程測(cè)試程序如下所示:

#include #include #include #include int main(){ pid_t pid; //創(chuàng)建一個(gè)進(jìn)程 pid = fork(); //創(chuàng)建失敗 if (pid < 0) { perror("fork error:"); e__it(1); } //子進(jìn)程 if (pid == 0) { printf("I am the child process.\n"); //輸出進(jìn)程ID和父進(jìn)程ID printf("pid: %d\tppid:%d\n",getpid(),getppid()); printf("I will sleep five seconds.\n"); //睡眠5s,保證父進(jìn)程先退出 sleep(5); printf("pid: %d\tppid:%d\n",getpid(),getppid()); printf("child process is e__ited.\n"); } //父進(jìn)程 else { printf("I am father process.\n"); //父進(jìn)程睡眠1s,保證子進(jìn)程輸出進(jìn)程id sleep(1); printf("father process is e__ited."); } return 0;}

僵尸進(jìn)程測(cè)試程序如下所示:

#include #include #include #include int main(){ pid_t pid; pid = fork(); if (pid < 0) { perror("fork error:"); e__it(1); } else if (pid == 0) { printf("I am child process.I am e__iting.\n"); e__it(0); } printf("I am father process.I will sleep two seconds\n"); //等待子進(jìn)程先退出 sleep(2); //輸出進(jìn)程信息 system("ps -o pid,ppid,state,tty,command"); printf("father process is e__iting.\n"); return 0;}

測(cè)試結(jié)果如下所示:

僵尸進(jìn)程解決辦法

通過信號(hào)機(jī)制

子進(jìn)程退出時(shí)向父進(jìn)程發(fā)送SIGCHILD信號(hào),父進(jìn)程處理SIGCHILD信號(hào)。在信號(hào)處理函數(shù)中調(diào)用wait進(jìn)行處理僵尸進(jìn)程

fork兩次

將子進(jìn)程成為孤兒進(jìn)程,從而其的父進(jìn)程變?yōu)?init 進(jìn)程,通過 init 進(jìn)程可以處理僵尸進(jìn)程

10. 守護(hù)進(jìn)程

Linu__ Daemon(守護(hù)進(jìn)程)是運(yùn)行在后臺(tái)的一種特殊進(jìn)程。它獨(dú)立于控制終端并且周期性地執(zhí)行某種任務(wù)或等待處理某些發(fā)生的事件。它不需要用戶輸入就能運(yùn)行而且提供某種服務(wù),不是對(duì)整個(gè)系統(tǒng)就是對(duì)某個(gè)用戶程序提供服務(wù)。Linu__系統(tǒng)的大多數(shù)服務(wù)器就是通過守護(hù)進(jìn)程實(shí)現(xiàn)的。常見的守護(hù)進(jìn)程包括系統(tǒng)日志進(jìn)程syslogd、 web服務(wù)器httpd、郵件服務(wù)器sendmail和數(shù)據(jù)庫(kù)服務(wù)器mysqld等。

守護(hù)進(jìn)程一般在系統(tǒng)啟動(dòng)時(shí)開始運(yùn)行,除非強(qiáng)行終止,否則直到系統(tǒng)關(guān)機(jī)都保持運(yùn)行。守護(hù)進(jìn)程經(jīng)常以超級(jí)用戶(root)權(quán)限運(yùn)行,因?yàn)樗鼈円褂锰厥獾亩丝?1-1024)或訪問某些特殊的資源。

一個(gè)守護(hù)進(jìn)程的父進(jìn)程是init進(jìn)程,因?yàn)樗嬲母高M(jìn)程在fork出子進(jìn)程后就先于子進(jìn)程e__it退出了,所以它是一個(gè)由init繼承的孤兒進(jìn)程。守護(hù)進(jìn)程是非交互式程序,沒有控制終端,所以任何輸出,無(wú)論是向標(biāo)準(zhǔn)輸出設(shè)備stdout還是標(biāo)準(zhǔn)出錯(cuò)設(shè)備stderr的輸出都需要特殊處理。

守護(hù)進(jìn)程的名稱通常以d結(jié)尾,比如sshd、__inetd、crond等

編寫守護(hù)進(jìn)程的一般步驟步驟:

在父進(jìn)程中執(zhí)行 fork 并 e__it 推出;

在子進(jìn)程中調(diào)用 setsid 函數(shù)創(chuàng)建新的會(huì)話;

在子進(jìn)程中調(diào)用 chdir 函數(shù),讓根目錄 / 成為子進(jìn)程的工作目錄;

在子進(jìn)程中調(diào)用umask函數(shù),設(shè)置進(jìn)程的umask 為 0;

在子進(jìn)程中關(guān)閉任何不需要的文件描述符。

11. 上下文切換

上下文切換,有時(shí)也稱做進(jìn)程切換或任務(wù)切換,是指CPU從一個(gè)進(jìn)程或線程切換到另一個(gè)進(jìn)程或線程。在操作系統(tǒng)中,CPU 切換到另一個(gè)進(jìn)程需要保存當(dāng)前進(jìn)程的狀態(tài)并恢復(fù)另一個(gè)進(jìn)程的狀態(tài):當(dāng)前運(yùn)行任務(wù)轉(zhuǎn)為就緒(或者掛起、刪除)狀態(tài),另一個(gè)被選定的就緒任務(wù)成為當(dāng)前任務(wù)

三、死鎖

資源分類:(1)可重用資源;(2)消耗資源

1. 什么是死鎖

造成死鎖的原因就是多個(gè)線程或進(jìn)程對(duì)同一個(gè)資源的爭(zhēng)搶或相互依賴。一個(gè)最簡(jiǎn)單的解釋就是你去面試,面試官問你告訴我什么是死鎖,我就錄用你,你回答面試官你錄用我,我告訴你。

如果一個(gè)進(jìn)程集合里面的每個(gè)進(jìn)程都在等待只能由這個(gè)集合中的其他一個(gè)進(jìn)程(包括他自身)才能引發(fā)的事件,這種情況就是死鎖。

這個(gè)定義可能有點(diǎn)拗口,下面用一個(gè)簡(jiǎn)單例子說明。

資源 A、B,進(jìn)程 C、D 描述如下:

資源 A 和資源 B,都是不可剝奪資源,現(xiàn)在進(jìn)程 C 已經(jīng)申請(qǐng)了資源 A,進(jìn)程 D 也申請(qǐng)了資源 B,進(jìn)程 C 接下來(lái)的操作需要用到資源 B,而進(jìn)程 D 恰好也在申請(qǐng)資源A,進(jìn)程 C、D 都得不到接下來(lái)的資源,那么就引發(fā)了死鎖。

然后套用回去定義:如果一個(gè)進(jìn)程集合里面(進(jìn)程 C 和進(jìn)程 D)的每個(gè)進(jìn)程(進(jìn)程 C 和進(jìn)程 D)都在等待只能由這個(gè)集合中的其他一個(gè)進(jìn)程(對(duì)于進(jìn)程 C,他在等進(jìn)程 D;對(duì)于進(jìn)程 D,他在等進(jìn)程 C)才能引發(fā)的事件(釋放相應(yīng)資源)。

這里的資源包括了軟的資源(代碼塊)和硬的資源(例如掃描儀)。資源一般可以分兩種:可剝奪資源(Preemptable)和不可剝奪資源 (Nonpreemptable)。一般來(lái)說對(duì)于由可剝奪資源引起的死鎖可以由系統(tǒng)的重新分配資源來(lái)解決,所以一般來(lái)說大家說的死鎖都是由于不可剝奪資源所引起的。

2. 死鎖的必要條件

互斥:每個(gè)資源要么已經(jīng)分配給了一個(gè)進(jìn)程,要么就是可用的。

占有和等待:已經(jīng)得到了某個(gè)資源的進(jìn)程可以再請(qǐng)求新的資源。

不可搶占:已經(jīng)分配給一個(gè)進(jìn)程的資源不能強(qiáng)制性地被搶占,它只能被占有它的進(jìn)程顯式地釋放。

循環(huán)等待:有兩個(gè)或者兩個(gè)以上的進(jìn)程組成一條環(huán)路,該環(huán)路中的每個(gè)進(jìn)程都在等待下一個(gè)進(jìn)程所占有的資源。

3. 死鎖的處理方法

1. 處理死鎖的策略

鴕鳥策略

把頭埋在沙子里,假裝根本沒發(fā)生問題。

因?yàn)榻鉀Q死鎖問題的代價(jià)很高,因此鴕鳥策略這種不采取任務(wù)措施的方案會(huì)獲得更高的性能。當(dāng)發(fā)生死鎖時(shí)不會(huì)對(duì)用戶造成多大影響,或發(fā)生死鎖的概率很低,可以采用鴕鳥策略。

大多數(shù)操作系統(tǒng),包括 Uni__,Linu__ 和 Windows,處理死鎖問題的辦法僅僅是忽略它。

檢測(cè)死鎖并且恢復(fù)。

仔細(xì)地對(duì)資源進(jìn)行動(dòng)態(tài)分配,以避免死鎖。

通過破除死鎖四個(gè)必要條件之一,來(lái)防止死鎖產(chǎn)生。

2. 死鎖檢測(cè)與死鎖恢復(fù)

不試圖阻止死鎖,而是當(dāng)檢測(cè)到死鎖發(fā)生時(shí),采取措施進(jìn)行恢復(fù)。

(一)每種類型一個(gè)資源的死鎖檢測(cè)

上圖為資源分配圖,其中方框表示資源,圓圈表示進(jìn)程。資源指向進(jìn)程表示該資源已經(jīng)分配給該進(jìn)程,進(jìn)程指向資源表示進(jìn)程請(qǐng)求獲取該資源。

圖 a 可以抽取出環(huán),如圖 b,它滿足了環(huán)路等待條件,因此會(huì)發(fā)生死鎖。

每種類型一個(gè)資源的死鎖檢測(cè)算法是通過檢測(cè)有向圖是否存在環(huán)來(lái)實(shí)現(xiàn),從一個(gè)節(jié)點(diǎn)出發(fā)進(jìn)行深度優(yōu)先搜索,對(duì)訪問過的節(jié)點(diǎn)進(jìn)行標(biāo)記,如果訪問了已經(jīng)標(biāo)記的節(jié)點(diǎn),就表示有向圖存在環(huán),也就是檢測(cè)到死鎖的發(fā)生。

(二)每種類型多個(gè)資源的死鎖檢測(cè)

上圖中,有三個(gè)進(jìn)程四個(gè)資源,每個(gè)數(shù)據(jù)代表的含義如下:

E 向量:資源總量

A 向量:資源剩余量

C 矩陣:每個(gè)進(jìn)程所擁有的資源數(shù)量,每一行都代表一個(gè)進(jìn)程擁有資源的數(shù)量

R 矩陣:每個(gè)進(jìn)程請(qǐng)求的資源數(shù)量

進(jìn)程 P1 和 P2 所請(qǐng)求的資源都得不到滿足,只有進(jìn)程 P3 可以,讓 P3 執(zhí)行,之后釋放 P3 擁有的資源,此時(shí) A = (2 2 2 0)。P2 可以執(zhí)行,執(zhí)行后釋放 P2 擁有的資源,A = (4 2 2 1) 。P1 也可以執(zhí)行。所有進(jìn)程都可以順利執(zhí)行,沒有死鎖。

算法總結(jié)如下:

每個(gè)進(jìn)程最開始時(shí)都不被標(biāo)記,執(zhí)行過程有可能被標(biāo)記。當(dāng)算法結(jié)束時(shí),任何沒有被標(biāo)記的進(jìn)程都是死鎖進(jìn)程。

尋找一個(gè)沒有標(biāo)記的進(jìn)程 Pi,它所請(qǐng)求的資源小于等于 A。

如果找到了這樣一個(gè)進(jìn)程,那么將 C 矩陣的第 i 行向量加到 A 中,標(biāo)記該進(jìn)程,并轉(zhuǎn)回 1。

如果沒有這樣一個(gè)進(jìn)程,算法終止。

(三)死鎖恢復(fù)

利用搶占恢復(fù)

利用回滾恢復(fù)

通過殺死進(jìn)程恢復(fù)

3. 死鎖預(yù)防

在程序運(yùn)行之前預(yù)防發(fā)生死鎖,確保系統(tǒng)永遠(yuǎn)不會(huì)進(jìn)入死鎖狀態(tài)。

(一)破壞互斥條件

例如假脫機(jī)打印機(jī)技術(shù)允許若干個(gè)進(jìn)程同時(shí)輸出,唯一真正請(qǐng)求物理打印機(jī)的進(jìn)程是打印機(jī)守護(hù)進(jìn)程。(把互斥地封裝成可以同時(shí)訪問的,例如:打印機(jī)的緩存)

(二)破壞占有和等待條件

一種實(shí)現(xiàn)方式是規(guī)定所有進(jìn)程在開始執(zhí)行前請(qǐng)求所需要的全部資源。

但是,這種策略也有如下缺點(diǎn):

在許多情況下,一個(gè)進(jìn)程在執(zhí)行之前不可能知道它所需要的全部資源。這是由于進(jìn)程在執(zhí)行時(shí)是動(dòng)態(tài)的,不可預(yù)測(cè)的;

資源利用率低。無(wú)論所分資源何時(shí)用到,一個(gè)進(jìn)程只有在占有所需的全部資源后才能執(zhí)行。即使有些資源最后才被該進(jìn)程用到一次,但該進(jìn)程在生存期間卻一直占有它們,造成長(zhǎng)期占著不用的狀況。這顯然是一種極大的資源浪費(fèi);

降低了進(jìn)程的并發(fā)性。因?yàn)橘Y源有限,又加上存在浪費(fèi),能分配到所需全部資源的進(jìn)程個(gè)數(shù)就必然少了。

(三)破壞不可搶占條件

允許進(jìn)程強(qiáng)行從占有者那里奪取某些資源。就是說,當(dāng)一個(gè)進(jìn)程已占有了某些資源,它又申請(qǐng)新的資源,但不能立即被滿足時(shí),它必須釋放所占有的全部資源,以后再重新申請(qǐng)。它所釋放的資源可以分配給其它進(jìn)程。這就相當(dāng)于該進(jìn)程占有的資源被隱蔽地強(qiáng)占了。這種預(yù)防死鎖的方法實(shí)現(xiàn)起來(lái)困難,會(huì)降低系統(tǒng)性能。

(四)破壞循環(huán)等待

實(shí)行資源有序分配策略。采用這種策略,即把資源事先分類編號(hào),按號(hào)分配,使進(jìn)程在申請(qǐng),占用資源時(shí)不會(huì)形成環(huán)路。所有進(jìn)程對(duì)資源的請(qǐng)求必須嚴(yán)格按資源序號(hào)遞增的順序提出。進(jìn)程占用了小號(hào)資源,才能申請(qǐng)大號(hào)資源,就不會(huì)產(chǎn)生環(huán)路,從而預(yù)防了死鎖。這種策略與前面的策略相比,資源的利用率和系統(tǒng)吞吐量都有很大提高,但是也存在以下缺點(diǎn):

限制了進(jìn)程對(duì)資源的請(qǐng)求,同時(shí)給系統(tǒng)中所有資源合理編號(hào)也是件困難事,并增加了系統(tǒng)開銷;

為了遵循按編號(hào)申請(qǐng)的次序,暫不使用的資源也需要提前申請(qǐng),從而增加了進(jìn)程對(duì)資源的占用時(shí)間。

4. 死鎖避免

在程序運(yùn)行時(shí)避免發(fā)生死鎖,在使用前進(jìn)行判斷,只允許不會(huì)出現(xiàn)死鎖的進(jìn)程請(qǐng)求資源。

(一)安全狀態(tài)

圖 a 的第二列 Has 表示已擁有的資源數(shù),第三列 Ma__ 表示總共需要的資源數(shù),F(xiàn)ree 表示還有可以使用的資源數(shù)。從圖 a 開始出發(fā),先讓 B 擁有所需的所有資源(圖 b),運(yùn)行結(jié)束后釋放 B,此時(shí) Free 變?yōu)?5(圖 c);接著以同樣的方式運(yùn)行 C 和 A,使得所有進(jìn)程都能成功運(yùn)行,因此可以稱圖 a 所示的狀態(tài)時(shí)安全的。

定義:如果沒有死鎖發(fā)生,并且即使所有進(jìn)程突然請(qǐng)求對(duì)資源的最大需求,也仍然存在某種調(diào)度次序能夠使得每一個(gè)進(jìn)程運(yùn)行完畢,則稱該狀態(tài)是安全的。

安全狀態(tài)的檢測(cè)與死鎖的檢測(cè)類似,因?yàn)榘踩珷顟B(tài)必須要求不能發(fā)生死鎖。下面的銀行家算法與死鎖檢測(cè)算法非常類似,可以結(jié)合著做參考對(duì)比。

(二)單個(gè)資源的銀行家算法

一個(gè)小城鎮(zhèn)的銀行家,他向一群客戶分別承諾了一定的貸款額度,算法要做的是判斷對(duì)請(qǐng)求的滿足是否會(huì)進(jìn)入不安全狀態(tài),如果是,就拒絕請(qǐng)求;否則予以分配。

不安全狀態(tài),因此算法會(huì)拒絕之前的請(qǐng)求,從而避免進(jìn)入圖 c 中的狀態(tài)。

(三)多個(gè)資源的銀行家算法

有五個(gè)進(jìn)程,四個(gè)資源。左邊的圖表示已經(jīng)分配的資源,右邊的圖表示還需要分配的資源。最右邊的 E、P 以及 A 分別表示:總資源、已分配資源以及可用資源,注意這三個(gè)為向量,而不是具體數(shù)值,例如 A=(1020),表示 4 個(gè)資源分別還剩下 1/0/2/0。

檢查一個(gè)狀態(tài)是否安全的算法如下:

查找右邊的矩陣是否存在一行小于等于向量 A。如果不存在這樣的行,那么系統(tǒng)將會(huì)發(fā)生死鎖,狀態(tài)是不安全的。

假若找到這樣一行,將該進(jìn)程標(biāo)記為終止,并將其已分配資源加到 A 中。

重復(fù)以上兩步,直到所有進(jìn)程都標(biāo)記為終止,則狀態(tài)時(shí)安全的。

如果一個(gè)狀態(tài)不是安全的,需要拒絕進(jìn)入這個(gè)狀態(tài)。

4. 如何在寫程序的時(shí)候就避免死鎖

所謂的死鎖呢,發(fā)生的主要原因在于了有多個(gè)進(jìn)程去競(jìng)爭(zhēng)資源,也就是同時(shí)去搶占。

可以自己寫一個(gè)支持多線程的消息管理類,單開一個(gè)線程訪問獨(dú)占資源,其它線程用消息交互實(shí)現(xiàn)間接訪問。這種機(jī)制適應(yīng)性強(qiáng)、效率高,更適合多核環(huán)境。

四、內(nèi)存管理

1. 虛擬內(nèi)存

虛擬內(nèi)存的目的是為了讓物理內(nèi)存擴(kuò)充成更大的邏輯內(nèi)存,從而讓程序獲得更多的可用內(nèi)存。

為了更好的管理內(nèi)存,操作系統(tǒng)將內(nèi)存抽象成地址空間。每個(gè)程序擁有自己的地址空間,這個(gè)地址空間被分割成多個(gè)塊,每一塊稱為一頁(yè)。這些頁(yè)被映射到物理內(nèi)存,但不需要映射到連續(xù)的物理內(nèi)存,也不需要所有頁(yè)都必須在物理內(nèi)存中。當(dāng)程序引用到一部分不在物理內(nèi)存中的地址空間時(shí),由硬件執(zhí)行必要的映射,將缺失的部分裝入物理內(nèi)存并重新執(zhí)行失敗的指令。

從上面的描述中可以看出,虛擬內(nèi)存允許程序不用將地址空間中的每一頁(yè)都映射到物理內(nèi)存,也就是說一個(gè)程序不需要全部調(diào)入內(nèi)存就可以運(yùn)行,這使得有限的內(nèi)存運(yùn)行大程序稱為可能。例如有一臺(tái)計(jì)算機(jī)可以產(chǎn)生 16 位地址,那么一個(gè)程序的地址空間范圍是 0~64K。該計(jì)算機(jī)只有 32KB 的物理內(nèi)存,虛擬內(nèi)存技術(shù)允許該計(jì)算機(jī)運(yùn)行一個(gè) 64K 大小的程序。

2. 分頁(yè)系統(tǒng)地址映射

內(nèi)存管理單元(MMU):管理著地址空間和物理內(nèi)存的轉(zhuǎn)換。

頁(yè)表(Page table):頁(yè)(地址空間)和頁(yè)框(物理內(nèi)存空間)的映射表。例如下圖中,頁(yè)表的第 0 個(gè)表項(xiàng)為 010,表示第 0 個(gè)頁(yè)映射到第 2 個(gè)頁(yè)框。頁(yè)表項(xiàng)的最后一位用來(lái)標(biāo)記頁(yè)是否在內(nèi)存中。

下圖的頁(yè)表存放著 16 個(gè)頁(yè),這 16 個(gè)頁(yè)需要用 4 個(gè)比特位來(lái)進(jìn)行索引定位。因此對(duì)于虛擬地址(0010 000000000100),前 4 位是用來(lái)存儲(chǔ)頁(yè)面號(hào),而后 12 位存儲(chǔ)在頁(yè)中的偏移量。

(0010 000000000100)根據(jù)前 4 位得到頁(yè)號(hào)為 2,讀取表項(xiàng)內(nèi)容為(110 1),它的前 3 為為頁(yè)框號(hào),最后 1 位表示該頁(yè)在內(nèi)存中。最后映射得到物理內(nèi)存地址為(110 000000000100)。

3. 頁(yè)面置換算法

在程序運(yùn)行過程中,如果要訪問的頁(yè)面不在內(nèi)存中,就發(fā)生缺頁(yè)中斷從而將該頁(yè)調(diào)入內(nèi)存中。此時(shí)如果內(nèi)存已無(wú)空閑空間,系統(tǒng)必須從內(nèi)存中調(diào)出一個(gè)頁(yè)面到磁盤對(duì)換區(qū)中來(lái)騰出空間。

頁(yè)面置換算法和緩存淘汰策略類似,可以將內(nèi)存看成磁盤的緩存。在緩存系統(tǒng)中,緩存的大小有限,當(dāng)有新的緩存到達(dá)時(shí),需要淘汰一部分已經(jīng)存在的緩存,這樣才有空間存放新的緩存數(shù)據(jù)。

頁(yè)面置換算法的主要目標(biāo)是使頁(yè)面置換頻率最低(也可以說缺頁(yè)率最低)。

1. 最佳

Optimal

所選擇的被換出的頁(yè)面將是最長(zhǎng)時(shí)間內(nèi)不再被訪問,通??梢员WC獲得最低的缺頁(yè)率。

是一種理論上的算法,因?yàn)闊o(wú)法知道一個(gè)頁(yè)面多長(zhǎng)時(shí)間不再被訪問。

舉例:一個(gè)系統(tǒng)為某進(jìn)程分配了三個(gè)物理塊,并有如下頁(yè)面引用序列:

開始運(yùn)行時(shí),先將 7, 0, 1 三個(gè)頁(yè)面裝入內(nèi)存。當(dāng)進(jìn)程要訪問頁(yè)面 2 時(shí),產(chǎn)生缺頁(yè)中斷,會(huì)將頁(yè)面 7 換出,因?yàn)轫?yè)面 7 再次被訪問的時(shí)間最長(zhǎng)。

2. 最近最久未使用

LRU, Least Recently Used

雖然無(wú)法知道將來(lái)要使用的頁(yè)面情況,但是可以知道過去使用頁(yè)面的情況。LRU 將最近最久未使用的頁(yè)面換出。

為了實(shí)現(xiàn) LRU,需要在內(nèi)存中維護(hù)一個(gè)所有頁(yè)面的鏈表。當(dāng)一個(gè)頁(yè)面被訪問時(shí),將這個(gè)頁(yè)面移到鏈表表頭。這樣就能保證鏈表表尾的頁(yè)面時(shí)最近最久未訪問的。

因?yàn)槊看卧L問都需要更新鏈表,因此這種方式實(shí)現(xiàn)的 LRU 代價(jià)很高。

3. 最近未使用

NRU, Not Recently Used

每個(gè)頁(yè)面都有兩個(gè)狀態(tài)位:R 與 M,當(dāng)頁(yè)面被訪問時(shí)設(shè)置頁(yè)面的 R=1,當(dāng)頁(yè)面被修改時(shí)設(shè)置 M=1。其中 R 位會(huì)定時(shí)被清零??梢詫㈨?yè)面分成以下四類:

R=0,M=0

R=0,M=1

R=1,M=0

R=1,M=1

當(dāng)發(fā)生缺頁(yè)中斷時(shí),NRU 算法隨機(jī)地從類編號(hào)最小的非空類中挑選一個(gè)頁(yè)面將它換出。

NRU 優(yōu)先換出已經(jīng)被修改的臟頁(yè)面(R=0,M=1),而不是被頻繁使用的干凈頁(yè)面(R=1,M=0)。

4. 先進(jìn)先出

FIFO, First In First Out

選擇換出的頁(yè)面是最先進(jìn)入的頁(yè)面。

該算法會(huì)將那些經(jīng)常被訪問的頁(yè)面也被換出,從而使缺頁(yè)率升高。

5. 第二次機(jī)會(huì)算法

FIFO 算法可能會(huì)把經(jīng)常使用的頁(yè)面置換出去,為了避免這一問題,對(duì)該算法做一個(gè)簡(jiǎn)單的修改:

當(dāng)頁(yè)面被訪問 (讀或?qū)? 時(shí)設(shè)置該頁(yè)面的 R 位為 1。需要替換的時(shí)候,檢查最老頁(yè)面的 R 位。如果 R 位是 0,那么這個(gè)頁(yè)面既老又沒有被使用,可以立刻置換掉;如果是 1,就將 R 位清 0,并把該頁(yè)面放到鏈表的尾端,修改它的裝入時(shí)間使它就像剛裝入的一樣,然后繼續(xù)從鏈表的頭部開始搜索。

6. 時(shí)鐘

Clock

第二次機(jī)會(huì)算法需要在鏈表中移動(dòng)頁(yè)面,降低了效率。時(shí)鐘算法使用環(huán)形鏈表將頁(yè)面鏈接起來(lái),再使用一個(gè)指針指向最老的頁(yè)面。

4. 分段

虛擬內(nèi)存采用的是分頁(yè)技術(shù),也就是將地址空間劃分成固定大小的頁(yè),每一頁(yè)再與內(nèi)存進(jìn)行映射。

下圖為一個(gè)編譯器在編譯過程中建立的多個(gè)表,有 4 個(gè)表是動(dòng)態(tài)增長(zhǎng)的,如果使用分頁(yè)系統(tǒng)的一維地址空間,動(dòng)態(tài)增長(zhǎng)的特點(diǎn)會(huì)導(dǎo)致覆蓋問題的出現(xiàn)。

分段的做法是把每個(gè)表分成段,一個(gè)段構(gòu)成一個(gè)獨(dú)立的地址空間。每個(gè)段的長(zhǎng)度可以不同,并且可以動(dòng)態(tài)增長(zhǎng)。

5. 段頁(yè)式

程序的地址空間劃分成多個(gè)擁有獨(dú)立地址空間的段,每個(gè)段上的地址空間劃分成大小相同的頁(yè)。這樣既擁有分段系統(tǒng)的共享和保護(hù),又擁有分頁(yè)系統(tǒng)的虛擬內(nèi)存功能。

6. 分頁(yè)與分段的比較

對(duì)程序員的透明性:分頁(yè)透明,但是分段需要程序員顯示劃分每個(gè)段。

地址空間的維度:分頁(yè)是一維地址空間,分段是二維的。

大小是否可以改變:頁(yè)的大小不可變,段的大小可以動(dòng)態(tài)改變。

出現(xiàn)的原因:分頁(yè)主要用于實(shí)現(xiàn)虛擬內(nèi)存,從而獲得更大的地址空間;分段主要是為了使程序和數(shù)據(jù)可以被劃分為邏輯上獨(dú)立的地址空間并且有助于共享和保護(hù)。

五、設(shè)備管理

1. 磁盤結(jié)構(gòu)

盤面(Platter):一個(gè)磁盤有多個(gè)盤面;

磁道(Track):盤面上的圓形帶狀區(qū)域,一個(gè)盤面可以有多個(gè)磁道;

扇區(qū)(Track Sector):磁道上的一個(gè)弧段,一個(gè)磁道可以有多個(gè)扇區(qū),它是最小的物理儲(chǔ)存單位,目前主要有 512 bytes 與 4 K 兩種大小;

磁頭(Head):與盤面非常接近,能夠?qū)⒈P面上的磁場(chǎng)轉(zhuǎn)換為電信號(hào)(讀),或者將電信號(hào)轉(zhuǎn)換為盤面的磁場(chǎng)(寫);

制動(dòng)手臂(Actuator arm):用于在磁道之間移動(dòng)磁頭;

主軸(Spindle):使整個(gè)盤面轉(zhuǎn)動(dòng)。

2. 磁盤調(diào)度算法

讀寫一個(gè)磁盤塊的時(shí)間的影響因素有:

旋轉(zhuǎn)時(shí)間(主軸旋轉(zhuǎn)磁盤,使得磁頭移動(dòng)到適當(dāng)?shù)纳葏^(qū)上)

尋道時(shí)間(制動(dòng)手臂移動(dòng),使得磁頭移動(dòng)到適當(dāng)?shù)拇诺郎?

實(shí)際的數(shù)據(jù)傳輸時(shí)間

其中,尋道時(shí)間最長(zhǎng),因此磁盤調(diào)度的主要目標(biāo)是使磁盤的平均尋道時(shí)間最短。

1. 先來(lái)先服務(wù)

FCFS, First Come First Served

按照磁盤請(qǐng)求的順序進(jìn)行調(diào)度

公平對(duì)待所有進(jìn)程

在有很多進(jìn)程的情況下,接近隨機(jī)調(diào)度的性能

優(yōu)點(diǎn)是公平和簡(jiǎn)單。缺點(diǎn)也很明顯,因?yàn)槲磳?duì)尋道做任何優(yōu)化,使平均尋道時(shí)間可能較長(zhǎng)。

2. 最短尋道時(shí)間優(yōu)先

SSTF, Shortest Seek Time First

優(yōu)先調(diào)度與當(dāng)前磁頭所在磁道距離最近的磁道。

雖然平均尋道時(shí)間比較低,但是不夠公平。如果新到達(dá)的磁道請(qǐng)求總是比一個(gè)在等待的磁道請(qǐng)求近,那么在等待的磁道請(qǐng)求會(huì)一直等待下去,也就是出現(xiàn)饑餓現(xiàn)象。具體來(lái)說,兩邊的磁道請(qǐng)求更容易出現(xiàn)饑餓現(xiàn)象。

3. 電梯算法

SCAN

電梯總是保持一個(gè)方向運(yùn)行,直到該方向沒有請(qǐng)求為止,然后改變運(yùn)行方向。

電梯算法(掃描算法)和電梯的運(yùn)行過程類似,總是按一個(gè)方向來(lái)進(jìn)行磁盤調(diào)度,直到該方向上沒有未完成的磁盤請(qǐng)求,然后改變方向。

因?yàn)榭紤]了移動(dòng)方向,因此所有的磁盤請(qǐng)求都會(huì)被滿足,解決了 SSTF 的饑餓問題。

六、鏈接

1. 編譯系統(tǒng)

以下是一個(gè) hello.c 程序:

#include int main(){ printf("hello, world\n"); return 0;}

在 Uni__ 系統(tǒng)上,由編譯器把源文件轉(zhuǎn)換為目標(biāo)文件。

gcc -o hello hello.c

這個(gè)過程大致如下:

1. 預(yù)處理階段 (Preprocessing phase)

預(yù)處理(cpp)根據(jù)以字符 # 開頭的命令,修改原始的 C 程序,生成擴(kuò)展名為 .i 的文件。

$ gcc -E hello.c -o hello.i

2. 編譯階段 (Compilation phase)

編譯器(cc1)將文本文件 hello.i 翻譯成文本文件 hello.s,它包含一個(gè)匯編語(yǔ)言程序。

$ gcc -S hello.i -o hello.s

3. 匯編階段 (Assembly phase)

編譯器(as)將 hello.s 翻譯成機(jī)器語(yǔ)言指令,把這些指令打包成一種叫做可重定位目標(biāo)程序(relocatable object program)的格式,并將結(jié)果保存在目標(biāo)文件 hello.o 中。

$ as hello.s -o hello.o

4. 鏈接階段 (Linking phase)

printf 函數(shù)是標(biāo)準(zhǔn) C 庫(kù)中的一個(gè)函數(shù),在 printf.o 這個(gè)單獨(dú)預(yù)編譯好的目標(biāo)文件中。連接器(ld)將 printf.o 和 hello.o 合并,結(jié)果得到 hello 可執(zhí)行目標(biāo)文件。

$ gcc hello.o -o hello

2. 靜態(tài)鏈接

靜態(tài)連接器以一組可重定向目標(biāo)文件為輸入,生成一個(gè)完全鏈接的可執(zhí)行目標(biāo)文件作為輸出。鏈接器主要完成以下兩個(gè)任務(wù):

符號(hào)解析:每個(gè)符號(hào)對(duì)應(yīng)于一個(gè)函數(shù)、一個(gè)全局變量或一個(gè)靜態(tài)變量,符號(hào)解析的目的是將每個(gè)符號(hào)引用與一個(gè)符號(hào)定義關(guān)聯(lián)起來(lái)。

重定位:鏈接器通過把每個(gè)符號(hào)定義與一個(gè)內(nèi)存位置關(guān)聯(lián)起來(lái),然后修改所有對(duì)這些符號(hào)的引用,使得它們指向這個(gè)內(nèi)存位置。

3. 目標(biāo)文件

可執(zhí)行目標(biāo)文件:可以直接在內(nèi)存中執(zhí)行;

可重定向目標(biāo)文件:可與其它可重定向目標(biāo)文件在鏈接階段合并,創(chuàng)建一個(gè)可執(zhí)行目標(biāo)文件;

共享目標(biāo)文件:這是一種特殊的可重定向目標(biāo)文件,可以在運(yùn)行時(shí)被動(dòng)態(tài)加載進(jìn)內(nèi)存并鏈接;

4. 動(dòng)態(tài)鏈接

靜態(tài)庫(kù)有以下兩個(gè)問題:

當(dāng)靜態(tài)庫(kù)更新時(shí)那么整個(gè)程序都要重新進(jìn)行鏈接;

對(duì)于 printf 這種標(biāo)準(zhǔn)函數(shù)庫(kù),如果每個(gè)程序都要有代碼,這會(huì)極大浪費(fèi)資源。

共享庫(kù)是為了解決靜態(tài)庫(kù)的這兩個(gè)問題而設(shè)計(jì)的,在 Linu__ 系統(tǒng)中通常用 .so 后綴來(lái)表示,Windows 系統(tǒng)上它們被稱為 DLL。它具有以下特點(diǎn):

在給定的文件系統(tǒng)中一個(gè)庫(kù)只有一個(gè)文件,所有引用該庫(kù)的可執(zhí)行目標(biāo)文件都共享這個(gè)文件,它不會(huì)被復(fù)制到引用它的可執(zhí)行文件中;

在內(nèi)存中,一個(gè)共享庫(kù)的 .te__t 節(jié)(已編譯程序的機(jī)器代碼)的一個(gè)副本可以被不同的正在運(yùn)行的進(jìn)程共享。

學(xué)完這份計(jì)算機(jī)基礎(chǔ)知識(shí)與操作系統(tǒng)后,帥地飄了

1、操作系統(tǒng)的四個(gè)特征

有以下四個(gè)特征:

并發(fā)

共享

虛擬

異步

接下來(lái),我們分別來(lái)搞定每一個(gè)特征。

1.1 并發(fā)是什么?和并行有啥區(qū)別?

舉個(gè)例子,假如你在語(yǔ)音跟同學(xué)玩英雄聯(lián)盟:

你一邊用鼠標(biāo)移動(dòng)打游戲,同時(shí)語(yǔ)音嘴里說"隊(duì)友掛機(jī),真坑!", 這叫并行(邊移動(dòng)鼠標(biāo)邊語(yǔ)音BB)

你一邊用鼠標(biāo)移動(dòng)打游戲,然后離開鼠標(biāo),去砸鍵盤, 這叫并發(fā)(先離開鼠標(biāo)然后砸鍵盤)

并發(fā)只是把時(shí)間分成若干段,使多個(gè)任務(wù)交替的執(zhí)行。

并行的關(guān)鍵是你有同時(shí)處理多個(gè)任務(wù)的能力。

所以我認(rèn)為它們最關(guān)鍵的點(diǎn)就是:是否是『同時(shí)』

那么對(duì)于操作系統(tǒng)而言,操作系統(tǒng)的并發(fā)性指計(jì)算機(jī)系統(tǒng)中同時(shí)存在多個(gè)運(yùn)行著的程序。

比如說以前的計(jì)算機(jī)是單核CPU,那么如何在操作系統(tǒng)上同時(shí)運(yùn)行QQ、瀏覽器,記事本、ppt等多個(gè)程序呢,這就需要操作系統(tǒng)具有并發(fā)性

CPU時(shí)間片(操作系統(tǒng)分配給每個(gè)正在運(yùn)行的進(jìn)程微觀上的一段CPU時(shí)間)輪著給進(jìn)程執(zhí)行的時(shí)間,因?yàn)閳?zhí)行速度很快,看起來(lái)就像瀏覽器能同時(shí)執(zhí)行任務(wù)一樣。

有人會(huì)說,現(xiàn)在都多核CPU了,還需要并發(fā)嗎,答案肯定是需要的,比如你有8核CPU,但是桌面要執(zhí)行的任務(wù)很可能超過8個(gè)。

1.2 共享是什么?共享和并發(fā)有什么關(guān)系?

舉一個(gè)例子:

你同時(shí)用QQ和微信發(fā)"年終述職.ppt"文件給領(lǐng)導(dǎo),這時(shí)候QQ和微信都在讀取這個(gè)ppt文件

兩個(gè)進(jìn)程正在并發(fā)執(zhí)行(并發(fā)性)

需要共享地訪問硬盤資源(共享性)

如果沒有并發(fā),也就是只有一個(gè)進(jìn)程在運(yùn)行,那就沒有共享了。如果沒有共享,QQ和微信就不能同時(shí)發(fā)文件,無(wú)法同時(shí)訪問硬盤資源,也就無(wú)法并發(fā)。

其中共享分為兩種情況:

上面的例子,QQ和微信都要訪問同一個(gè)文件,屬于同時(shí)共享。

對(duì)于互斥共享,比如打印機(jī),只能同一時(shí)刻被一個(gè)進(jìn)程控制,如打印機(jī),雖然他可以提供多個(gè)進(jìn)程使用,但是試想,同時(shí)打印多個(gè)東西,會(huì)造成打印結(jié)果的混亂,因此規(guī)定,某些資源在進(jìn)行使用的時(shí)候,必須要先讓某進(jìn)程先使用,等使用完之后,再同一其他進(jìn)程進(jìn)行訪問。

我們把一段時(shí)間內(nèi)只允許一個(gè)進(jìn)程訪問的資源稱為獨(dú)占資源,或臨界資源。

1.3 虛擬是啥?

先舉例,再說定義。

假如一個(gè)叫范桶的貨車司機(jī)在玩英雄聯(lián)盟,平時(shí)因?yàn)榫岂{太多,自己裝了很多次別人的車,住院也花了不少錢,所以家里沒錢,只能買個(gè)1G內(nèi)存的二手電腦玩游戲??捎⑿勐?lián)盟至少需要2G內(nèi)存,這就奇怪了,老司機(jī)雖然一到團(tuán)戰(zhàn)就卡死,但是還是能運(yùn)行英雄聯(lián)盟。為什么需要2G內(nèi)存的游戲,1G電腦還能運(yùn)行呢?

這就是虛擬存儲(chǔ)器技術(shù)。實(shí)際上只有1G內(nèi)存,在用戶看來(lái)遠(yuǎn)遠(yuǎn)大于1G。

還有,范桶的電腦還是單核的,但范桶居然能一邊迅雷下著愛情動(dòng)作片,一邊聽著網(wǎng)易云音樂,還在QQ上撩妹子,既然一個(gè)程序要被分配CPU才能正常執(zhí)行,按道理來(lái)說同一時(shí)間只有1個(gè)程序在運(yùn)行,為啥電腦能同時(shí)運(yùn)行這么多程序呢?

這就是虛擬處理器技術(shù)。實(shí)際上只有一個(gè)CPU,在用戶看來(lái)有3個(gè)CPU在同時(shí)服務(wù)。(因?yàn)镃PU來(lái)回切換進(jìn)程的速度特別塊,感覺就像很多CPU在為我們服務(wù))

虛擬這塊的總結(jié)如下:

1.4 異步性是啥?

異步在JS里是很常見的,比如aja__請(qǐng)求,我們發(fā)出請(qǐng)求后并不是立馬得到信息,也不會(huì)去等待aja__結(jié)果返回,而是繼續(xù)執(zhí)行下面的代碼,等aja__結(jié)果回來(lái),通知JS線程。這就跟CPU處理進(jìn)程很類似。

比如,CPU正在執(zhí)行一個(gè)進(jìn)程,進(jìn)程需要讀取文件,讀取文件可能要1個(gè)小時(shí),那CPU不可能一直等一個(gè)小時(shí),CPU會(huì)繼續(xù)把時(shí)間片分給別的進(jìn)程,等文件讀取完成了(類似aja__返回結(jié)果了),CPU再繼續(xù)執(zhí)行之前被中斷的進(jìn)程。

所以異步性就是描述進(jìn)程這種以不可預(yù)知的速度走走停停、何時(shí)開始何時(shí)暫停何時(shí)結(jié)束不可預(yù)知的性質(zhì)。

2、操作系統(tǒng)運(yùn)行機(jī)制和體系結(jié)構(gòu)

預(yù)備知識(shí):什么是指令

比如說,如下圖(簡(jiǎn)單掃一下即可):

a+b是一段程序代碼,a+b在CPU看來(lái)并不能一步完成,可以翻譯成如下:

// 意思是將內(nèi)存的16號(hào)單元數(shù)據(jù),放到A寄存器,LOAD A, 16// 意思是將內(nèi)存的16號(hào)單元數(shù)據(jù),放到B寄存器LOAD B, 17// 存器里的A,B數(shù)據(jù)相加,得到CADD C, A, B

這里就可以看得出來(lái),指令是CPU能識(shí)別和執(zhí)行的最基本命令。

2.1 兩種指令、兩種處理器狀態(tài)、兩種程序

假如說一個(gè)用戶可以隨意把服務(wù)器上的所有文件刪光,這是很危險(xiǎn)的。所以有些指令普通用戶是不能使用的,只能是權(quán)限較高的用戶能使用。此時(shí)指令就分為了兩種,如下圖:

這就引出一個(gè)問題:CPU如何判斷當(dāng)前是否可以執(zhí)行特權(quán)指令?

如下圖:

CPU通常有兩種工作模式即:內(nèi)核態(tài)和用戶態(tài),而在PSW(這個(gè)不用管,就知道有一個(gè)寄存器的標(biāo)志位0表示用戶態(tài),1表示核心態(tài))中有一個(gè)二進(jìn)制位控制這兩種模式。

對(duì)于應(yīng)用程序而言,有的程序能執(zhí)行特權(quán)指令,有的程序只能執(zhí)行非特權(quán)指令。所以操作系統(tǒng)里的程序又分為兩種:

2.2 操作系統(tǒng)內(nèi)核簡(jiǎn)單介紹

從下圖,我們先看看操作系統(tǒng)內(nèi)核包含哪些

操作系統(tǒng)內(nèi)核中跟硬件緊密相關(guān)的部分有:

時(shí)鐘管理。操作系統(tǒng)的時(shí)鐘管理是依靠硬件定時(shí)器的(具體硬件怎么實(shí)現(xiàn)我也不太清楚,好像是靠硬件周期性的產(chǎn)生一個(gè)脈沖信號(hào)實(shí)現(xiàn)的)。時(shí)鐘管理相當(dāng)重要,比如我們獲取時(shí)間信息,進(jìn)程切換等等都是要依靠時(shí)鐘管理。

中斷處理(下一小節(jié)會(huì)詳細(xì)介紹)。

原語(yǔ)(后面會(huì)有案例提到)?,F(xiàn)在可以簡(jiǎn)單理解為用來(lái)實(shí)現(xiàn)某個(gè)特定功能,在執(zhí)行過程中不可被中斷的指令集合。原語(yǔ)有一個(gè)非常重要的特性,就是原子性(其運(yùn)行一氣呵成,不可中斷)。

2.3 中斷

在程序運(yùn)行過程中,系統(tǒng)出現(xiàn)了一個(gè)必須由CPU立即處理的情況,此時(shí),CPU暫時(shí)中止程序的執(zhí)行轉(zhuǎn)而處理這個(gè)新的情況的過程就叫做中斷。

下面舉一個(gè)例子:

第一個(gè)應(yīng)用程序在用戶態(tài)執(zhí)行了一段時(shí)間后

接著操作系統(tǒng)切換到核心態(tài),處理中斷信號(hào)

操作系統(tǒng)發(fā)現(xiàn)中斷的信號(hào)是第一個(gè)程序的時(shí)間片(每個(gè)程序不能一直執(zhí)行,CPU會(huì)給每個(gè)程序一定的執(zhí)行時(shí)間,這段時(shí)間就是時(shí)間片)用完了,應(yīng)該換第二個(gè)應(yīng)用程序執(zhí)行了

切換到第2個(gè)進(jìn)程后,操作系統(tǒng)會(huì)將CPU的使用權(quán)交換給第二個(gè)應(yīng)用程序,接著第二個(gè)應(yīng)用程序就在用戶態(tài)下開始執(zhí)行。

進(jìn)程2需要調(diào)用打印機(jī)資源,這時(shí)會(huì)執(zhí)行一個(gè)系統(tǒng)調(diào)用(后面會(huì)講系統(tǒng)調(diào)用,這里簡(jiǎn)單理解為需要操作系統(tǒng)進(jìn)入核心態(tài)處理的函數(shù)),讓操作系統(tǒng)進(jìn)入核心態(tài),去調(diào)用打印機(jī)資源

打印機(jī)開始工作,此時(shí)進(jìn)程2因?yàn)橐却蛴C(jī)啟動(dòng),操作系統(tǒng)就不等待了(等到打印機(jī)準(zhǔn)備好了,再回來(lái)執(zhí)行程序2),直接切換到第三個(gè)應(yīng)用程序執(zhí)行

等到打印機(jī)準(zhǔn)備好了,此時(shí)打印機(jī)通過I/O控制器會(huì)給操作系統(tǒng)發(fā)出一個(gè)中斷信號(hào),操作系統(tǒng)又進(jìn)入到核心態(tài),發(fā)現(xiàn)這個(gè)中斷是因?yàn)槌绦?等待打印機(jī)資源,現(xiàn)在打印機(jī)準(zhǔn)備好了,就切換到程序2,切換到用戶態(tài),把CPU給程序2繼續(xù)執(zhí)行。

好了,現(xiàn)在可以給出一個(gè)結(jié)論,就是用戶態(tài)、核心態(tài)之間的切換是怎么實(shí)現(xiàn)的?

"用戶態(tài) ---> 核心態(tài)"是通過中斷實(shí)現(xiàn)的。并且中斷時(shí)唯一途徑。

"核心態(tài) ---> 用戶態(tài)"的切換時(shí)通過執(zhí)行一個(gè)特權(quán)指令,將程序狀態(tài)的標(biāo)志位設(shè)為用戶態(tài)。

2.4 中斷的分類

舉一個(gè)例子,什么是內(nèi)中斷和外中斷:

接著說之前的范桶同學(xué),小時(shí)候不愛學(xué)習(xí),每次學(xué)習(xí)著學(xué)習(xí)著突然異想天開,回過神來(lái)已經(jīng)過好好長(zhǎng)一段時(shí)間,這是內(nèi)部中斷。想著想著老師走過來(lái),給了范捅一嘴巴,這是外部中斷。

官方解釋如下:

內(nèi)中斷常見的情況如程序非法操作(比如你要拿的的數(shù)據(jù)的內(nèi)存地址不是內(nèi)存地址,是系統(tǒng)無(wú)法識(shí)別的地址),地址越界(比如系統(tǒng)給你的程序分配了一些內(nèi)存,但是你訪問的時(shí)候超出了你應(yīng)該訪問的內(nèi)存范圍)、浮點(diǎn)溢出(比如系統(tǒng)只能表示1.1到5.1的范圍,你輸入一個(gè)100, 超出了計(jì)算機(jī)能處理的范圍),或者異常,陷入trap(是指應(yīng)用程序請(qǐng)求系統(tǒng)調(diào)用造成的,什么是系統(tǒng)調(diào)用,后面小節(jié)會(huì)舉例講)。

外中斷常見的情況如I/O中斷(由I/O控制器產(chǎn)生,用于發(fā)送信號(hào)通知操作完成等信號(hào),比如進(jìn)程需要請(qǐng)求打印機(jī)資源,打印機(jī)有一個(gè)啟動(dòng)準(zhǔn)備的過程,準(zhǔn)備好了就會(huì)給CPU一個(gè)I/O中斷,告訴它已經(jīng)準(zhǔn)備好了)、時(shí)鐘中斷(由處理器內(nèi)部的計(jì)時(shí)器產(chǎn)生,允許操作系統(tǒng)以一定規(guī)程執(zhí)行函數(shù),操作系統(tǒng)每過大約15ms會(huì)進(jìn)行一次線程調(diào)度,就是利用時(shí)鐘中斷來(lái)實(shí)現(xiàn)的)。

2.5 系統(tǒng)調(diào)用

為什么需要系統(tǒng)調(diào)用?

比如你的程序需要讀取文件信息,可讀取文件屬于讀取硬盤里的數(shù)據(jù),這個(gè)操作應(yīng)該時(shí)CPU在內(nèi)核態(tài)去完成的,我們的應(yīng)用程序怎么讓CPU去幫助我們切換到內(nèi)核態(tài)完成這個(gè)工作呢,這里就需要系統(tǒng)調(diào)用了。

這里就引出系統(tǒng)調(diào)用的概念和作用。

應(yīng)用程序通過系統(tǒng)調(diào)用請(qǐng)求操作系統(tǒng)的服務(wù)。系統(tǒng)中的各種共享資源都由操作系統(tǒng)統(tǒng)一管理,因此在用戶程序中,凡是與資源有關(guān)的操作(如存儲(chǔ)分配、I/O操作、文件管理等),都必須通過系統(tǒng)調(diào)用的方式向操作系統(tǒng)提出服務(wù)請(qǐng)求,由操作系統(tǒng)代為完成。

以下內(nèi)容簡(jiǎn)單看一下即可,系統(tǒng)調(diào)用的分類:

需要注意的是,庫(kù)函數(shù)和系統(tǒng)調(diào)用容易混淆。

庫(kù)是可重用的模塊 處于用戶態(tài)

進(jìn)程通過系統(tǒng)調(diào)用從用戶態(tài)進(jìn)入內(nèi)核態(tài), 庫(kù)函數(shù)中有很大部分是對(duì)系統(tǒng)調(diào)用的封裝

舉個(gè)例子:比如windows和linu__中,創(chuàng)建進(jìn)程的系統(tǒng)調(diào)用方法是不一樣的。但在node中的只需要調(diào)用相同函數(shù)方法就可以創(chuàng)建一個(gè)進(jìn)程。例如

// 引入創(chuàng)建子進(jìn)程的模塊const childProcess = require('child_process')// 獲取cpu的數(shù)量const cpuNum = require('os').cpus().length// 創(chuàng)建與cpu數(shù)量一樣的子進(jìn)程for (let i = 0; i < cpuNum; ++i) { childProcess.fork('./worker.js')}

2.6 進(jìn)程的定義、組成、組織方式、狀態(tài)與轉(zhuǎn)換

2.6.1 為什么要引入進(jìn)程的概念呢?

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

現(xiàn)代計(jì)算機(jī)是多道程序執(zhí)行,就是同時(shí)看起來(lái)有多個(gè)程序在一起執(zhí)行,那每個(gè)程序執(zhí)行都需要系統(tǒng)分配給它資源來(lái)執(zhí)行,比如CPU、內(nèi)存。

拿內(nèi)存來(lái)說,操作系統(tǒng)要知道給A程序分配的內(nèi)存有哪些,給B程序分配的內(nèi)存有哪些,這些都要有小本本記錄下來(lái),這個(gè)小本本就是進(jìn)程的一部分,進(jìn)程的一大職責(zé)就是記錄目前程序運(yùn)行的狀態(tài)。

系統(tǒng)為每個(gè)運(yùn)行的程序配置一個(gè)數(shù)據(jù)結(jié)構(gòu),稱為進(jìn)程控制塊(PCB),用來(lái)描述進(jìn)程的各種信息(比如代碼段放在哪)。

2.6.2 進(jìn)程的定義?

簡(jiǎn)要的說,進(jìn)程就是具有獨(dú)立功能的程序在數(shù)據(jù)集合上運(yùn)行的過程。(強(qiáng)調(diào)動(dòng)態(tài)性)

2.6.3 PCB有哪些組成

如下圖,分別說明一下

進(jìn)程標(biāo)識(shí)符PID相當(dāng)于身份證。是在進(jìn)程被創(chuàng)建時(shí),操作系統(tǒng)會(huì)為該進(jìn)程分配一個(gè)唯一的、不重復(fù)的ID,用于區(qū)分不同的進(jìn)程。

用戶標(biāo)識(shí)符UID用來(lái)表示這個(gè)進(jìn)程所屬的用戶是誰(shuí)。

進(jìn)程當(dāng)前狀態(tài)和優(yōu)先級(jí)下一小節(jié)會(huì)詳細(xì)介紹

程序段指針是指當(dāng)前進(jìn)程的程序在內(nèi)存的什么地方。

數(shù)據(jù)段指針是指當(dāng)前進(jìn)程的數(shù)據(jù)在內(nèi)存的什么地方。

鍵盤和鼠標(biāo)是指進(jìn)程被分配得到的I/O設(shè)備。

各種寄存器值是指比如把程序計(jì)數(shù)器的值,比如有些計(jì)算的結(jié)果算到一半,進(jìn)程切換時(shí)需要把這些值保存下來(lái)。

2.6.4 進(jìn)程的組織

在一個(gè)系統(tǒng)中,通常由數(shù)十、數(shù)百乃至數(shù)千個(gè)PCB。為了對(duì)他們加以有效的管理,應(yīng)該用適當(dāng)?shù)姆绞桨堰@些PCB組織起來(lái)。這里介紹一種組織方式,類似數(shù)據(jù)結(jié)構(gòu)里的鏈表。

2.6.5 進(jìn)程的狀態(tài)

進(jìn)程是程序的一次執(zhí)行。在這個(gè)執(zhí)行過程中,有時(shí)進(jìn)程正在被CPU處理,有時(shí)又需要等待CPU服務(wù),可見,進(jìn)程的 狀態(tài)是會(huì)有各種變化。為了方便對(duì)各個(gè)進(jìn)程的管理,操作系統(tǒng)需要將進(jìn)程合理地劃分為幾種狀態(tài)。

進(jìn)程的三種基本狀態(tài):

進(jìn)程的另外兩種狀態(tài):

2.6.6 進(jìn)程狀態(tài)的轉(zhuǎn)換

進(jìn)程的狀態(tài)并不是一成不變的,在一定情況下會(huì)動(dòng)態(tài)轉(zhuǎn)換。

以上的這些進(jìn)程狀態(tài)的轉(zhuǎn)換是如何實(shí)現(xiàn)的呢,這就要引出下一個(gè)角色了,叫原語(yǔ)。

原語(yǔ)是不可被中斷的原子操作。我們舉一個(gè)例子看看原語(yǔ)是怎么保證不可中斷的。

原語(yǔ)采用關(guān)中斷指令和開中斷指令實(shí)現(xiàn)。

首先執(zhí)行關(guān)中斷指令

然后外部來(lái)了中斷信號(hào),不予以處理

等到開中斷指令執(zhí)行后,其他中斷信號(hào)才有機(jī)會(huì)處理。

¨K46K ¨K11K 因?yàn)檫M(jìn)程是分配系統(tǒng)資源的單位(包括內(nèi)存地址空間),因此各進(jìn)程擁有的內(nèi)存地址空間相互獨(dú)立。!w=1679&h=683&f=png&s=326726) ¨K47K 因?yàn)閮蓚€(gè)進(jìn)程的存儲(chǔ)空間不能相互訪問,所以操作系統(tǒng)就提供的一個(gè)內(nèi)存空間讓彼此都能訪問,這就是共享存儲(chǔ)的原理。其中,介紹一下基于存儲(chǔ)區(qū)的共享。

在內(nèi)存中畫出一塊共享存儲(chǔ)區(qū),數(shù)據(jù)的形式、存放位置都是由進(jìn)程控制,而不是操作系統(tǒng)。

管道數(shù)據(jù)是以字符流(注意不是字節(jié)流)的形式寫入管道,當(dāng)管道寫滿時(shí),寫進(jìn)程的write()系統(tǒng)調(diào)用將被阻塞,等待讀進(jìn)程將數(shù)據(jù)取走。當(dāng)讀進(jìn)程將數(shù)據(jù)全部取走后,管道變空,此時(shí)讀進(jìn)程的read()系統(tǒng)調(diào)用將被阻塞。

如果沒寫滿就不允許讀。如果都沒空就不允許寫。

數(shù)據(jù)一旦被讀出,就從管道中被丟棄,這就意味著讀進(jìn)程最多只能有一個(gè)。

¨K49K 進(jìn)程間的數(shù)據(jù)交換以格式化的消息為單位。進(jìn)程通過操作系統(tǒng)提供的"發(fā)送消息/接收消息"兩個(gè)原語(yǔ)進(jìn)行數(shù)據(jù)交換。其中消息是什么意思呢?就好像你發(fā)QQ消息,消息頭的來(lái)源是你,消息體是你發(fā)的內(nèi)容。

比如你在玩QQ的時(shí)候,QQ是一個(gè)進(jìn)程,如果QQ的進(jìn)程里沒有多線程并發(fā),那么QQ進(jìn)程就只能同一時(shí)間做一件事情(比如QQ打字聊天)

但是我們真實(shí)的場(chǎng)景是QQ聊天的同時(shí),還可以發(fā)文件,還可以視頻聊天,這說明如果QQ沒有多線程并發(fā)能力,QQ能夠的實(shí)用性就大大降低了。所以我們需要線程,也就是需要進(jìn)程擁有能夠并發(fā)多個(gè)事件的能力。

信號(hào)量主要是來(lái)解決進(jìn)程的同步和互斥的,我們前端需要了解,如果涉及到同步和互斥的關(guān)系(我們編程大多數(shù)關(guān)于流程的邏輯問題,本質(zhì)不就是同步和互斥嗎?) 在操作系統(tǒng)中,常用P、V信號(hào)量來(lái)實(shí)現(xiàn)進(jìn)程間的同步和互斥,我們簡(jiǎn)單了解一下一種常用的信號(hào)量,記錄型信號(hào)量來(lái)簡(jiǎn)單了解一下信號(hào)量本質(zhì)是怎樣的。(c語(yǔ)言來(lái)表示,會(huì)有備注) ¨G2G 意思是信號(hào)量的結(jié)構(gòu)有兩部分組成,一部分是剩余資源value,比如目前有兩臺(tái)打印機(jī)空閑,那么剩余資源就是2,誰(shuí)正在使用打印機(jī),剩余資源就減1。 Struct process __L 意思是,比如2臺(tái)打印機(jī)都有人在用,這時(shí)候你的要用打印機(jī),此時(shí)會(huì)把這個(gè)打印機(jī)資源的請(qǐng)求放入阻塞隊(duì)列,L就是阻塞隊(duì)列的地址。¨G3G 需要注意的是,如果剩余資源數(shù)不夠,使用block原語(yǔ)使進(jìn)程從運(yùn)行態(tài)進(jìn)入阻塞態(tài),并把掛到信號(hào)量S的等待隊(duì)列中?!4G 釋放資源后,若還有別的進(jìn)程在等待這個(gè)資源,比如打印機(jī)資源,則使用wakeup原語(yǔ)喚醒等待隊(duì)列中的一個(gè)進(jìn)程,該進(jìn)程從阻塞態(tài)變?yōu)槔^續(xù)態(tài)?!53K 為什么要講這個(gè)呢,主要是node的流的機(jī)制,本質(zhì)就是生產(chǎn)者消費(fèi)者問題,可以簡(jiǎn)單的看看這個(gè)問題如何解決。!生產(chǎn)者的主要作用是生成一定量的數(shù)據(jù)放到緩沖區(qū)中,然后重復(fù)此過程。與此同時(shí),消費(fèi)者也在緩沖區(qū)消耗這些數(shù)據(jù)。該問題的關(guān)鍵就是要保證生產(chǎn)者不會(huì)在緩沖區(qū)滿時(shí)加入數(shù)據(jù),消費(fèi)者也不會(huì)在緩沖區(qū)中空時(shí)消耗數(shù)據(jù)。這里我們需要兩個(gè)同步信號(hào)量和一個(gè)互斥信號(hào)量 ¨G5G 生產(chǎn)者代碼如下 ¨G6G 消費(fèi)者代碼如下 ¨G7G ¨K54K ¨K19K 內(nèi)存是計(jì)算機(jī)其它硬件設(shè)備與``CPU溝通的橋梁、中轉(zhuǎn)站。程序執(zhí)行前需要先放到內(nèi)存中才能被CPU處理。

4.1 cpu如何區(qū)分執(zhí)行程序的數(shù)據(jù)在內(nèi)存的什么地方

是通過給內(nèi)存的存儲(chǔ)單元編址實(shí)現(xiàn)的。(存儲(chǔ)單元一般是以字節(jié)為單位)

如下圖,內(nèi)存的存儲(chǔ)單元,就像一個(gè)酒店的房間,都有編號(hào),比如程序一的數(shù)據(jù)都在1樓,1樓1號(hào)存儲(chǔ)著程序里let a = 1這段代碼。

4.2 內(nèi)存管理-內(nèi)存空間的分配與回收

內(nèi)存分配分為連續(xù)分配和非連續(xù)分配,連續(xù)分配是指用戶進(jìn)程分配的必須是一個(gè)連續(xù)的內(nèi)存空間。

這里我們只講連續(xù)分配中的動(dòng)態(tài)分區(qū)分配。

什么是動(dòng)態(tài)分區(qū)分配呢,這種分配方式不會(huì)預(yù)先劃分內(nèi)存分區(qū),而是在進(jìn)程裝入內(nèi)存時(shí),根據(jù)進(jìn)程的大小動(dòng)態(tài)地建立分區(qū),并使分區(qū)的大小正好適合進(jìn)程的需要。(比如,某計(jì)算機(jī)內(nèi)存大小64MB,系統(tǒng)區(qū)8MB,用戶區(qū)56MB…,現(xiàn)在我們有幾個(gè)進(jìn)程要裝入內(nèi)存,如下圖)

隨之而來(lái)的問題就是,如果此時(shí)進(jìn)程1使用完了,相應(yīng)在內(nèi)存上的數(shù)據(jù)也被刪除了,那么空閑的區(qū)域,后面該怎么分配(也就是說隨著進(jìn)程退出,會(huì)有很多空閑的內(nèi)存區(qū)域出現(xiàn))

我們講一種較為簡(jiǎn)單的處理方法叫空閑分區(qū)表法來(lái)解決這個(gè)問題。如下圖,右側(cè)的表格就是一個(gè)空閑分區(qū)表。

當(dāng)很多個(gè)空閑分區(qū)都能滿足需求時(shí),應(yīng)該選擇哪個(gè)分區(qū)進(jìn)行分配呢,例如下圖,分別有20MB,10MB,4MB三個(gè)空閑分區(qū)塊,現(xiàn)在進(jìn)程5需要4MB空閑分區(qū),改怎么分配呢?

我們需要按照一定的動(dòng)態(tài)分區(qū)分配算法,比如有首次適應(yīng)算法,指的是每次都從低地址開始查找,找到第一個(gè)能滿足大小的空閑分區(qū)。還有比如最佳適應(yīng)算法,指的是從空閑分區(qū)表中找到最小的適合分配的分區(qū)塊來(lái)滿足需求。

連續(xù)分配缺點(diǎn)很明顯,大多數(shù)情況,需要分配的進(jìn)程大小,不能跟空閑分區(qū)剩下的大小完全一樣,這樣就產(chǎn)生很多很難利用的內(nèi)存碎片。

這里我們介紹一種更好的空閑分區(qū)的分配方法,基本分頁(yè)存儲(chǔ)。如下圖

將內(nèi)存空間分為一個(gè)個(gè)大小相等的分區(qū)(比如:每個(gè)分區(qū)4KB).每個(gè)分區(qū)就是一個(gè)“頁(yè)框”。頁(yè)框號(hào)從0開始。

將用戶進(jìn)程的地址空間分為與頁(yè)框大小相等的一個(gè)個(gè)區(qū)域,稱為“頁(yè)”。每個(gè)頁(yè)也是從0開始。

進(jìn)程的頁(yè)與內(nèi)存的頁(yè)框有著一一對(duì)應(yīng)的關(guān)系。各個(gè)頁(yè)不必連續(xù)存放,也不必按先后順序來(lái),可以放到不相鄰的各個(gè)頁(yè)框中。

5 文件管理

文件是什么?

文件就是一組有意義的信息/數(shù)據(jù)集合。

計(jì)算機(jī)中存放了各種各樣的文件,一個(gè)文件有哪些屬性呢?文件內(nèi)部的數(shù)據(jù)應(yīng)該怎樣組織起來(lái)?文件之間又該怎么組織起來(lái)?

5.1 文件的屬性

文件名。即文件的名字,需要注意的是,同一目錄下不允許有重名的文件。

標(biāo)識(shí)符。操作系統(tǒng)用于區(qū)分各個(gè)文件的一種內(nèi)部的名稱。

類型。文件的類型。

位置。文件存放的路徑,同時(shí)也是在硬盤里的位置(需要轉(zhuǎn)換成物理硬盤上的地址)

創(chuàng)建時(shí)間、上次修改時(shí)間、文件所有者就是字面意思。

保護(hù)信息。比如對(duì)這個(gè)文件的執(zhí)行權(quán)限,是否有刪除文件權(quán)限,修改文件權(quán)限等等。

5.2 文件內(nèi)部數(shù)據(jù)如何組織在一起

如下圖,文件主要分為有結(jié)構(gòu)文件和無(wú)結(jié)構(gòu)文件。

5.3 文件之間如何組織起來(lái)

通過樹狀結(jié)構(gòu)組織的。例如windows的文件間的組織關(guān)系如下:

接下來(lái)我們?cè)敿?xì)的了解一下文件的邏輯結(jié)構(gòu)

5.4 文件的邏輯結(jié)構(gòu)

邏輯結(jié)構(gòu)是指,在用戶看來(lái),文件內(nèi)部的數(shù)據(jù)是如何組織起來(lái)的,而“物理結(jié)構(gòu)”是在操作系統(tǒng)看來(lái),文件是如何保存在外存,比如硬盤中的。

比如,“線性表”就是一種邏輯結(jié)構(gòu),在用戶看來(lái),線性表就是一組有先后關(guān)系的元素序列,如:a,b,c,d,e....

“線性表”這種邏輯結(jié)構(gòu)可以用不同的物理結(jié)構(gòu)實(shí)現(xiàn),比如:順序表/鏈表。順序表的各個(gè)元素在邏輯上相鄰,在物理上也相鄰:而鏈表的各個(gè)元素在物理上可以是不相鄰的。

因此,順序表可以實(shí)現(xiàn)“隨機(jī)訪問”,而“鏈表”無(wú)法實(shí)現(xiàn)隨機(jī)訪問。

接下來(lái)我了解一下有結(jié)構(gòu)文件的三種邏輯結(jié)構(gòu)

5.4.1 順序文件

什么是順序文件

指的是文件中的記錄一個(gè)接一個(gè)地在邏輯上是順序排列,記錄可以是定長(zhǎng)或變長(zhǎng),各個(gè)記錄在物理上可以順序存儲(chǔ)或鏈?zhǔn)酱鎯?chǔ)

順序文件按結(jié)構(gòu)來(lái)劃分,可以分為串結(jié)構(gòu)和順序結(jié)構(gòu)。

串結(jié)構(gòu)是指記錄之間的順序與關(guān)鍵字無(wú)關(guān),通常都是按照記錄的時(shí)間決定記錄的順序。

順序結(jié)構(gòu)就必須保證記錄之間的先后順序按關(guān)鍵字排列。

這里需要注意的知識(shí)點(diǎn)是,順序文件的存儲(chǔ)方式和是否按關(guān)鍵字排列,會(huì)影響數(shù)據(jù)是否支持隨機(jī)存取和是否可以快速按關(guān)鍵字找到對(duì)應(yīng)記錄的功能。

5.4.2 索引文件

對(duì)于可變長(zhǎng)記錄文件,要找到第i個(gè)記錄,必須先順序查找前i-1個(gè)記錄,但是很多場(chǎng)景中又必須使用可變長(zhǎng)記錄,如何解決這個(gè)問題呢?這就引出來(lái)馬上講的索引文件

給這些變長(zhǎng)的記錄都用一張索引表來(lái)記錄,一個(gè)索引表項(xiàng)包括了索引號(hào),長(zhǎng)度和指針。

其中,可以將關(guān)鍵字作為索引號(hào)的內(nèi)容,如果關(guān)鍵字本身的排列是有序的,那么還可以按照關(guān)鍵字進(jìn)行折半查找。

但是建立索引表的問題也很明顯,首先若要?jiǎng)h除/增加一個(gè)記錄,同時(shí)也要對(duì)索引表操作,其次,如果增加一條記錄才1KB,但是索引表增加i一條記錄可能有8KB,以至于索引表的體積大大多于記錄。存儲(chǔ)空間的利用率就比較低。

5.4.3 索引順序文件

索引順序文件是索引文件和順序文件思想的結(jié)合。索引順序文件中,同樣會(huì)為文件建立一張索引表,但不同的是,并不是每個(gè)記錄對(duì)應(yīng)一個(gè)索引表項(xiàng),而是一組記錄對(duì)應(yīng)一個(gè)索引表項(xiàng)。

5.5 文件目錄

首先,我們需要了解一下文件控制塊是什么。我們假設(shè)目前在windows的D盤,如下圖

可以看到,目錄本身就是一種有結(jié)構(gòu)的文件,記錄了目錄里的文件和目錄的信息,比如名稱和類型。而這些一條條的記錄就是一個(gè)個(gè)的“文件控制塊”(FCB)。

文件目錄的結(jié)構(gòu)通常是樹狀的,例如linu__里/是指根路徑,/home是根路徑下的二級(jí)目錄

需要注意的是,樹狀目錄不容易實(shí)現(xiàn)文件共享,所以在樹形目錄結(jié)構(gòu)的基礎(chǔ)上,增加了一些指向同一節(jié)點(diǎn)的有向邊(可以簡(jiǎn)單理解為引用關(guān)系,就跟js里的對(duì)象一樣)

也就是說需要為每個(gè)共享節(jié)點(diǎn)設(shè)置一個(gè)共享計(jì)數(shù)器,用于記錄此時(shí)有多少個(gè)地方在共享該結(jié)點(diǎn)。只有共享計(jì)數(shù)器減為0,才刪除該節(jié)點(diǎn)。

5.6 文件存儲(chǔ)空間管理

首先,我們了解一下磁盤分為目錄區(qū)和文件區(qū)。

接著,我們了解一下常見的兩種文件存儲(chǔ)空間的管理算法,如下圖,假如硬盤上空閑的數(shù)據(jù)塊是藍(lán)色,非空閑的數(shù)據(jù)塊是橙色。

對(duì)分配連續(xù)的存儲(chǔ)空間,可以采用空閑表法(只講這種較簡(jiǎn)單的方法)來(lái)分配和回收磁盤塊。對(duì)于分配,可以采用首次適應(yīng),最佳適應(yīng)等算法來(lái)決定要為文件分配哪個(gè)區(qū)間。(空閑表表示如下)

首次適應(yīng)是指當(dāng)要插入數(shù)據(jù)的時(shí)候,空閑表會(huì)依次檢查空閑表中的表項(xiàng),然后找到第一個(gè)滿足條件的空閑區(qū)間。

最佳適應(yīng)算法是指當(dāng)要插入數(shù)據(jù)的時(shí)候,空閑表會(huì)依次檢查空閑表中的表項(xiàng),然后找到滿足條件而且空閑塊最小的空閑區(qū)間。

如何回收磁盤塊呢,主要分為以下4中情況

回收區(qū)的前后沒有相鄰空閑區(qū)

回收區(qū)前后都是空閑區(qū)

回收區(qū)前面是空前去

回收區(qū)后面是空閑區(qū)

最重要的是要注意表項(xiàng)合并的問題。(比如說回收區(qū)前后都有空閑區(qū)就將其一起合并為一個(gè)空閑區(qū))

5.7 文件共享

文件共享分為兩種

注意,多個(gè)用戶共享同一個(gè)文件,意味著系統(tǒng)只有“一份”文件數(shù)據(jù)。并且只要某個(gè)用戶修改了該文件的數(shù)據(jù),其他用戶也可以看到文件的變化。

軟連接可以理解為windows里的快捷方式。

硬鏈接可以理解為js里的引用計(jì)數(shù),只有引用為0的時(shí)候,才會(huì)真正刪除這個(gè)文件。

5.8 文件保護(hù)

操作系統(tǒng)需要保護(hù)文件的安全,一般有如下3種方式:

口令保護(hù)。是指為文件設(shè)置一個(gè)“口令”(比如123),用戶請(qǐng)求訪問該文件時(shí)必須提供對(duì)應(yīng)的口令??诹钜话惴旁谖募?duì)應(yīng)的FCB或者索引結(jié)點(diǎn)上。

加密保護(hù)。使用某個(gè)"密碼"對(duì)文件進(jìn)行加密,在訪問文件時(shí)需要提供正確的“密碼”才能對(duì)文件進(jìn)行正確的解密。

訪問控制。在每個(gè)文件的FCB或者索引節(jié)點(diǎn)種增加一個(gè)訪問控制列表,該表中記錄了各個(gè)用戶可以對(duì)該文件執(zhí)行哪些操作。

6 I/O設(shè)備

什么是I/O設(shè)備

I/O就是輸入輸出(Input/Output)的意思,I/O設(shè)備就是可以將數(shù)據(jù)輸入到計(jì)算機(jī),或者可以接收計(jì)算機(jī)輸出數(shù)據(jù)的外部設(shè)備,屬于計(jì)算機(jī)中的硬件部件。

6.1 I/O設(shè)備分類--按使用特性

人機(jī)交互類設(shè)備,這類設(shè)備傳輸數(shù)據(jù)的速度慢

存儲(chǔ)設(shè)備,這類設(shè)備傳輸數(shù)據(jù)的速度較快

網(wǎng)絡(luò)通信設(shè)備,這類設(shè)備的傳輸速度介于人機(jī)交互設(shè)備和存儲(chǔ)設(shè)備之間

6.2 I/O控制器

CPU無(wú)法直接控制I/O設(shè)備的機(jī)械部件,因此I/O設(shè)備還要有一個(gè)電子部件作為CPU和I/O設(shè)備機(jī)械部件之間的“中介”,用于實(shí)現(xiàn)CPU對(duì)設(shè)備的控制。這個(gè)電子部件就是I/O控制器。

接收和識(shí)別CPU發(fā)出的指令是指,比如CPU發(fā)來(lái)讀取文件的命令,I/O控制器中會(huì)有相應(yīng)的控制寄存器來(lái)存放命令和參數(shù)

向cpu報(bào)告設(shè)備的狀態(tài)是指,I/O控制器會(huì)有相應(yīng)的狀態(tài)寄存器,用來(lái)記錄I/O設(shè)備是否空閑或者忙碌

數(shù)據(jù)交換是指I/O控制器會(huì)設(shè)置相應(yīng)的數(shù)據(jù)寄存器。輸出時(shí),數(shù)據(jù)寄存器用于暫存CPU發(fā)來(lái)的數(shù)據(jù),之后再由控制器傳送給設(shè)備。

地址識(shí)別是指,為了區(qū)分設(shè)備控制器中的各個(gè)寄存器中的各個(gè)寄存器,也需要給各個(gè)寄存器設(shè)置一個(gè)特性的“地址”。I/O控制器通過CPU提供的“地址”來(lái)判斷CPU要讀寫的是哪個(gè)寄存器

6.3 I/O控制方式

這里我們指講一下目前比較先進(jìn)的方式,通道控制方式。

通道可以理解為一種“弱雞版CPU”。通道可以識(shí)別并執(zhí)行一系列通道指令。

通道最大的優(yōu)點(diǎn)是極大的減少了CPU的干預(yù)頻率,I/O設(shè)備完成任務(wù),通道會(huì)向CPU發(fā)出中斷,不需要輪詢來(lái)問I/O設(shè)備是否完成CPU下達(dá)的任務(wù)。

操作系統(tǒng)基礎(chǔ)知識(shí)大全相關(guān)文章

操作系統(tǒng)基礎(chǔ)知識(shí)范文

操作系統(tǒng)基礎(chǔ)知識(shí)

Win10系統(tǒng)的基礎(chǔ)知識(shí)大全有哪些

電腦知識(shí)大全菜鳥必備

操作系統(tǒng)基本特征和功能

電腦操作常識(shí)入門必學(xué)知識(shí)

操作系統(tǒng)的概念、作用及原理

簡(jiǎn)述操作系統(tǒng)的發(fā)展與分類

電腦系統(tǒng)安全基礎(chǔ)知識(shí)大全有哪些

電腦入門基本知識(shí)大全

操作系統(tǒng)基礎(chǔ)知識(shí)大全相關(guān)文章:

操作系統(tǒng)基礎(chǔ)知識(shí)范文

操作系統(tǒng)基礎(chǔ)知識(shí)

Win10系統(tǒng)的基礎(chǔ)知識(shí)大全有哪些

電腦知識(shí)大全菜鳥必備

操作系統(tǒng)基本特征和功能

電腦操作常識(shí)入門必學(xué)知識(shí)

操作系統(tǒng)的概念、作用及原理

簡(jiǎn)述操作系統(tǒng)的發(fā)展與分類

電腦系統(tǒng)安全基礎(chǔ)知識(shí)大全有哪些

電腦入門基本知識(shí)大全

操作系統(tǒng)基礎(chǔ)知識(shí)大全

計(jì)算機(jī)系統(tǒng)中的一個(gè)系統(tǒng)軟件,它是這樣一些程序模塊的集合——它們管理和控制計(jì)算機(jī)系統(tǒng)中的軟硬件資源,合理地組織計(jì)算機(jī)的工作流程,以便有效地利用這些資源為用戶提供一個(gè)功能強(qiáng)大、使用方便和可擴(kuò)展的工作環(huán)境,從而在計(jì)算機(jī)與其用戶之間起到接口的作用。下面就讓小編帶你去看看操作系統(tǒng)基礎(chǔ)知識(shí)大全吧,希望能幫助到大家!操作系統(tǒng)基礎(chǔ)知識(shí)筆記一、操作系統(tǒng)相關(guān)概念計(jì)算機(jī)軟件:系統(tǒng)軟件和應(yīng)用軟件。計(jì)算機(jī)系統(tǒng)資源:硬件資源、軟件資源。硬件資源:中央處理器、存儲(chǔ)器、輸入、輸出等物理設(shè)備。軟件資源:以文件形式保存到存儲(chǔ)器上的
推薦度:
點(diǎn)擊下載文檔文檔為doc格式

精選文章

  • 操作系統(tǒng)基礎(chǔ)必備知識(shí)
    操作系統(tǒng)基礎(chǔ)必備知識(shí)

    今天給大家推薦兩份大佬們總結(jié)的 PDF,一份是計(jì)算機(jī)基礎(chǔ)知識(shí),一份是操作系統(tǒng),反正帥地看完之后,和面試官聊天,都有點(diǎn)飄了,廢話不多說,下面就讓

  • 操作系統(tǒng)必備基礎(chǔ)知識(shí)
    操作系統(tǒng)必備基礎(chǔ)知識(shí)

    今天給大家推薦兩份大佬們總結(jié)的 PDF,一份是計(jì)算機(jī)基礎(chǔ)知識(shí),一份是操作系統(tǒng),反正帥地看完之后,和面試官聊天,都有點(diǎn)飄了,廢話不多說,下面就讓

736496