20145320《信息安全系統設計基礎》第十三週學習總結

第 11 章 網絡編程

11.1 客戶端-服務器編程模型

每一個網絡應用都是基於客戶端一服務器模型的。一個應用是由一個服務器進程和一個或者多個客戶端進程組成。服務器管理某種資源,而且經過操做這種資源來爲它的客戶端提供某種服務。
客戶端一服務器模型中的基本操做是事務,由四步組成:程序員

  • 當一個客戶端須要服務時,它向服務器發送一個請求,發起一個事務。web

  • 服務器收到請求後,解釋它,並以適當的方式操做它的資源。算法

  • 服務器給客戶端發送一個響應,並等待下一個請求。數據庫

  • 客戶端收到響應並處理它。編程

一個客戶端-服務器事務:瀏覽器

11.2 網絡

  • 客戶端和服務器一般運行在不一樣的主機上,而且經過計算機網絡的硬件和軟件資源來通訊。安全

  • 使用一些電纜和叫作網橋 (bridge) 的小盒子,多個以太網段能夠鏈接成較大的局域網,稱爲橋接以太網 (bridged Ethernet)服務器

橋接以太網:網絡

  • 網橋比集線器更充分地利用了電纜帶寬。多線程

  • 多個不兼容的局域網能夠經過叫作路由器 (router)的特殊計算機鏈接起來,組成一個internet(互聯網絡)。

  • Internet 和 internet 咱們老是用小寫字母的 internet 描述通常概念, 而用大寫字母的Internet 來描述一種具體的實現,也就是所謂的全球 IP 因特網。

    11.3 全球 IP 因特網

  • 每臺因特網主機都運行實現 TCP/IP 協議 (Transmission Control Protocol/Internet Protocol,傳輸控制協議/互聯網絡協議)的軟件,幾乎每一個現代計算機系統都支持這個協議。

  • 因特網的客戶端和服務器混合使用套接字接口函數和 Unix I/O 函數來進行通訊。套接字函數典型地是做爲會陷入內核的系統調用來實現 的,並調用各類內核模式的 TCP/IP 函數。

    11.3.1 IP地址

  • 一個 IP 地址就是一個 32 位無符號整數。

  • htonl 函數將32 位整數由主機字節順序轉換爲網絡字節順序。

  • ntohl 函數將 32 位整數從網絡宇節順序轉換爲主機字節。

  • htons和 ntohs 函數爲 16 位的整數執行相應的轉換。

  • IP 地址一般是以一種稱爲點分十進制表示法來表示的。

  • "n" 表示的是網絡(network)。"a" 表示應用(application)。而 "to" 表示轉換。

11.3.2 因特網域名

  • 因特網客戶端和服務器互相通訊時使用的是 IP 地址。

  • 常見的第一層域名包括 com、 edu、 gov、org 和 net。

  • 下一層是二級 (second-level) 域名,例如 cmu. edu

  • 一旦一個組織獲得了一個二級域名,那麼它就能夠在這個子域中建立任何新的域名了。

  • 因特網定義了域名集合和 IP 地址集合之間的映射。

  • 因特網應用程序經過調用 gethostbyname 和 gethostbyaddr 函數,從 DNS 數據庫中檢索任意的主機條目。

11.3.3 因特網鏈接

  • Web 服務器一般使用端口 80,而電子郵件服務器使用端口 25。

    11.4 套接字接口

  • 套接字接口 (socket interface) 是一組函數,它們和 Unix I/O 函數結合起來,用以建立網絡應用。

套接字接口描述:

11.4.2 socket 函數

客戶端和服務器使用 socket 函數來建立一個套接字描述符

11.4.3 connect 函數

客戶端經過調用 connect 函數來創建和服務器的鏈接。

11.4.4 open_clientfd 函數

open_clientfd 函數和運行在主機 hostname 上的服務器創建一個鏈接,並在知名端口 port 上監聽鏈接請求。它返回一個打開的套接宇描述符,該描述符準備好了,能夠用 Unix I/O 函數作輸入和輸出。

11.4.5 bind 函數

bind、 listen 和 accept 被服務器用來和客戶端創建鏈接。

bind 函數告訴內核將 my_addr中的服務器套接字地址和套接字描述符 sockfd 聯繫起來。參數 addrlen 就是 sizeof(sockaddr_in) 。

11.4.6 listen 函數

客戶端是發起鏈接請求的主動實體。服務器是等待來自客戶端的鏈接請求的被動實體。默認狀況下,內核會認爲 socket 函數建立的描述符對應於主動套接字 (active socket),它存在 於一個鏈接的客戶端。

listen 函數將 sockfd 從一個主動套接字轉化爲一個監聽套接字 (listening socket),該套接字能夠接受來自客戶端的鏈接請求。

11.4.7 open_listenfd 函數


socket、 bind 和 listen 函數結合成一個叫作。open_listenfd 的輔助函數

11.4.8 accept 函數

accept 函數來等待來自客戶端的鏈接請求

accept 函數等待來自客戶端的鏈接請求到達偵聽描述符 listenfd,而後在 addr 中填寫客戶端的套接字地址,並返回一個巳鏈接描述符 (connected descriptor),這個描述符可被用來利用 Unix I/O 函數與客戶端通訊。

監聽描述符是做爲客戶端鏈接請求的一個端點。

11.5 Web 服務器

11.5.1 Web 基礎

Web 客戶端和服務器之間的交互用的是一個基於文本的應用級協議,叫作 HTTP (Hypertext Transfer Protocol,超文本傳輸協議). HTTP 是一個簡單的協議。一個 Web 客戶端(即瀏覽器) 打開一個到服務器的因特網鏈接,而且請求某些內容。服務器響應所請求的內容,而後關閉鏈接。瀏覽器讀取這些內容,並把它顯示在屏幕上。

11.5.2 Web 內容

每條由 Web 服務器返回的內容都是和它管理的某個文件相關聯的。這些文件中的每個都有一個惟一的名字,叫作 URL (Universal Resource Locator,通用資源定位符)。

11.5.3 HTTP 事務

由於 HTTP 是基於在因特網鏈接上傳送的文本行的,咱們可使用 Unix 的TELNET程序來和因特網上的任何 Web 服務器執行事務。

  • 1.HTTP 請求

    • 一個 HTTP 請求的組成是這樣的:一個請求行 (request line) (第 5 行),後面跟隨零個或更多個請求報頭 (request header) (第 6 行),再跟隨一個空的文本行來終止報頭列表

    • HTTP 支持許多不一樣的方法,包括 GET、 POST、 OPTIONS、 HEAD、 PUT、 DELETE 和 TRACE。

  • 2.HTTP 晌應

    • 一個 HTTP 響應的組成是這樣的:一個響應行 (response line) (第 8 行)後面跟隨着零個或更多的響應報頭 (response header) (第 9 ~ 13 行), 再跟隨一個終止報頭的空行(第 14 行),再跟隨一個響應主體 (response body)

第十二章 併發編程

  • 三種基本的構造併發程序的方法:
    • 進程

      每一個邏輯控制流是一個進程,由內核進行調度,進程有獨立的虛擬地址空間

    • I/O多路複用

      邏輯流被模型化爲狀態機,全部流共享同一個地址空間

    • 線程

      運行在單一進程上下文中的邏輯流,由內核進行調度,共享同一個虛擬地址空間

12.1 基於進程的併發編程

  • 基於進程的併發服務器

    1.使用SIGCHLD處理程序來回收僵死子進程的資源。

    2.父進程必須關閉他們各自的connfd拷貝(已鏈接的描述符),避免存儲器泄露。

    3.由於套接字的文件表表項中的引用計數,直到父子進程的connfd都關閉了,到客戶端的鏈接纔會終止。

  • 注意

    1.父進程須要關閉它的已鏈接描述符的拷貝(子進程也須要關閉)

    2.必需要包括一個SIGCHLD處理程序來回收僵死子進程的資源

    3.父子進程之間共享文件表,可是不共享用戶地址空間。

  • 關於獨立地址空間

    優勢:防止虛擬存儲器被錯誤覆蓋

    缺點:開銷高,共享狀態信息才須要IPC機制

12.2 基於I/O多路複用的併發編程

使用select函數,要求內核掛起進程,只有在一個或多個I/O事件發生後,纔將控制返回給應用程序。

select函數處理類型爲fd_set的集合,叫作描述符集合,看作一個大小爲n位的向量:

bn-1,......,b1,b0

  • 對描述符集合的處理方法:

    1.分配他們

    2.將一個此種類型的變量賦值給另外一個變量

    3.用FD_ZERO,FD_SET,FD_CLR和FD_ISSET宏指令來修改和檢查他們。

  • 基於I/O多路複用的併發事件驅動服務器

    1.I/O多路複用能夠用做事件併發驅動程序的基礎。

    2.狀態機:一組狀態、輸入事件、輸出事件和轉移。

    3.自循環:同一輸入和輸出狀態之間的轉移。

  • I/O多路複用技術的優劣

    • 相比基於進程的設計給了程序員更多的對進程行爲的控制,運行在單一進程上下文中,每一個邏輯流都能訪問所有的地址空間,在流之間共享數據很容易。

    • 編碼複雜,隨着併發粒度的減少,複雜性還會上升。粒度:每一個邏輯流每一個時間片執行的指令數量。

12.3 基於線程的併發編程

  • 線程執行模型

    • 每一個進程開始生命週期時都是單一線程(主線程),在某一時刻建立一個對等線程,今後開始併發地運行,最後,由於主線程執行一個慢速系統調用,或者被中斷,控制就會經過上下文切換傳遞到對等線程。
  • Posix線程

    • Posix線程是C語言中處理線程的一個標準接口,容許程序建立、殺死和回收線程,與對等線程安全的共享數據。

    • 線程的代碼和本地數據被封裝在一個線程例程中,

  • 建立線程

線程經過調用pthread_create來建立其餘線程。

int pthread_create(pthread_t *tid,pthread_attr_t *attr,func *f,void *arg);
成功則返回0,出錯則爲非零

當函數返回時,參數tid包含新建立的線程的ID,新線程能夠經過調用pthread_self函數來得到本身的線程ID。

pthread_t pthread_self(void);
返回調用者的線程ID。

  • 終止線程

一個線程是經過如下方式之一來終止的。

當頂層的線程例程返回時,線程會隱式地終止。
經過調用pthread_exit函數,線程會顯式地終止

void pthread_exit(void *thread_return);

  • 回收已終止的線程資源

線程經過調用pthread_join函數等待其餘線程終止。

int pthread_join(pthread_t tid,void **thread_return);
成功則返回0,出錯則爲非零

  • 分離線程

在任何一個時間點上,線程是可結合或可分離的。一個可結合的線程可以被其餘線程收回其資源和殺死,在被回收以前,它的存儲器資源是沒有被釋放的。分離的線程則相反,資源在其終止時自動釋放。

int pthread_deacth(pthread_t tid);
成功則返回0,出錯則爲非零

  • 初始化線程

pthread_once容許初始化與線程例程相關的狀態。

pthread_once_t once_control=PTHREAD_ONCE_INIT;
int pthread_once(pthread_once_t *once_contro,
void (*init_routine)(void));
老是返回0

12.4 多線程程序中的共享變量

一個變量是共享的,當且僅當多個線程引用這個變量的某個實例。

  • 1、線程存儲器模型

    1.每一個線程都有本身獨立的線程上下文,包括一個惟一的整數線程ID,棧、棧指針、程序計數器、通用目的寄存器和條件碼。

    2.寄存器是從不共享的,而虛擬存儲器老是共享的。

    3.各自獨立的線程棧被保存在虛擬地址空間的棧區域中,而且一般是被相應的線程獨立地訪問的。

  • 2、將變量映射到存儲器

    1.全局變量:定義在函數以外的變量

    2.本地自動變量:定義在函數內部可是沒有static屬性的變量。

  • 本地靜態變量:定義在函數內部並有static屬性的變量。

  • 3、共享變量

變量v是共享的——當且僅當它的一個實例被一個以上的線程引用。

12.5用信號量同步線程

  • 轉換規則:

    • 合法的轉換是向右或者向上,即某一個線程中的一條指令完成
    • 兩條指令不能在同一時刻完成,即不容許出現對角線
    • 程序不能反向運行,即不能出現向下或向左

而一個程序的執行歷史被模型化爲狀態空間中的一條軌跡線。

  • 信號量

    1.P(s):若是s是非零的,那麼P將s減一,而且當即返回。若是s爲零,那麼就掛起這個線程,直到s變爲非零。

    2.V(s):將s加一,若是有任何線程阻塞在P操做等待s變爲非零,那麼V操做會重啓線程中的一個,而後該線程將s減一,完成他的P操做。
    信號量不變性:一個正確初始化了的信號量有一個負值。

  • 信號量操做函數:

int sem_init(sem_t *sem,0,unsigned int value);//將信號量初始化爲value
int sem_wait(sem_t *s);//P(s)
int sem_post(sem_t *s);//V(s)

  • 使用信號量來實現互斥

    • 二元信號量(互斥鎖):將每一個共享變量與一個信號量s聯繫起來,而後用P(s)(加鎖)和V(s)(解鎖)操做將相應的臨界區包圍起來。

    • 禁止區:s<0,由於信號量的不變性,沒有實際可行的軌跡線可以直接接觸不安全區的部分

12.6 使用線程來提升並行性

並行程序的加速比一般定義爲:
Sp=T1/Tp

其中,p爲處理器核的數量,T爲在p個核上的運行時間

12.7 其餘併發問題

  • 線程安全性

一個線程是安全的,當且僅當被多個併發線程反覆的調用時,它會一直產生正確的結果。

四個不相交的線程不安全函數類以及應對措施:

不保護共享變量的函數——用P和V這樣的同步操做保護共享變量

保持跨越多個調用的狀態的函數——重寫,不用任何static數據。

返回指向靜態變量的指針的函數——①重寫;②使用加鎖-拷貝技術。

調用線程不安全函數的函數——參考以前三種

  • 可重入性

當它們被多個線程調用時,不會引用任何共享數據。

1.顯式可重入的:

全部函數參數都是傳值傳遞,沒有指針,而且全部的數據引用都是本地的自動棧變量,沒有引用靜態或全劇變量。

2.隱式可重入的:

調用線程當心的傳遞指向非共享數據的指針。

3、在線程化的程序中使用已存在的庫函數
一句話,就是使用線程不安全函數的可重入版本,名字以_r爲後綴結尾。

  • 競爭

    • 1.競爭發生的緣由:
      一個程序的正確性依賴於一個線程要在另外一個線程到達y點以前到達它的控制流中的x點。也就是說,程序員假定線程會按照某種特殊的軌跡穿過執行狀態空間,忘了一條準則規定:線程化的程序必須對任何可行的軌跡線都正確工做。

    • 2.消除方法:
      動態的爲每一個整數ID分配一個獨立的塊,而且傳遞給線程例程一個指向這個塊的指針

  • 死鎖:

1.定義

一組線程被阻塞了,等待一個永遠也不會爲真的條件。

2.解決死鎖的方法
a.不讓死鎖發生:

- 靜態策略:設計合適的資源分配算法,不讓死鎖發生---死鎖預防;
- 動態策略:進程在申請資源時,系統審查是否會產生死鎖,若會產生死鎖則不分配---死鎖避免。

b.讓死鎖發生:

進程申請資源時不進行限制,系統按期或者不按期檢測是否有死鎖發生,當檢測到時解決死鎖----死鎖檢測與解除。

代碼運行

  • hello_multi.c

先打印world換行打印hello,間隔1秒再打印相同內容,一共打印5次,最後輸出t1,t2 finished

  • hello_multi1.c

  • hello_single.c

打印一個hello,以後每間隔1秒打印一 個hello,共5個;而後打印一個world並換行,以後每間隔1秒打印一個world,共5個

  • incprint.c

在屏幕上換行輸出count=1,2,3,4,5,間隔1秒

  • twordcount1.c

統計兩個file的words數

  • twordcount2.c

引入pthread_mutex_t,統計兩個file的words數

  • twordcount3.c

先統計兩個file各自的words數,再統計兩個file的總words數

  • twordcount4.c

先輸出兩個file各自的信息和words數,再統計兩個file的總words數

  • condvar.c

消費者等待生產者產出產品後纔打印,不然消費者阻塞等待生產者生產。

  • count.c

建立兩個線程共享同一變量,都實現加一操做

  • countwithmutex.c

引入互斥鎖(Mutex)解決訪問衝突的問題

  • createthread.c

打印進程和線程ID

  • share.c

得到線程的終止狀態

  • threadexit.c

退出並輸出線程的狀態

相關文章
相關標籤/搜索