操作系統——進程,線程,鎖


基本概念

狀態、地址空間

  1. 三種基本狀態 —— 就緒、運行、阻塞

  1. 進程控制塊PCB(Process Control Block)

    1. 進程描述信息(如PID);
    2. 進程控制&管理信息(狀態、優先級等);
    3. 源分配清單(地址空間狀況、fd等);
    4. 處理其相關信息(各寄存器的值等)

進程存在的標識,在Linux系統中是task_struct,task_struct在內核棧(Linux進程氛圍用戶棧和內核棧)的尾端分配。

  1. 進程地址空間

從低地址到高地址:

  • text 代碼段 —— 代碼段,一般是只讀的區域;
  • static_data 段 =
  • stack 棧區 —— 局部變量,函數的參數,返回值等,由編譯器自動分配釋放;
  • heap 堆區 —— 動態內存分配,由程序員分配釋放;
    image
  1. 進程與線程

進程是擁有資源的基本單位,進程的地址空間相互獨立;

線程是獨立調度的基本單位,共享同一個進程內的資源(線程有自己的棧),減少了程序並發時所付出的時空開銷,並且可以高效的共享數據,有效地利用多處理器和多核計算機,提高os的並發度。

一個進程異常退出不會引起另外的進程運行異常;但是線程若異常退出一般是會引起整個進程奔潰。

創建/撤銷/切換 進程的開銷遠大於線程的(創建線程比創建進程快10~100倍 UNPv2/P406)。

  1. 僵屍、孤兒、守護

孤兒進程 —— 父進程退出,而它的一個或多個子進程還在運行,那么那些子進程將成為孤兒進程。孤兒進程將被init進程(進程號為1)所收養,並由init進程對它們完成狀態收集工作。

僵屍進程 —— 一個進程使用fork創建子進程,如果子進程退出,而父進程並沒有調用wait或waitpid獲取子進程的狀態信息,那么子進程的進程描述符仍然保存在系統中。

守護進程 —— 守護進程的父進程是init進程,因為它真正的父進程在fork出子進程后就先於子進程exit退出了,所以它是一個由init繼承的孤兒進程。不需要用戶輸入就能運行而且提供某種服務,不是對整個系統就是對某個用戶程序提供服務。常見的守護進程包括系統日志進程syslogd、 web服務器httpd、郵件服務器sendmail和數據庫服務器mysqld等。

最大進程數以及單進程內的最大線程數?

最大進程數受以下3方面限制:

  1. 不能超過pid_t類型的最大值
  2. 使用命令ulimit -u查看系統中限制的最大進程數。/etc/security/limits.conf里面是硬限制,ulimit -u是軟限制,內核參數kernel.pid_max也做了限制。
  3. 受系統資源限制,創建一個新進程會消耗系統資源,最主要的就是內存。

IPC (interprocess communication)

UNP 分了以下幾中形式的IPC:

  • 消息傳遞 —— 管道、FIFO、消息隊列
  • 共享內存
  • 同步 ——信號量、互斥量、條件變量、讀寫鎖、文件和記錄鎖、
  • 遠程過程調用 —— solaris 門、Sun RPC
  • 跨網絡 IPC —— 套接字
  • 域套接字
  • 信號

進程間通信方式:匿名管道,有名管道,消息隊列,共享內存,信號量,套接字,域套接字,信號

線程同步方式:互斥量,條件變量,讀寫鎖

進程間通信

匿名管道

半雙工的(即數據只能在一個方向上流動),具有固定的讀端和寫端;是一種特殊的文件(pipefs,掛載在內核中),有固定大小,只存在於內存中;

實現原理:

管道是由內核管理的一個緩沖區,被設計成為環形的數據結構,以便管道可以被循環利用。當管道中沒有信息的話,從管道中讀取的進程會等待,直到另一端的進程放入信息。當管道被放滿信息的時候,嘗試放入信息的進程會等待,直到另一端的進程取出信息。當兩個進程都終結的時候,管道也自動消失。

在 Linux 中,管道的實現借助了文件系統的file結構和VFS的索引節點inode。通過將兩個file結構指向同一個臨時的VFS索引節點,而這個VFS索引節點又指向一個物理頁面而實現的。

內核會利用一定的機制同步對管道的訪問。
image

有名管道

半雙工,可在無關進程使用;FIFO有路徑名與之相關聯,以一種特殊設備文件形式存在於文件系統中

實現原理:

Linux中設立了一個專門的特殊文件系統--管道文件,FIFO在文件系統中有對應的路徑。當一個進程以讀(r)的方式打開該文件,而另一個進程以寫(w)的方式打開該文件,那么內核就會在這兩個進程之間建立管道,雖然FIFO在VFS的目錄樹下可見,但是它並不對應disk上的文件。
本質上是一個先進先出的隊列數據結構,最早放入的數據被最先讀出來,從而保證信息交流的順序。FIFO只是借用了文件系統來為管道命名。當刪除FIFO文件時,管道連接也隨之消失。當進程終止時,管道內的數據會被刪除。

消息隊列

  1. 面向記錄的,其中的消息具有特定的格式以及特定的優先級;
  2. 獨立於發送與接收進程。進程終止時,消息隊列及其內容並不會被刪除(隨內核的持性)。
  3. 可以實現消息的隨機查詢,不一定要以先進先出的次序讀取,也可以按消息的類型讀取。
為什么不使用消息隊列
  1. 進程終止時,消息隊列及其內容並不會被刪除(隨內核的持性)。
  2. 在文件系統中沒有名字,不能使用IO。

共享內存和信號量

共享內存是最快的:

通常往管道、FIFO或消息隊列寫入數據時,這些IPC需要將數據從進程復制到內核,通常總共需要復制4次,而共享內存則只拷貝2次數據;如圖:
image
image

信號(Signal)

用於通知接收進程,有某種事件發生。

信號是在軟件層次上對中斷機制的一種模擬,在原理上,一個進程收到一個信號與處理器收到一個中斷請求可以說是一樣的。信號是異步的,一個進程不必通過任何操作來等待信號的到達。

  • SIGRTMIN之前的信號是非排隊(不可靠)的,多次連續發生的相同信號,只遞交一次;SIGRTMIN之后的信號不會丟失,會遞交多次。
  • 在信號處理函數
    • singal:只阻塞當前正在處理的信號,后續信號會排隊(也會區分可靠和不可靠)
    • sigaction:可以設置阻塞正在處理的信號和其他型號

Pipe 與 FIFO 比較:

  1. pipe在特殊文件系統pipefs中(內核中),VFS 目錄樹下不可見;FIFO 在目錄樹下可見;
  2. 都有 inode,但沒有磁盤鏡像(disk image);
  3. Pipe 用於親緣關系進程通信;FIFO 無此要求;
  4. 限制都包含兩個:OPEN_MAX(一個進程在任意時刻打開的最大描述符,默認1024);還有PIPE_BUF(可原子性地往一個管道/FIFO 的最大數據量,默認 4K)

線程間同步

互斥鎖(Mutex)

加鎖原語,排他性訪問共享數據,用於保護臨界區。可細分為遞歸鎖/非遞歸鎖。
如果存在某個線程依然使用原先的程序

(即不嘗試獲得mutex,而直接修改共享變量),互斥鎖不能阻止其修改。所以,互斥鎖機制需要程序員自己來寫出完善的程序來實現互斥鎖的功能(以下鎖 一樣)。

image

條件變量(Condition Variable)

互斥鎖用於上鎖,條件變量用於等待,條件變量的使用是與互斥鎖共通使用的。

條件變量學名叫管程(monitor)【From muduo P40】。

image

讀寫鎖

讀寫鎖也叫做 共享-獨占鎖,允許更高的並發度。

互斥量要么是鎖住狀態,要么是不加鎖狀態,而且一次只有一個線程對其加鎖。
讀寫鎖可以有三種狀態:讀模式下加鎖狀態,寫模式下加鎖狀態,不加鎖狀態。一次只有一個線程可以占有寫模式的讀寫鎖,但是多個線程可用同時占有讀模式的讀寫鎖。

image

讀寫鎖可以通過使用互斥鎖和條件變量來實現。

自旋鎖(spinlock)

在獲取鎖之前一直處於忙等(自旋)阻塞狀態;常用於
鎖被持有的時間短,且線程並不希望在重新調度上花費太多成本。當線程自旋等待鎖變為可用時,CPU不能做其他事情。

故而自旋鎖常作為底層原語,用於實現其他類型的鎖。

記錄鎖

記錄鎖是讀寫鎖的一種擴展類型,可用於親緣關系或無親緣關系的進程之間共享某個文件的讀與寫。被鎖住的文件通過文件描述符進行訪問,執行上鎖的操作函數是fcntl,這種類型的鎖通常在內核中維護。

記錄鎖的功能是:一個進程正在讀或修改文件的某個部分時,可以阻止其他進程修改同一文件區,即其鎖定的是文件的一個區域或整個文件。

記錄鎖有兩種類型:共享讀鎖,獨占寫鎖。基本規則是:多個進程在一個給定的字節上可以有一把共享的讀鎖,但在一個給定字節上的寫鎖只能有一個進程獨用。即:如果在一個給定的字節上已經有一把讀或多把讀鎖,則不能在該字節上再加寫鎖;如果在一個字節上已經有一把獨占性的寫鎖,則不能再對它加任何讀鎖。

死鎖

  1. 死鎖 —— 就是兩個或多個進程被無限期地阻塞、相互等待的一種狀態。
  2. 死鎖的四個條件
    1. 競爭同一個資源
    2. 持有資源不釋放
    3. 不能搶占資源
    4. 循環使用資源

處理死鎖的策略:
一般來說,打破循環使用資源最容易,即順序加減鎖

image
銀行家算法(死鎖避免算法)。在資源動態分配過程中,防止系統進入不安全狀態,以避免發生死鎖。

數據庫中會用到等待圖進行死鎖檢測

  1. 死鎖定理:
    通過將資源分配圖簡化的方法,來檢測系統狀態是否為死鎖狀態。
    當且僅當S狀態的資源分配圖不可完全簡化時,S為死鎖狀態。

經典問題

  1. 生產者-消費者問題
  2. 讀者-寫者問題

Futex

在Linux下,信號量和線程互斥鎖的實現都是通過futex系統調用。


免責聲明!

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



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