程序员不得不学的操作系统知识(二)
進(jìn)程
進(jìn)程的組成
進(jìn)程是操作系統(tǒng)中分配資源的最小單位。進(jìn)程由 3 個(gè)部分組成,分別是程序代碼、數(shù)據(jù)集、棧和進(jìn)程控制塊(PCB)。
各自的作用如下:
程序代碼:描述了進(jìn)程需要完成的功能。
數(shù)據(jù)集、棧:程序在執(zhí)行時(shí)所需要的數(shù)據(jù)和工作區(qū)。
進(jìn)程控制塊:包含進(jìn)程的描述信息和控制信息,它是進(jìn)程存在的唯一標(biāo)識。
PCB:用來描述和控制進(jìn)程運(yùn)行的通用數(shù)據(jù)結(jié)構(gòu),是進(jìn)程能夠獨(dú)立運(yùn)行的基本單位。(常駐內(nèi)存,存在系統(tǒng)專門開放的PCB塊)
進(jìn)程的狀態(tài)
進(jìn)程通信
信號signal:通過向一個(gè)或多個(gè)進(jìn)程發(fā)送異步事件信號來實(shí)現(xiàn),如:SIGSTOP、SIGKILL等信號。
管道pipe:在兩個(gè)進(jìn)程之間,可以建立一個(gè)通道,一個(gè)進(jìn)程向通道寫入字節(jié)流,另一個(gè)進(jìn)程從管道讀取字節(jié)流。管道是同步的,當(dāng)進(jìn)程嘗試從空管道讀取數(shù)據(jù)時(shí),該進(jìn)程會被阻塞,直到有可用數(shù)據(jù)為止。如linux的 | 管線
共享內(nèi)存:通過共享內(nèi)存進(jìn)行進(jìn)程間通信,一個(gè)進(jìn)程所作的修改對另一個(gè)進(jìn)程可見。
先入先出隊(duì)列FIFO:也稱命名管道,具有支持文件和獨(dú)特 API ,命名管道在文件系統(tǒng)中作為設(shè)備的專用文件存在。而非命名管道在結(jié)束后緩沖區(qū)會被系統(tǒng)回收。
消息隊(duì)列:描述內(nèi)核尋址空間內(nèi)的內(nèi)部鏈接列表。可以按幾種不同的方式將消息按順序發(fā)送到隊(duì)列并從隊(duì)列中檢索消息。每個(gè)消息隊(duì)列由 IPC 標(biāo)識符唯一標(biāo)識。
套接字Socket:提供端到端的雙向通信,可有TCP、UDP的支持。
進(jìn)程同步
臨界資源:指的是一些雖作為共享資源卻又無法同時(shí)被多個(gè)進(jìn)程或線程共同訪問的共享資源。為了對臨界資源進(jìn)行有效的約束,就提出了進(jìn)程間同步的四個(gè)原則
-
空閑讓進(jìn):資源無占用,允許使用
-
忙則等待:資源被占用,請求進(jìn)程等待
-
有限等待:保證有限等待時(shí)間能夠使用資源,避免其它等待的進(jìn)程僵死
-
讓權(quán)等待:等待時(shí),進(jìn)程需讓出CPU,也就是進(jìn)程由執(zhí)行狀態(tài)變?yōu)樽枞麪顟B(tài),這也是保證CPU可以高效使用的前提
死鎖
死鎖定義:如果一組進(jìn)程中的每個(gè)進(jìn)程都在等待一個(gè)事件,而這個(gè)事件只能由該組中的另一個(gè)進(jìn)程觸發(fā),這種情況會導(dǎo)致死鎖。
死鎖的條件:
處理死鎖策略:
- 破壞互斥條件
- 破壞保持等待條件
- 破壞不可搶占條件
- 破壞循環(huán)等待條件
兩階段加鎖
一種解決方式是使用 兩階段提交(two-phase locking)。顧名思義分為兩個(gè)階段,一階段是進(jìn)程嘗試一次鎖定它需要的所有記錄。如果成功后,才會開始第二階段,第二階段是執(zhí)行更新并釋放鎖。第一階段并不做真正有意義的工作。
如果在第一階段某個(gè)進(jìn)程所需要的記錄已經(jīng)被加鎖,那么該進(jìn)程會釋放所有鎖定的記錄并重新開始第一階段。從某種意義上來說,這種方法類似于預(yù)先請求所有必需的資源或者是在進(jìn)行一些不可逆的操作之前請求所有的資源。
通信死鎖
進(jìn)程 A 給進(jìn)程 B 發(fā)了一條消息,然后進(jìn)程 A 阻塞直到進(jìn)程 B 返回響應(yīng)。假設(shè)請求消息丟失了,那么進(jìn)程 A 在一直等著回復(fù),進(jìn)程 B 也會阻塞等待請求消息到來,這時(shí)候就產(chǎn)生死鎖。
解決方法:超時(shí)重傳
**進(jìn)程間同步的方法:**消息隊(duì)列、共享存儲、信號量。會在后邊的文章中詳細(xì)介紹這些進(jìn)程間同步的方法
fork進(jìn)程
-
fork系統(tǒng)調(diào)用是用于創(chuàng)建進(jìn)程的
-
對于虛擬空間地址來說,子進(jìn)程會拷貝父進(jìn)程的虛擬地址空間。所以,fork后子進(jìn)程的用戶區(qū)與父進(jìn)程的用戶區(qū)相同,也會拷貝內(nèi)核區(qū)內(nèi)容,僅僅是進(jìn)程的 pid不同。
-
在父進(jìn)程中返回子進(jìn)程的ID,在子進(jìn)程中返回0。所以可以通過fork 的返回值來區(qū)分父進(jìn)程與子進(jìn)程
-
fork系統(tǒng)調(diào)用無參數(shù)
運(yùn)用了讀時(shí)共享、寫時(shí)拷貝的原則,fork后,父子進(jìn)程共享父進(jìn)程的地址空間(只讀),在父進(jìn)程或者子進(jìn)程進(jìn)行寫指令時(shí),子進(jìn)程才會復(fù)制一份地址空間,從而使得虛擬地址空間獨(dú)立,在自己的地址空間進(jìn)行寫操作。也就是說,資源的復(fù)制是在需要寫入時(shí)才會進(jìn)行,在此之前,只會以只讀方式進(jìn)行共享。
進(jìn)程類型
前臺進(jìn)程:具有終端,可以和用戶進(jìn)行交互的進(jìn)程
后臺進(jìn)程:不與用戶進(jìn)行交互,優(yōu)先級比前臺進(jìn)程低
守護(hù)進(jìn)程:特殊的后臺進(jìn)程
孤兒進(jìn)程:父進(jìn)程退出后,子進(jìn)程即成為孤兒進(jìn)程,將由**init進(jìn)程(pid為1)**收養(yǎng)
僵尸進(jìn)程:子進(jìn)程的進(jìn)程描述符在子進(jìn)程退出后不會釋放,只有當(dāng)父進(jìn)程調(diào)用**wait()、waitpid()**獲取子進(jìn)程信息才釋放
線程
線程是操作系統(tǒng)進(jìn)行運(yùn)行調(diào)度的最小單位,線程除了擁有自己的棧、程序計(jì)數(shù)器等資源外,共享進(jìn)程的資源。
通信,對于進(jìn)程來說是進(jìn)程間的通信(IPC),而對于線程,它是通過讀寫同一個(gè)進(jìn)程的數(shù)據(jù)進(jìn)行通信。
線程同步
互斥量
- 本質(zhì)上是資源排他性使用,效果相當(dāng)于原子性。擁有兩種狀態(tài):加鎖和解鎖。(會帶來相關(guān)損耗,阻塞鎖)
- 自旋鎖:等待獲取資源的時(shí)候CPU不會釋放,優(yōu)點(diǎn):如果線程占用鎖時(shí)間不長,就能避免上下文切換代價(jià);缺點(diǎn):耗費(fèi)CPU時(shí)間
- 讀寫鎖:讀不進(jìn)行加鎖,寫的時(shí)候進(jìn)行加鎖,對于多讀少寫的情況,性能能有很好的提升
信號量:表示同時(shí)允許訪問資源的最大線程數(shù)量,它是一個(gè)全局變量。(Java的semaphore)
條件變量:利用線程間共享的全局變量進(jìn)行同步的一種機(jī)制,主要包括兩個(gè)動(dòng)作:一個(gè)線程等待某個(gè)條件為真,而將自己掛起;另一個(gè)線程設(shè)置條件為真,并通知等待的線程繼續(xù)。條件變量與互斥量一起使用時(shí),允許線程以無競爭的方式等待特定的條件發(fā)生。
- 基本動(dòng)作:wait、signal、notify
調(diào)度算法
-
調(diào)度算法的目標(biāo):
-
先來先服務(wù):按照FIFO的原則,將作業(yè)加入到就緒隊(duì)列中,按照順序調(diào)度作業(yè)。
-
最短作業(yè)優(yōu)先:按照線程作業(yè)的CPU時(shí)間片,優(yōu)先調(diào)度所需最短CPU時(shí)間片的作業(yè)。
-
最短剩余時(shí)間:最短作業(yè)優(yōu)先的搶占式版本,總是調(diào)度剩余運(yùn)行時(shí)間最短的作業(yè)。
-
輪詢調(diào)度:每個(gè)作業(yè)都會被分配一個(gè)CPU時(shí)間片,在這個(gè)時(shí)間片內(nèi)允許進(jìn)程運(yùn)行。如果時(shí)間片結(jié)束時(shí)進(jìn)程還在運(yùn)行的話,則搶占一個(gè) CPU 并將其分配給另一個(gè)進(jìn)程。
-
優(yōu)先級調(diào)度:按照作業(yè)優(yōu)先級進(jìn)行調(diào)度。
-
多級反饋隊(duì)列:設(shè)置多級隊(duì)列,設(shè)置不同優(yōu)先級,并且分配不同的時(shí)間片。
POSIX線程
即線程標(biāo)準(zhǔn)。
| pthread_create | 創(chuàng)建一個(gè)新線程 |
| pthread_exit | 結(jié)束調(diào)用的線程 |
| pthread_join | 等待一個(gè)特定的線程退出 |
| pthread_yield | 釋放CPU來運(yùn)行另外一個(gè)線程 |
| pthread_attr_init | 創(chuàng)建并初始化一個(gè)線程的屬性結(jié)構(gòu) |
| pthread_attr_destory | 刪除一個(gè)線程的屬性結(jié)構(gòu) |
線程實(shí)現(xiàn)
在用戶空間實(shí)現(xiàn)線程:
內(nèi)核并不知道線程的存在,以進(jìn)程為單位分配CPU時(shí)間片,每個(gè)進(jìn)程要有專用的線程表。
優(yōu)點(diǎn):允許每個(gè)進(jìn)程有自己定制的調(diào)度算法、調(diào)度效率比內(nèi)核調(diào)用高(無需陷入內(nèi)核,即上下文切換,無需刷新內(nèi)存高速緩存)
缺點(diǎn):會因?yàn)樽枞{(diào)用/缺頁中斷阻塞整個(gè)進(jìn)程直到完成
在內(nèi)核空間實(shí)現(xiàn)線程
內(nèi)核中會有用來記錄系統(tǒng)中所有線程的線程表,當(dāng)進(jìn)行系統(tǒng)調(diào)度的時(shí)候,會通過對線程表的更新進(jìn)行調(diào)度。線程表擁有每個(gè)線程的寄存器、狀態(tài)和其他信息。
優(yōu)點(diǎn):不會因某個(gè)線程阻塞而導(dǎo)致進(jìn)程阻塞
缺點(diǎn):系統(tǒng)調(diào)用代價(jià)大,上下文切換開銷大,以及需要切換系統(tǒng)狀態(tài)
混合實(shí)現(xiàn):將用戶級線程與某些或者全部內(nèi)核線程多路復(fù)用起來。編程人員可以自由控制用戶線程和內(nèi)核線程的數(shù)量,具有很大的靈活度。內(nèi)核只識別內(nèi)核級線程,并對其進(jìn)行調(diào)度。其中一些內(nèi)核級線程會被多個(gè)用戶級線程多路復(fù)用。
了解更多文章,🙋?♂?關(guān)注公眾號:學(xué)編程的文若
總結(jié)
以上是生活随笔為你收集整理的程序员不得不学的操作系统知识(二)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: oracle trim没用,Oracle
- 下一篇: ARM neon详解