1. 程式人生 > >進程線程相關

進程線程相關

多道程序設計 linux下 先來 exit 所有 for 短作業優先 一個個 讓其

1.1什麽是進程?

狹義定義:進程就是一段程序的執行過程。

廣義定義:進程是一個具有一定獨立功能的程序,關於某個數據集合的一次運行活動。它是操作系統動態執行 的基本單元,在傳統的操作系統中,進程既是基本的分配單元,也是基本的執行單元。

1.1.1 進城和程序的區別

程序:計算機指令的集合,它以文件的形式存儲在磁盤上。程序是靜態實體(passive Entity),在多道程序系統中,它是不能獨立運行的,更不能與其他程序並發執行。

使用系統資源情況:不使用(程序不能申請系統資源,不能被系統調度,也不能作為獨立運行的單位,它不占用系統的運行資源)。

進程:進程是程序實體(包括:程序段、相關的數據段、進程控制塊PCB)的運行過程,是一個程序在其自身的地址空間中的一次執行活動。是系統進行資源分配和調度的一個獨立單位。

(進程是資源申請、調度和獨立運行的單位,因此,它使用系統中的運行資源)

1.2操作系統引入進程的概念的原因

? 從理論角度看,是對正在運行的程序過程的抽象;? 從實現角度看,是一種數據結構,目的在於清晰地刻畫動態系統的內在規律,有效管理和調度進入計算機系統主存儲器運行的程序。

1.3進程的特征

動態性:進程的實質是程序在多道程序系統中的一次執行過程,進程是動態產生,動態消亡的。

並發性:任何進程都可以同其他進程一起並發執行

獨立性:進程是一個能獨立運行的基本單位,同時也是系統分配資源和調度的獨立單位;

異步性:由於進程間的相互制約,使進程具有執行的間斷性,即進程按各自獨立的、不可預知的速度向前推進

結構特征:進程由程序、數據和進程控制塊三部分組成;

多個不同的進程可以包含相同的程序:一個程序在不同的數據集裏就構成不同的進程,能得到不同的結果; 但是執行過程中,程序不能發生改變。

1.4進程狀態

1.4.1 進程三(五)種基本狀態

創建狀態:進程在創建時需要申請一個空白PCB,向其中填寫控制和管理進程的信息,完成資源分配,把此時進程所處狀態稱為創建狀態

就緒狀態:進程已獲得除處理器外的所需資源,等待分配處理器資源;只要分配了處理器進程就可執行.就緒進程可以按多個優先級來劃分隊列。例如,當一個進程由於時間片用完而進入就緒狀態時,排入低優先級隊列;當進程由I/O操作完成而進入就緒狀態時,排入高優先級隊列。(三種之一)運行狀態

:進程占用處理器資源;處於此狀態的進程的數目小於等於處理器的數目。在沒有其他進程可以執行時(如所有進程都在阻塞狀態),通常會自動執行系統的空閑進程。(三種之一)阻塞狀態:由於進程等待某種條件(如I/O操作或進程同步),在條件滿足之前無法繼續執行。該事件發生前即使把處理機分配給該進程,也無法運行。(三種之一)

終止狀態:進程結束,或出現錯誤,或被系統終止,進入終止狀態。無法再執行

1.4.3 linux下進程狀態 6種

在linux下,通過ps命令我們能夠查看到系統中存在的進程,以及它們的狀態:

R (TASK_RUNNING)

可執行狀態.只有在該狀態的進程才可能在CPU上運行。而同一時刻可能有多個進程處於可執行狀態,這些進程的task_struct結構(進程控制塊)被放入對應CPU的可執行隊列中(一個進程最多只能出現在一個CPU的可執行隊列中)。進程調度器的任務就是從各個CPU的可執行隊列中分別選擇一個進程在該CPU上運行。只要可執行隊列不為空,其對應的CPU就不能偷懶,就要執行其中某個進程。一般稱此時的CPU“忙碌”。對應的,CPU“空閑”就是指其對應的可執行隊列為空,以致於CPU無事可做。很多操作系統教科書將正在CPU上執行的進程定義為RUNNING狀態、而將可執行但是尚未被調度執行的進程定義為READY狀態,這兩種狀態在linux下統一為 TASK_RUNNING狀態。

S (TASK_INTERRUPTIBLE)

可中斷的睡眠狀態。處於這個狀態的進程因為等待某某事件的發生(比如等待socket連接、等待信號量),而被掛起。這些進程的task_struct結構被放入對應事件的等待隊列中。當這些事件發生時(由外部中斷觸發、或由其他進程觸發),對應的等待隊列中的一個或多個進程將被喚醒。通過ps命令我們會看到,一般情況下,進程列表中的絕大多數進程都處於TASK_INTERRUPTIBLE狀態(除非機器的負載很高)。畢竟CPU就這麽一兩個,進程動輒幾十上百個,如果不是絕大多數進程都在睡眠,CPU又怎麽響應得過來。

D (TASK_UNINTERRUPTIBLE)

不可中斷的睡眠狀態。顯式被wakeup,否則一直是D狀態。“UN” 只針對信號,調用wakeup()是可以的與TASK_INTERRUPTIBLE狀態類似,進程處於睡眠狀態,但是此刻進程是不可中斷的。不可中斷,指的並不是CPU不響應外部硬件的中斷,而是指進程不響應異步信號絕大多數情況下,進程處在睡眠狀態時,總是應該能夠響應異步信號的。kill -9殺不死一個TASK_UNINTERRUPTIBLE進程了!而TASK_UNINTERRUPTIBLE狀態存在的意義就在於,內核的某些處理流程是不能被打斷的。如果響應異步信號,程序的執行流程中就會被插入一段用於處理異步信號的流程(這個插入的流程可能只存在於內核態,也可能延伸到用戶態),於是原有的流程就被中斷了(。在進程對某些硬件進行操作時(比如進程調用read系統調用對某個設備文件進行讀操作,而read系統調用最終執行到對應設備驅動的代碼,並與對應的物理設備進行交互),可能需要使用TASK_UNINTERRUPTIBLE狀態對進程進行保護,以避免進程與設備交互的過程被打斷,造成設備陷入不可控的狀態。(比如read系統調用觸發了一次磁盤到用戶空間的內存的DMA,如果DMA進行過程中,進程由於響應信號而退出了,那麽DMA正在訪問的內存可能就要被釋放了。)這種情況下的TASK_UNINTERRUPTIBLE狀態總是非常短暫的,通過ps命令基本上不可能捕捉到linux系統中也存在容易捕捉的TASK_UNINTERRUPTIBLE狀態。執行vfork系統調用後,父進程將進入TASK_UNINTERRUPTIBLE狀態,直到子進程調用exit或exec.不管kill還是kill -9,這個TASK_UNINTERRUPTIBLE狀態的父進程依然屹立不倒。

T (TASK_STOPPED or TASK_TRACED)

暫停狀態或跟蹤狀態。向進程發送一個SIGSTOP信號,它就會因響應該信號而進入TASK_STOPPED狀態(除非該進程本身處於TASK_UNINTERRUPTIBLE狀態而不響應信號)。(SIGSTOP與SIGKILL信號一樣,是非常強制的。不允許用戶進程通過signal系列的系統調用重新設置對應的信號處理函數。)向進程發送一個SIGCONT信號,可以讓其從TASK_STOPPED狀態恢復到TASK_RUNNING狀態。當進程正在被跟蹤時,它處於TASK_TRACED這個特殊的狀態。“正在被跟蹤”指的是進程暫停下來,等待跟蹤它的進程對它進行操作。比如在gdb中對被跟蹤的進程下一個斷點,進程在斷點處停下來的時候就處於TASK_TRACED狀態。而在其他時候,被跟蹤的進程還是處於前面提到的那些狀態。

對於進程本身來說,TASK_STOPPED和TASK_TRACED狀態很類似,都是表示進程暫停下來。而TASK_TRACED狀態相當於在TASK_STOPPED之上多了一層保護,處於TASK_TRACED狀態的進程不能響應SIGCONT信號而被喚醒。只能等到調試進程通過ptrace系統調用執行PTRACE_CONT、PTRACE_DETACH等操作(通過ptrace系統調用的參數指定操作),或調試進程退出,被調試的進程才能恢復TASK_RUNNING狀態。

Z (TASK_DEAD - EXIT_ZOMBIE)

退出狀態,進程成為僵屍進程。進程在退出的過程中,處於TASK_DEAD狀態。在這個退出過程中,進程占有的所有資源將被回收,除了task_struct結構(以及少數資源)以外。於是進程就只剩下task_struct這麽個空殼,故稱為僵屍。之所以保留task_struct,是因為task_struct裏面保存了進程的退出碼、以及一些統計信息。而其父進程很可能會關心這些信息。比如在shell中,$?變量就保存了最後一個退出的前臺進程的退出碼,而這個退出碼往往被作為if語句的判斷條件。父進程可以通過wait系列的系統調用(如wait4、waitid)來等待某個或某些子進程的退出,並獲取它的退出信息。然後wait系列的系統調用會順便將子進程的屍體(task_struct)也釋放掉。子進程在退出的過程中,內核會給其父進程發送一個信號,通知父進程來“收屍”。這個信號默認是SIGCHLD,但是在通過clone系統調用創建子進程時,可以設置這個信號。當進程退出的時候,會將它的所有子進程都托管給別的進程(使之成為別的進程的子進程)。可能是退出進程所在進程組的下一個進程(如果存在的話),或者是1號進程。所以每個進程、每時每刻都有父進程存在。除非它是1號進程。1號進程,pid為1的進程,又稱init進程。linux系統啟動後,第一個被創建的用戶態進程就是init進程。它有兩項使命:1、執行系統初始化腳本,創建一系列的進程(它們都是init進程的子孫);2、在一個死循環中等待其子進程的退出事件,並調用waitid系統調用來完成“收屍”工作;init進程不會被暫停、也不會被殺死(這是由內核來保證的)。它在等待子進程退出的過程中處於TASK_INTERRUPTIBLE狀態,“收屍”過程中則處於TASK_RUNNING狀態。

X (TASK_DEAD - EXIT_DEAD)

退出狀態,進程即將被銷毀。而進程在退出過程中也可能不會保留它的task_struct。比如這個進程是多線程程序中被detach過的線程,或者父進程通過設置SIGCHLD信號的handler為SIG_IGN,顯式的忽略了SIGCHLD信號。(這是posix的規定,盡管子進程的退出信號可以被設置為SIGCHLD以外的其他信號。)此時,進程將被置於EXIT_DEAD退出狀態,這意味著接下來的代碼立即就會將該進程徹底釋放。所以EXIT_DEAD狀態是非常短暫的,幾乎不可能通過ps命令捕捉到

技術分享圖片

1.5進程和程序的區別

  1. 程序是指令和數據的有序集合,是一個靜態的概念。而進程是程序在處理機上的一次執行過程,它是一個動態的概念。

  2. 程序可以作為一種軟件資料長期存在,而進程是有一定生命期的。程序是永久的,進程是暫時的。

  3. 進程是由進程控制塊、程序段、數據段三部分組成;

  4. 進程具有創建其他進程的功能,而程序沒有。

  5. 同一程序同時運行於若幹個數據集合上,它將屬於若幹個不同的進程,也就是說同一程序可以對應多個進程。

  6. 在傳統的操作系統中,程序並不能獨立運行,作為資源分配和獨立運行的基本單元都是進程。

1.6 進程控制

進程控制是進程管理中最基本的功能。它用於創建一個新進程,終止一個已完成的進程,或者去終止一個因出現某事件而使其無法運行下去的進程,還可負責進程運行中的狀態轉換。

1.6.1 引起創建進程的事件

  1. 用戶登錄

    在分時系統中,用戶在終端鍵入登錄命令後,如果是合法用戶,系統將為該終端建立一個進程,並把它插入到就緒隊列中。

  2. 作業調度

    在批處理系統中,當作業調度程序按照一定的算法調度到某作業時,便將該作業裝入到內存,為它分配必要的資源,並立即為它創建進程,再插入到就緒隊列中。

  3. 提供服務

    當運行中的用戶程序提出某種請求後,系統將專門創建一個進程來提供用戶所需要的服務,例如,用戶程序要求進行文件打印,操作系統將為它創建一個打印進程,這樣,不僅可以使打印進程與該用戶進程並發執行,而且還便於計算出為完成打印任務所花費的時間。

  4. 應用請求

    用戶程序自己創建進程。進程派生。基於應用進程的需求,由它創建一個新的進程,以便使新進程以並發的運行方式完成特定任務

1.6.2 進程的創建過程

一旦操作系統發現了要求創建新進程的事件後,便調用進程創建原語creat()按下述步驟創建一個新進程。

  1. 申請空白PCB。為新進程申請獲得唯一的數字標識符,並從PCB集合中索取一個空白PCB。

  2. 為新進程分配資源。

  3. 初始化進程控制塊。PCB的初始化包括:

    1. 初始化標識信息,將系統分配的標識符和父進程標識符,填入新的PCB中。

    2. 初始化處理機狀態信息,使程序計數器指向程序的入口地址,使棧指針指向棧頂。

    3. 初始化處理機控制信息,將進程的狀態設置為就緒狀態或靜止就緒狀態,對於優先級,通常是將它設置為最低優先級,除非用戶以顯式的方式提出高優先級要求。

  4. 將新進程插入就緒隊列,如果進程就緒隊列能夠接納新進程,便將新進程插入到就緒隊列中。

1.6.3 進程終止

引起進程終止的事件

  1. 正常結束

  2. 異常結束

    超過時限,無可用內存,越界,保護錯誤,算數錯誤,IO失敗,無效指令,特權指令,數據錯誤

  3. 外界幹預

    父進程終止,父進程請求

    ?

1.6.4 進程的終止過程

如果系統發生了上述要求終止進程的某事件後,OS便調用進程終止原語,按下述過程去終止指定的進程。

  1. 根據被終止進程的標識符,從PCB集合中檢索出該進程的PCB,從中讀出該進程狀態。

  2. 若被終止進程正處於執行狀態,應立即終止該進程的執行,並置調度標誌為真。用於指示該進程被終止後應重新進行調度。

  3. 若該進程還有子孫進程,還應將其所有子孫進程予以終止,以防他們成為不可控的進程。

  4. 將被終止的進程所擁有的全部資源,或者歸還給其父進程,或者歸還給系統。

  5. 將被終止進程(它的PCB)從所在隊列(或鏈表)中移出,等待其它程序來搜集信息。

1.6.5 進入阻塞態原因

引起進程阻塞和喚醒的事件

  1. 請求系統服務

  2. 啟動某種操作

  3. 新數據尚未到達

  4. 無新工作可做

1.6.6 進程阻塞過程和喚醒

阻塞:

正在執行的進程,當發現上述某事件後,由於無法繼續執行,於是進程便通過調用阻塞原語block把自阻塞。可見,進程的阻塞是進程自身的一種主動行為。進入block過程後,由於此時該進程還處於執行狀態,所以應先立即停止執行,把進程控制塊中的現行狀態由執行改為阻塞,並將PCB插入阻塞隊列。如果系統中設置了因不同事件而阻塞的多個阻塞隊列,則應將本進程插入到具有相同事件的阻塞(等待)隊列。最後,轉調度程序進行重新調度,將處理機分配給另一就緒進程,並進行切換,亦即,保留被阻塞進程的處理機狀態(在PCB中),再按新進程的PCB中的處理機狀態設置CPU環境。

喚醒:

當被阻塞的進程所期待的事件出現時,如I/O完成或者其所期待的數據已經到達,則由有關進程(比如,用完並釋放了該I/O設備的進程)調用喚醒原語wakeup(),將等待該事件的進程喚醒。喚醒原語執行的過程是:首先把被阻塞的進程從等待該事件的阻塞隊列中移出,將其PCB中的現行狀態由阻塞改為就緒,然後再將該PCB插入到就緒隊列中。

1.7 進程間通信方式

高級通信機制:

  1. 共享存儲器系統(存儲器中劃分的共享存儲區)

    實際操作中對應的是“剪貼板”(剪貼板實際上是系統維護管理的一塊內存區域)的通信方式。

  2. 消息傳遞系統(進程間的數據交換以消息(message)為單位)

  當今最流行的微內核操作系統中,微內核與服務器之間的通信,都采用 了消息傳遞機制

  1. 管道通信系統(連接讀寫進程實現他們之間通信的共享文件(pipe文件,類似先進先出的隊列,由一個進程寫,另一進程讀))

  2. socket

1.7.1 消息隊列

解耦寫入和處理的能力差異,避免寫的太多處理不過來,一個個排隊等著。將突發大量請求轉換為後端能承受的隊列請求。生產消費模式.

消息隊列好處

  1. 異步化

  2. 解耦

  3. 消除峰值

消息隊列使用的四種場景介紹

1.7.2 管道

普通管道PIPE 、流管道(s_pipe)、命名管道(name_pipe

  • 管道是一種半雙工的通信方式,數據只能單項流動,並且只能在具有親緣關系的進程間流動,進程的親緣關系通常是父子進程

  • 命名管道也是半雙工的通信方式,它允許無親緣關系的進程間進行通信

管道: 優點是所有的UNIX實現都支持, 並且在最後一個訪問管道的進程終止後,管道就被完全刪除;缺陷是管道只允許單向傳輸或者用於父子進程之間.

系統IPC: 優點是功能強大,能在毫不相關進程之間進行通訊; 缺陷是關鍵字KEY_T使用了內核標識,占用了內核資源,而且只能被顯式刪除,而且不能使用SOCKET的一些機制,例如select,epoll等.

1.8 進程和作業

作業是用戶需要計算機完成某項任務時要求計算機所做工作的集合。而進程則是已提交完畢程序的執行過程的描述,是資源分配的基本單位。主要區別:

  1. 作業是用戶向計算機提交任務的任務實體。

  2. 一個作業可由多個進程組成。

  3. 作業的概念主要用於批處理系統中。

1.9 進程上下文

1.9.1 什麽是進程上下文

進程上下文是一個抽象的概念,它包含了每個進程執行過的、執行時的以及待執行的指令和數據,在指令寄存器、堆棧(存放個調用子程序的返回點和參數等),狀態字寄存器等中的內容。

  • 上文:已執行過的進程指令和數據在相關寄存器與堆棧中的內容。

  • 正文:正在執行的指令和數據在相關寄存器與堆棧中的內容。

  • 下文:待執行的指令和數據在相關寄存器與堆棧中的內容。

1.9.2 進程上下文切換

進程上下文切換發生在不同的進程之間而不是同一個進程內。包含3個部分,

  1. 保存被切換進程的正文部分(或當前狀態)至有關存儲區。

  2. 操作系統進程中有關調度和資源分配程序執行,並選取新的進程。

  3. 將被選中進程的原來被保存的正文部分從有關存儲區中選出,並送至有關寄存器或堆棧中,激活被選中進程執行。

1.10 進程調度算法

1.10.1 先來先服務 (FCFS,first come first served)

在所有調度算法中,最簡單的是非搶占式的FCFS算法。 算法原理:進程按照它們請求CPU的順序使用CPU.就像你買東西去排隊,誰第一個排,誰就先被執行,在它執行的過程中,不會中斷它。當其他人也想進入內存被執行,就要排隊等著,如果在執行過程中出現一些事,他現在不想排隊了,下一個排隊的就補上。此時如果他又想排隊了,只能站到隊尾去。 算法優點:易於理解且實現簡單,只需要一個隊列(FIFO),且相當公平 算法缺點:比較有利於長進程,而不利於短進程,有利於CPU 繁忙的進程,而不利於I/O 繁忙的進程

1.10.2 最短作業優先(SJF, Shortest Job First)

短作業優先(SJF, Shortest Job First)又稱為“短進程優先”SPN(Shortest Process Next);這是對FCFS算法的改進,其目標是減少平均周轉時間。 算法原理:對預計執行時間短的進程優先分派處理機。通常後來的短進程不搶先正在執行的進程。 算法優點:相比FCFS 算法,該算法可改善平均周轉時間和平均帶權周轉時間,縮短進程的等待時間,提高系統的吞吐量。 算法缺點:對長進程非常不利,可能長時間得不到執行,且未能依據進程的緊迫程度來劃分執行的優先級,以及難以準確估計進程的執行時間,從而影響調度性能。

1.10.3 最高響應比優先法(HRRN,Highest Response Ratio Next)

最高響應比優先法(HRRN,Highest Response Ratio Next)是對FCFS方式和SJF方式的一種綜合平衡。FCFS方式只考慮每個作業的等待時間而未考慮執行時間的長短,而SJF方式只考慮執行時間而未考慮等待時間的長短。因此,這兩種調度算法在某些極端情況下會帶來某些不便。HRN調度策略同時考慮每個作業的等待時間長短和估計需要的執行時間長短,從中選出響應比最高的作業投入執行。這樣,即使是長作業,隨著它等待時間的增加,W / T也就隨著增加,也就有機會獲得調度執行。這種算法是介於FCFS和SJF之間的一種折中算法。 算法原理響應比R定義如下: R =(W+T)/T = 1+W/T 其中T為該作業估計需要的執行時間,W為作業在後備狀態隊列中的等待時間。每當要進行作業調度時,系統計算每個作業的響應比,選擇其中R最大者投入執行。 算法優點:由於長作業也有機會投入運行,在同一時間內處理的作業數顯然要少於SJF法,從而采用HRRN方式時其吞吐量將小於采用SJF 法時的吞吐量。 算法缺點:由於每次調度前要計算響應比,系統開銷也要相應增加。

1.10.4 時間片輪轉算法(RR,Round-Robin)

該算法采用剝奪策略。時間片輪轉調度是一種最古老,最簡單,最公平且使用最廣的算法,又稱RR調度。每個進程被分配一個時間段,稱作它的時間片,即該進程允許運行的時間。 算法原理:讓就緒進程以FCFS 的方式按時間片輪流使用CPU 的調度方式,即將系統中所有的就緒進程按照FCFS 原則,排成一個隊列,每次調度時將CPU 分派給隊首進程,讓其執行一個時間片,時間片的長度從幾個ms 到幾百ms。在一個時間片結束時,發生時鐘中斷,調度程序據此暫停當前進程的執行,將其送到就緒隊列的末尾,並通過上下文切換執行當前的隊首進程,進程可以未使用完一個時間片,就出讓CPU(如阻塞)。 算法優點:時間片輪轉調度算法的特點是簡單易行、平均響應時間短。 算法缺點:不利於處理緊急作業。在時間片輪轉算法中,時間片的大小對系統性能的影響很大,因此時間片的大小應選擇恰當 怎樣確定時間片的大小:

時間片大小的確定 1.系統對響應時間的要求 2.就緒隊列中進程的數目 3.系統的處理能力

1.10.5 多級反饋隊列(Multilevel Feedback Queue)

多級反饋隊列調度算法是一種CPU處理機調度算法,UNIX操作系統采取的便是這種調度算法。 多級反饋隊列調度算法描述:   1、進程在進入待調度的隊列等待時,首先進入優先級最高的Q1等待。   2、首先調度優先級高的隊列中的進程。若高優先級中隊列中已沒有調度的進程,則調度次優先級隊列中的進程。例如:Q1,Q2,Q3三個隊列,只有在Q1中沒有進程等待時才去調度Q2,同理,只有Q1,Q2都為空時才會去調度Q3。   3、對於同一個隊列中的各個進程,按照時間片輪轉法調度。比如Q1隊列的時間片為N,那麽Q1中的作業在經歷了N個時間片後若還沒有完成,則進入Q2隊列等待,若Q2的時間片用完後作業還不能完成,一直進入下一級隊列,直至完成。   4、在低優先級的隊列中的進程在運行時,又有新到達的作業,那麽在運行完這個時間片後,CPU馬上分配給新到達的作業(搶占式)。   在多級反饋隊列調度算法中,如果規定第一個隊列的時間片略大於多數人機交互所需之處理時間時,便能夠較好的滿足各種類型用戶的需要。

1.11 linux 線程調度策略

linux內核的三種調度方法:

  1. SCHED_OTHER 分時調度策略,

  2. SCHED_FIFO實時調度策略,先到先服務

  3. SCHED_RR實時調度策略,時間片輪轉

實時進程將得到優先調用,實時進程根據實時優先級決定調度權值,分時進程則通過nice和counter值決定權值,nice越小,counter越大,被調度的概率越大,也就是曾經使用了cpu最少的進程將會得到優先調度。

SHCED_RR和SCHED_FIFO:

當采用SHCED_RR策略的進程的時間片用完,系統將重新分配時間片,並置於就緒隊列尾。放在隊列尾保證了所有具有相同優先級的RR任務的調度公平。

SCHED_FIFO一旦占用cpu則一直運行。一直運行直到有更高優先級任務到達或自己放棄。如果有相同優先級的實時進程(根據優先級計算的調度權值是一樣的)已經準備好,FIFO時必須等待該進程主動放棄後才可以運行這個優先級相同的任務。而RR可以讓每個任務都執行一段時間。

相同點:RR和FIFO都只用於實時任務。 創建時優先級大於0(1-99)。 按照可搶占優先級調度算法進行。就緒態的實時任務立即搶占非實時任務。

.12 進程其他東西

1.12.1 進程控制塊PCB的作用

  1. 作為獨立運行基本單位的標誌

  2. 能實現間斷性運行方式

  3. 提供進程通信管理所需要的信息

  4. 提供進程調度所需要的信息

2. 線程

2.1 什麽是線程

進程是資源管理的最小單位,線程是程序執行的最小單位。在操作系統設計上,從進程演化出線程,最主要的目的就是更好的支持SMP以及減小(進程/線程)上下文切換開銷。

核心級線程和用戶級線程兩種線程模型,分類的標準主要是線程的調度者在核內還是在核外。前者更利於並發使用多處理器的資源,而後者則更多考慮的是上下文切換開銷

線程是進程的實體,是CPU調度和分派的基本單位,它是比進程更小的能獨立運行的基本單位。

有了進程之後還要有線程的原因是

2.1.1 線程和進程間的區別

  • 進程是具有一定功能的程序關於某個數據集合上的一次運行活動,進程是系統進行資源調度和分配的一個獨立單位。

  • 線程是進程的實體,是CPU調度和分派的基本單位,它是比進程更小的能獨立運行的基本單位。

  • 一個進程可以有多個線程,多個線程也可以並發執行

2.1.2 線程優勢

  1. 進程的換入換出、數據交換開銷太大,線程遠遠小於進程的開銷。首先,進程之間通信需要獲取共享資源鎖,而同一進程內的線程完全享有進程獲得的資源,也因此線程的編寫要特別註意同步問題。

  2. 進程的創建成本很高,涉及空間分配、初始化棧、初始化數據等,而線程的創建成本低很多可以包括操作系統創建和進程調用庫自己創建。於是有了兩種不同類型的線程——用戶級別和內核級別。

  3. 多道程序設計中線程的換入換出比進程來得快多了,因為不需要保存進程控制塊等一大堆數據(當然要保存線程控制塊,但是成本很低)

  4. 銷毀成本低。

2.1.3 多線程應用場景

  1. 用戶界面和後臺數據運行:用戶界面渲染和後臺數據運算使用不同的線程,沒有喜歡後臺一運算界面就卡死的程序吧。

  2. 異步計算:不如實時備份,一個線程定時觸發就可以了,沒必要進程定時的檢查是不是該備份了。

  3. 速度敏感的計算:可以並發計算的、多核處理的,在計算和組織上線程更具優勢。

  4. 模塊化:模塊化的程序有時候很適合用多線程來設計。

2.1.4 線程與進程的區別

主要差別在於它們是不同的操作系統資源管理方式。

進程有獨立的地址空間,一個進程崩潰後,在保護模式下不會對其它進程產生影響,而線程只是一個進程中的不同執行路徑。

線程有自己的堆棧和局部變量,但線程之間沒有單獨的地址空間,一個線程死掉就等於整個進程死掉,所以多進程的程序要比多線程的程序健壯。

但在進程切換時,耗費資源較大,效率要差一些。

但對於一些要求同時進行並且又要共享某些變量的並發操作,只能用線程,不能用進程。

2.2 線程模型

LinuxThreads和NPTL

用戶級別的線程是進程調用線程庫創建的,對操作系統是透明的,是進程中通過代碼實現切換的因此因此一旦一個線程引起了I/O阻塞從操作系統的視角就是進程阻塞,所以要阻塞整個進程;

而內核級別的線程是操作系統創建的,所以該阻塞線程的不會阻塞整個進程,當然效率也就沒有用戶級別的高。

2.3 同步

2.3.1 同步方式

  • 互斥量:采用互斥對象機制,只有擁有互斥對象的線程才有訪問公共資源的權限。因為互斥對象只有一個,所以可以保證公共資源不會被多個線程同時訪問。

  • 信號量:它允許同一時刻多個線程訪問同一資源,但是需要控制同一時刻訪問此資源的最大線程數量

  • 條件變量:通過通知操作的方式來保持多線程同步,還可以方便的實現多線程優先級的比較操作。

  • 讀寫鎖:

2.4 死鎖

2.4.1 死鎖是什麽

在兩個或者多個並發進程中,如果每個進程持有某種資源而又等待其它進程釋放它或它們現在保持著的資源,在未改變這種狀態之前都不能向前推進,稱這一組進程產生了死鎖。通俗的講就是兩個或多個進程無限期的阻塞、相互等待的一種狀態。

應該改為函數,一個進程中的一個函數,和信號處理函數。

2.4.2 死鎖產生的四個條件

有一個條件不成立,則不會產生死鎖。

  • 互斥條件:一個資源一次只能被一個進程使用

  • 請求與保持條件:一個進程因請求資源而阻塞時,對已獲得資源保持不放

  • 不剝奪條件:進程獲得的資源,在未完全使用完之前,不能強行剝奪

  • 循環等待條件:若幹進程之間形成一種頭尾相接的環形等待資源關系

系統資源的競爭

通常系統中擁有的不可剝奪資源,其數量不足以滿足多個進程運行的需要,使得進程在運行過程中,會因爭奪資源而陷入僵局,如磁帶機、打印機等。只有對不可剝奪資源的競爭 才可能產生死鎖,對可剝奪資源的競爭是不會引起死鎖的。

進程推進順序非法

進程在運行過程中,請求和釋放資源的順序不當,也同樣會導致死鎖。例如,並發進程 P1、P2分別保持了資源R1、R2,而進程P1申請資源R2,進程P2申請資源R1時,兩者都 會因為所需資源被占用而阻塞。

2.4.3 預防死鎖

  1. 破壞“請求和保持”條件:規定所有進程在開始運行之前,都必須一次性的申請其在整個運行過程所需要的全部資源。優點:簡單,安全。 缺點:資源嚴重浪費,惡化了系統的利用率;

  2. 破壞“不剝奪”條件:進程逐個的提出資源請求,當一個已經保持了某些資源的進程,再提出新的資源請求而不能立即得到滿足時,必須釋放它已經保持了的所有資源,待以後需要時再重新申請。

    缺點:實現復雜,代價大,反復地申請和釋放資源,而使進程的執行無限的推遲、延長了進程的周轉時間增加系統開銷、降低系統吞吐量。

  3. 破壞“環路等待”條件:將所有的資源按類型進行線性排隊,並賦予不同的序號。所有進程請求資源必須按照資源遞增的次序提出,防止出現環路。

    缺點:1、序號必須相對穩定,限制了新設備類型的增加2、作業(進程)使用資源順序和系統規定的順序不同而造成資源的浪費3、限制了用戶編程

由於互斥條件是非共享設備所必需的,不能改變

2.4.4 避免死鎖

  1. 加鎖順序(線程按照一定的順序加鎖)

    當多個線程需要相同的一些鎖,但是按照不同的順序加鎖,死鎖就很容易發生。

  2. 加鎖時限(線程嘗試獲取鎖的時候加上一定的時限,超過時限則放棄對該鎖的請求,並釋放自己占有的鎖)

    若一個線程沒有在給定的時限內成功獲得所有需要的鎖,則會進行回退並釋放所有已經獲得的鎖,然後等待一段隨機的時間再重試。這段隨機的等待時間讓其它線程有機會嘗試獲取相同的這些鎖,並且讓該應用在沒有獲得鎖的時候可以繼續運

  3. 死鎖檢測

    每當一個線程獲得了鎖,會在線程和鎖相關的數據結構中(map、graph等等)將其記下。除此之外,每當有線程請求鎖,也需要記錄在這個數據結構中。

    當一個線程請求鎖失敗時,這個線程可以遍歷鎖的關系圖看看是否有死鎖發生。例如,線程A請求鎖7,但是鎖7這個時候被線程B持有,這時線程A就可以檢查一下線程B是否已經請求了線程A當前所持有的鎖。如果線程B確實有這樣的請求,那麽就是發生了死鎖(線程A擁有鎖1,請求鎖7;線程B擁有鎖7,請求鎖1)。

    當然,死鎖一般要比兩個線程互相持有對方的鎖這種情況要復雜的多。線程A等待線程B,線程B等待線程C,線程C等待線程D,線程D又在等待線程A。線程A為了檢測死鎖,它需要遞進地檢測所有被B請求的鎖。從線程B所請求的鎖開始,線程A找到了線程C,然後又找到了線程D,發現線程D請求的鎖被線程A自己持有著。這是它就知道發生了死鎖。

    當檢測出死鎖時,這些線程該做些什麽呢?

    一個可行的做法是釋放所有鎖,回退,並且等待一段隨機的時間後重試。這個和簡單的加鎖超時類似,不一樣的是只有死鎖已經發生了才回退,而不會是因為加鎖的請求超時了。雖然有回退和等待,但是如果有大量的線程競爭同一批鎖,它們還是會重復地死鎖(編者註:原因同超時類似,不能從根本上減輕競爭)。

    一個更好的方案是給這些線程設置優先級,讓一個(或幾個)線程回退,剩下的線程就像沒發生死鎖一樣繼續保持著它們需要的鎖。如果賦予這些線程的優先級是固定不變的,同一批線程總是會擁有更高的優先級。為避免這個問題,可以在死鎖發生的時候設置隨機的優先級。

2.4.5 鎖的分類

分為獨占鎖,互斥量等

共享鎖和更新鎖 ,主要是讀寫鎖的,讀鎖和寫鎖

進程線程相關