20145302張薇《資訊保安系統設計基礎》第13周學習總結

20145302張薇發表於2016-12-11

20145302張薇《資訊保安系統設計基礎》第13周學習總結

教材學習內容總結

1.客戶端-伺服器程式設計模型


一個伺服器程式 -> 管理某種資源 -> 通過操作這種資源來為它的客戶端提供某種服務
一個或多個客戶端程式
  • 基本操作:事務

當一個客戶端需要服務時,向伺服器傳送一個請求,發起一個事務。
伺服器收到請求後,解釋它,並以適當的方式操作它的資源。
伺服器給客戶端傳送一個相應,並等待下一個請求。
客戶端收到響應並處理它。

20145302張薇《資訊保安系統設計基礎》第13周學習總結

  • 客戶端和伺服器都是程式。

2.網路
(1)對主機而言:網路是一種I/O裝置

  • 從網路上接收到的資料從介面卡經過I/O和儲存器匯流排拷貝到儲存器,典型地是通過DMA(直接儲存器存取方式)傳送。

(2)物理上:網路是一個按照地理遠近組成的層次系統

  • 最底層:LAN(區域網),最流行的是乙太網,

  • 乙太網段

    • 包括一些電纜和集線器。每根電纜都有相同的最大位頻寬,集線器不加分辯地將一個埠上收到的每個位複製到其他所有的埠上,因此每臺主機都能看到每個位。
    • 每個乙太網介面卡都有一個全球唯一的48位地址,儲存在介面卡的非易失性儲存器上。
    • 一臺主機可以傳送一段位:幀,到這個網段內其它任何主機。每個幀包括一些固定數量的頭部位(標識此幀的源和目的地址及幀長)和資料位(有效載荷)。每個主機都能看到這個幀,但是隻有目的主機能讀取。
    • 使用電纜和網橋,多個乙太網段可以連線成較大的區域網,稱為橋接乙太網。這些電纜的頻寬可以是不同的。
    • 多個不相容的區域網可以通過叫做路由器的特殊計算機連線起來,組成一個internet網際網路絡。

(3)協議

  • 網際網路重要特性:由採用不同技術,互不相容的區域網和廣域網組成,並能使其相互通訊。其中不同網路相互通訊的解決辦法是一層執行在每臺主機和路由器上的協議軟體,消除不同網路的差異。

  • 協議提供的兩種基本能力

    • 命名機制:唯一的標示一臺主機
    • 傳送機制:定義一種把資料位捆紮成不連續的片的同一方式

(4)全球IP因特網

  • TCP/IP協議族
  • 混合使用套接字介面函式和UnixI/O函式進行通訊
  • 世界範圍的主機集合

特性:
- 主機集合被對映為一組32位的IP地址
- 這組IP地址被對映為一組稱為因特網域名的識別符號
- 因特網主機上的程式能夠通過連線和任何其他主機上的程式
  • 檢索並列印一個DNS主機條目:

#include "csapp.h"
int main(int argc, char **argv) 
{
    char **pp;
    struct in_addr addr;
    struct hostent *hostp;
    if (argc != 2) {
    fprintf(stderr, "usage: %s <domain name or dotted-decimal>\n", 
        argv[0]);
    exit(0);
    }
    if (inet_aton(argv[1], &addr) != 0) 
    hostp = Gethostbyaddr((const char *)&addr, sizeof(addr), AF_INET); 
    else                                
    hostp = Gethostbyname(argv[1]);
    printf("official hostname: %s\n", hostp->h_name);
    for (pp = hostp->h_aliases; *pp != NULL; pp++)
    printf("alias: %s\n", *pp);
    for (pp = hostp->h_addr_list; *pp != NULL; pp++) {
    addr.s_addr = ((struct in_addr *)*pp)->s_addr;
    printf("address: %s\n", inet_ntoa(addr));
    }
    exit(0);
}

3.套接字

20145302張薇《資訊保安系統設計基礎》第13周學習總結

  • 函式:

    • socket函式
    • connect函式
    • open_clientfd函式
    • bind函式
    • listen函式
    • open_listenfd函式
    • accept函式

4.Web伺服器
(1)協議

  • Web 客戶端和伺服器之間的互動用的是一個基於文字的應用級協議,叫做 HTTP (Hypertext Transfer Protocol,超文字傳輸協議). HTTP 是一個簡單的協議。一個 Web 客戶端(即瀏覽器) 開啟一個到伺服器的因特網連線,並且請求某些內容。伺服器響應所請求的內容,然後關閉連線。瀏覽器讀取這些內容,並把它顯示在螢幕上。

(2)內容

  • Web內容可以用一種叫做 HTML(Hypertext Markup Language,超文字標記語言)的語言來編寫。一個 HTML 程式(頁)包含指令(標記),它們告訴瀏覽器如何顯示這頁中的各種文字和圖形物件。

  • Web 伺服器以兩種不同的方式向客戶端提供內容:

    • 取一個磁碟檔案,並將它的內容返回給客戶端。磁碟檔案稱為靜態內容 (static content), 而返回檔案給客戶端的過程稱為服務靜態內容 (serving static content)。 執行一個可執行檔案,並將它的輸出返回給客戶端。執行時可執行檔案產生的輸出稱為態內容 (dynamic content),而執行程式並返回它的輸出到客戶端的過程稱為服務動態內容 (serving dynamic content)。

5.併發概述

  • 邏輯控制流在時間上重疊,那麼它們就是併發的。
  • 併發(concurrency ) ,出現在計算機系統的許多不同層面上。

  • 應用級併發

    • 訪問慢速I/O裝置。當一個應用正在等待來自慢速 I/O 裝置(例如磁碟)的資料到達時, 核心會執行其他程式,使 CPU保持繁忙。每個應用都可以按照類似的方式,通過交替執行 I/O 請求和其他有用的工作來使用併發。
    • 與人互動。和計算機互動的人要求計算機有同時執行多個任務的能力。例如,他們在列印一個文件時,可能想要調整一個視窗的大小。現代視窗系統利用併發來提供這種能力。每次使用者請求某種操作(比如通過單擊滑鼠)時,一個獨立的併發邏輯流被建立來執行這個操作。
    • 通過推遲工作以降低延遲。有時,應用程式能夠通過推遲其他操作和併發地執行它們,利用併發來降低某些操作的延遲。比如,一個動態儲存分配器可以通過推遲合併,把它放到一個執行在較低優先順序上的併發"合併"流中,在有空閒的 CPU 週期時充分利用這些空閒 週期,從而降低單個 free 操作的延遲。
    • 服務多個網路客戶端。
    • 在多核機器上進行平行計算。許多現代系統都配備有多核處理器,多核處理器中包含多個 CPU。被劃分成併發流的應用程式通常在多核機器上比在單處理器機器上執行得快,因為這些流會並行執行,而不是交錯執行。
  • 使用應用級併發的應用程式稱為併發程式。現代作業系統提供了三種基本的構造併發程式的方法:
    • 每個邏輯控制流都是一個程式,由核心來排程和維護。因為程式 有獨立的虛擬地址空間,想要和其他流通訊,控制流必須使用某種顯式的程式間通訊(IPC)機制。
    • I/O 多路複用。在這種形式的併發程式設計中,應用程式在一個程式的上下文中顯式地排程它們自己的邏輯流。邏輯流被模型化為狀態機,資料到達檔案描述符後,主程式顯式地從一個狀態轉換到另一個狀態。因為程式是一個單獨的程式,所以所有的流都共享同一個地址空間。
    • 執行緒是執行在一個單一程式上下文中的邏輯流,由核心進行排程。是其他兩種方式的混合體,像程式流一樣由核心進行排程,而像I/O 多路複用流一樣共享同一個虛擬地址空間。

6.基於程式的併發程式設計

  • 構造併發程式最簡單的方法就是用程式。
    • 一個構造併發伺服器的自然方法就是,在父程式中接受客戶端連線請求,然後建立一個新的子程式來為每個新客戶端提供服務。
  • 基於程式的併發伺服器
    • 通常伺服器會執行很長的時間,所以我們必須要包括一個 SIGCHLD 處理程式,來回收僵死 (zombie) 子程式的資源。因為當 SIGCHLD 處理程式執行時, SIGCHLD 訊號是阻塞的,而 Unix 訊號是不排隊的,所以 SIGCHLD 處理程式必須準備好回收多個僵死子程式的資源。

    • 父子程式必須關閉它們各自的 connfd 拷貝。這對父程式而言尤為重要,它必須關閉它的已連線描述 符,以避免儲存器洩漏。

    • 因為套接字的檔案表表項中的引用計數,直到父子程式的 connfd 都關閉了,到客戶端的連線才會終止。


第一步:伺服器接受客戶端的連線請求
第二步:伺服器派生一個子程式為這個客戶端服務
第三步:伺服器接受另一個連線請求
第四步:伺服器派生另一個子程式為新的客戶端服務
  • 程式的優劣
    • 在父、子程式間共享狀態資訊,程式有一個非常清晰的模型:共享檔案表,但是不共享使用者地址空間。

    • 程式有獨立的地址空間既是優點也是缺點。
    • 優點:一個程式不可能不小心覆蓋另一個程式的虛擬儲存器,這就消除了許多令人迷惑的錯誤。
    • 缺點:獨立的地址空間使得程式共享狀態資訊變得更加困難。為了共享資訊,它們必須使用顯式的IPC(程式間通訊)機制。基於程式的設計的另一個缺點是,它們往往比較慢,因為程式控制和 IPC 的開銷很高。

7.基於 I/O 多路複用的併發程式設計


I/O 多路複用(I/O multiplexing) 技術。基本的思路就是使用 select 函式,要求核心掛起程式,只有在一個或多個I/O事件發生後,才將控制返回給應用程式。

(1)基於 I/O 多路複用的併發事件驅動伺服器

  • I/O 多路複用可以用做併發事件驅動 (event-driven) 程式的基礎,在事件驅動程式中,流是因為某種事件而前進的。

  • 將邏輯流模型化為狀態機。不嚴格地說,一個狀態機 (state machine) 就是一組狀態 (state)、輸入事件(input event) 和轉移他(transition),其中轉移就是將狀態和輸入事件對映到狀態。每個轉移都將一個(輸入狀態,輸入事件)對對映到一個輸出狀態。自迴圈(self-loop) 是同一輸入和輸出狀態之間的轉移。節 點表示狀態,有向弧表示轉移,而弧上的標號表示輸入事件。一個狀態機從某種初始狀態開始執行。每個輸入事件都會引發一個從當前狀態到下一狀態的轉移。

  • 伺服器使用I/O多路複用,藉助 select 函式檢測輸入事件的發生。

  • 伺服器呼叫 select 函式來 檢測兩種不同型別的輸人事件: - a) 來自一個新客戶端的連線請求到達 - b) 一個己存在的客戶 端的己連線描述符準備好可以讀了。


init_pool 函式初始化客戶端池。 clientfd 陣列表示已連線描述符的集合, 其中整數 -1 表示一個可用的槽位。初始時,已連線描述符集合是空的,而且監聽描述符是 select 讀集合中唯一的描述符。
add_clieht函式新增一個新的客戶端到活動客戶端池中。在 clientfd 陣列中找到一個空槽位後,伺服器將這個已連線描述符新增到陣列中,並初始化相應的RIO讀緩衝區,這樣一來我們就能夠對這個描述符呼叫rio_readlineb。將這個已連線描述符新增到 select 讀集合,並更新該池的一些全域性屬性。 maxfd 變數記錄了 select 的最大檔案描述符。 maxi 變數記錄的 是到 clientfd陣列的最大索引,這樣 check_clients 函式就無需搜尋整個陣列了。
check_clients 函式回送來自每個準備好的已連線描述符的一個文字行。 如果成功地從描述符讀取了一個文字行,那麼我們就將該文字行回送到客戶。
select 函式檢測到輸入事件,而 add_client 函式建立 一個新的邏輯流(狀態機)。
check_clients 函式通過回送輸入行來執行狀態轉移,而且當客 戶端完成文字行傳送時,它還要刪除這個狀態機。

(2)I/O 多路複用技術的優劣

事件驅動設計的優點:

  • 它比基於程式的設計給了程式設計師更多的對程式行為的控制。
  • 一個基於 I/O 多路複用的事件驅動伺服器是執行在單一程式上下文中的,因 此每個邏輯流都能訪問該程式的全部地址空間。

事件驅動設計的缺點:

  • 編碼複雜。我們的事件驅動的併發 echo 伺服器需要的程式碼比基於程式的伺服器多三倍。不幸的是,隨著併發粒度的減小,複雜性還會上升。這裡的粒度是指每個邏輯流每個時間片執行的指令數量。

8.基於執行緒的併發程式設計

  • 執行緒(thread) 就是執行在程式上下文中的邏輯流。

  • 每個執行緒都有它自己的執行緒上下文 (thread context),包括一個唯一的整數執行緒 (Thread ID, TID)、棧、棧指標、程式計數器、通用目的暫存器和條件碼。所有的執行在一個程式裡的執行緒共享該程式的整個虛擬地址空間。

  • 基於執行緒的邏輯流結合了基於程式和基於 I/O 多路複用的流的特性。同程式一樣,執行緒由核心自動排程,並且核心通過一個整數 ID 來識別執行緒。同基於 I/O 多路複用的流一樣,多個執行緒 執行在單一程式的上下文中,因此共享這個程式虛擬地址空間的整個內容,包括它的程式碼、資料、堆、共享庫和開啟的檔案。

(1)執行緒執行模型

  • 每個程式開始生命週期時都是單一執行緒,這個執行緒稱為主執行緒 (main thread)。在某一時刻,主執行緒建立一個對等執行緒 (peer thread),從這個時間點開始,兩個執行緒就併發地執行。最後,因為主執行緒執行一個慢速系統呼叫。或者因為它被系統的間隔計時器中斷, 控制就會通過上下文切換傳遞到對等執行緒。對等執行緒會執行一段時間,然後控制傳遞迴主執行緒,依次類推。

- 執行緒的上下文切換要比程式的上下文切換快得多。
- 不是按照嚴格的父子層次來組織的。
- 和一個程式相關的執行緒組成一個對等(執行緒)池 (pool),獨立於其他執行緒建立的執行緒。
- 主執行緒和其他執行緒的區別僅在於它總是程式中第一個執行的執行緒。
- 對等 (執行緒)池概念的主要影響是,一個執行緒可以殺死它的任何對等執行緒,或者等待它的任意對等執行緒終止。
- 每個對等執行緒都能讀寫相同的共享資料。

(2)Posix 執行緒

  • Posix 執行緒 (Pthreads) 是在 C 程式中處理執行緒的一個標準介面。Pthreads 定義了大約 60 個函式,允許程式建立、殺死和回收執行緒,與對等執行緒安全地共享資料,還可以通知對等執行緒系統狀態的變化。

  • 執行緒的程式碼和本地資料被封裝在一個執行緒例程(thread routine) 中。如果想傳遞多個引數給錢程例程,那麼你應該將引數放 到一個結構中,並傳遞一個指向該結構的指標。想要執行緒例程返回多個引數,你可以返回一個指向一個結構的指標。

(3)建立執行緒

  • pthread_create 函式建立一個新的執行緒,並帶著一個輸入變數arg,在新執行緒的上下文中執行執行緒例程f。能用attr引數來改變新建立執行緒的預設屬性。

  • 當 pthreadcreate 返回時,引數 tid包含新建立執行緒的ID。新執行緒可以通過呼叫 pthreadself 函式來獲得它自己的執行緒 ID.

(4)終止執行緒

  • 當頂層的執行緒例程返回時,執行緒會隱式地終止。 通過呼叫 pthreadexit 函式,執行緒會顯式地終止。如果主執行緒呼叫 pthreadexit , 它會等待所有其他對等執行緒終止,然後再終止主執行緒和整個程式,返回值為 thread_return。

(5)回收已終止執行緒的資源

  • 執行緒通過呼叫 pthread_join 函式等待其他執行緒終止。

  • pthreadjoin 函式會阻塞,直到執行緒 tid 終止,將執行緒例程返回的 (void*) 指標賦值為 threadreturn 指向的位置,然後回收己終止執行緒佔用的所有儲存器資源。
  • pthread join 函式只能等待一個指定的執行緒終止。
    (6)分離執行緒

  • 在任何一個時間點上,執行緒是可結合的 (joinable) 或者是分離的 (detached)。一個可結合的執行緒能夠被其他執行緒收回其資源和殺死。在被其他執行緒回收之前,它的儲存器資源(例如棧)是沒有被釋放的。相反,一個分離的執行緒是不能被其他執行緒回收或殺死的。它的儲存器資源在它終止時由系統自動釋放。

  • 預設情況下,執行緒被建立成可結合的。為了避免儲存器洩漏,每個可結合執行緒都應該要麼被其他執行緒顯式地收回,要麼通過呼叫 pthread_detach 函式被分離。

  • pthreaddetach 函式分離可結合執行緒 tid. 執行緒能夠通過以 pthreadself()為引數的 pthread_detach 呼叫來分離它們自己。
    (7)初始化執行緒

  • pthread_once 函式允許你初始化與執行緒例程相關的狀態。

(8)一個基於執行緒的併發伺服器

  • 呼叫 pthread_ create 時,如何將已連線描述符傳遞給對等執行緒。最明顯的方法就是傳遞一個指向這個描述符的指標。 對等執行緒間接引用這個指標,並將它賦值給一個區域性變數。

9.多執行緒程式中的共享變數
(1)執行緒儲存器模型


一個變數是共享的,當且僅當多個執行緒引用這個變數的某個例項。

(2)共享變數


變數是共享的<=>當且僅當它的一個例項被一個以上的執行緒引用

10.用訊號量同步執行緒
(1)進度圖

  • 進度圖是將n個併發執行緒的執行模型化為一條n維笛卡爾空間中的軌跡線,原點對應於沒有任何執行緒完成一條指令的初始狀態。

  • 當n=2時,狀態比較簡單,是比較熟悉的二維座標圖,橫縱座標各代表一個執行緒,而轉換被表示為有向邊


轉換規則:
- 合法的轉換是向右或者向上,即某一個執行緒中的一條指令完成
- 兩條指令不能在同一時刻完成,即不允許出現對角線
- 程式不能反向執行,即不能出現向下或向左

而一個程式的執行歷史被模型化為狀態空間中的一條軌跡線。
  • 執行緒迴圈程式碼的分解: H:在迴圈頭部的指令塊 L:載入共享變數cnt到執行緒i中暫存器%eax的指令。 U:更新(增加)%eax的指令 S:將%eax的更新值存回到共享變數cnt的指令 T:迴圈尾部的指令塊

  • 臨界區:對於執行緒i,操作共享變數cnt內容的指令L,U,S構成了一個關於共享變數cnt的臨界區。

  • 不安全區:兩個臨界區的交集形成的狀態
  • 安全軌跡線:繞開不安全區的軌跡線

(2)訊號量

訊號量實現互斥的基本原理

    • 兩個或多個程式通過傳遞訊號進行合作,可以迫使程式在某個位置暫時停止執行(阻塞等待),直到它收到一個可以“向前推進”的訊號(被喚醒);

將實現訊號燈作用的變數稱為訊號量,常定義為記錄型變數s,其一個域為整型,另一個域為佇列,其元素為等待該訊號量的阻塞程式(FIFO)。


訊號量定義:

type semaphore=record
count: integer;
queue: list of process
end;
var s:semaphore;
  • 使用訊號量來實現互斥基本思想:將每個共享變數(或者一組相關的共享變數)與一個訊號量s(初始為1)聯絡起來,然後用P和V操作將相應的臨界區包圍起來。
  • 二元訊號量:用這種方式來保護共享變數的訊號量叫做二元訊號量,取值總是0或者1.
  • 互斥鎖:以提供互斥為目的的二元訊號量
  • 加鎖:對一個互斥鎖執行P操作
  • 解鎖:對一個互斥鎖執行V操作
  • 計數訊號量:被用作一組可用資源的計數器的訊號量
  • 禁止區:由於訊號量的不變性,沒有實際可能的軌跡能夠包含禁止區中的狀態。

wait(s)/signal(s)的應用

程式進入臨界區之前,首先執行wait(s)原語,若s.count<0,則程式呼叫阻塞原語,將自己阻塞,並插入到s.queue佇列排隊;
注意,阻塞程式不會佔用處理機時間,不是“忙等”。直到某個從臨界區退出的程式執行signal(s)原語,喚醒它;
一旦其它某個程式執行了signal(s)原語中的s.count+1操作後,發現s.count ≤0,即阻塞佇列中還有被阻塞程式,則呼叫喚醒原語,把s.queue中第一個程式修改為就緒狀態,送就緒佇列,準備執行臨界區程式碼。

相關文章