網絡程式設計
并發的意義
概念:
隻要邏輯控制流在時間上重疊,那麼就可以稱為并發。
意義:
通路慢速裝置(如I/O裝置):CPU可以在這樣的慢速中“騰出手”再去做其他事情,使自己保持“繁忙”。
與人互動:每次使用者進行某種操作的請求的時候使用者不會在意在自己進行操作的時候系統是否還處理着其他程式,一個獨立的邏輯并發流被用來處理這個程式。
通過推遲工作來降低延遲 服務多個網絡用戶端
程序:每個邏輯控制流都是一個程序,由核心進行排程和維護。
程序間的通信也必須有顯式的通信機制。
每個程序都有獨立的虛拟位址空間,發生交流的時候肯定要有機制
i/O多路複用:在并發程式設計中,一個程式在上下文中調用它們自己的邏輯流。因為程式是一個單獨的程序,是以所有的流共享一個位址空間。即,對于從該程序所屬的資料空間内進出的I/O道路上“運輸”的資料是要提供給多個邏輯流的
線程:像程序一樣由核心調用,像I/O多路複用一樣共享同樣的虛拟位址空間 2.構造并發伺服器過程 (假設是一個伺服器端兩個用戶端,伺服器端始終監聽)伺服器正在監聽一個描述符3,用戶端1向伺服器端提出申請,伺服器端傳回一個已經建立連接配接描述符4; 伺服器派生一個子程序處理該連接配接。子程序擁有從父程序伺服器描述符清單中完整的拷貝。,
父程序與子程序需要各自切斷連接配接:父程序切斷它的描述符清單中的連接配接描述符4,子程序切斷它拷貝的監聽描述符3; 伺服器接收新的用戶端2的連接配接請求,同樣傳回連接配接描述符5,派生一個子程序; 伺服器端繼續等待請求,兩個子程序并發地處理用戶端連接配接。 基于程序的并發程式設計的優缺點
優點:程序不可能不小心覆寫另一個程序的虛拟存儲器。
缺點:獨立的位址空間使得程序共享狀态資訊變得更加困難。為了共享資訊,它們必須使用顯示的IPC(程序間通信)機制,而程序控制和IPC的開銷很高,是以會比較慢。
基于I/O多路複用技術的并發程式設計的優缺點
優點: 它比基于程序的設計給了程式員更多的對程式行為的控制。 每個邏輯流都能通路該程序的全部位址空間,這使得流之間共享資料變得容易。
缺點:編碼複雜。
線程與線程池
線程不是按照嚴格的父子層次來組織的。和一個線程相關的線程組成一個對等(線程)池獨立于其他線程建立的線程。
主線程和其他線程的差別僅在于它總是程序中第一個運作的線程。在對等線程池中,一個線程可以殺死它的任何對等線程,或者等待它的任意對等線程終止。另外,每個對等線程都能讀寫相同的共享資料
台網際網路主機都運作實作TCP/IP協定的軟體。
網際網路的用戶端和伺服器混合使用套接字接口函數和Unix I/O函數來進行通信。
TCP/IP實際是一個協定組,其中每一個都提供不同的功能。
IP位址
htonl函數将32位整數由主機位元組順序轉換成網絡位元組順序。
ntohl函數将32位整數從網絡位元組順序轉換成主機位元組。
htons函數和ntohs為16位的整數執行相應的轉換。
可以使用hostname -i來确定自己主機的點分十進制位址
網際網路域名
對于IP位址人性化的命名為域名。
DNS域名系統
可以用hostinfo程式來挖掘一些DNS映射的特性。
hostname确定自己的主機域名。
網際網路連結
網際網路用戶端和伺服器通過在連接配接上發送和接收位元組流來通信。
一個套接字是連接配接的一個斷點,每個套接字都由相應的套接字位址,是由一個網際網路位址和一個16位的整數端口組成的,用“位址:端口”來表示。
在Unix機器上,檔案/etc/services包含一張這台機器提供的服務以及它們的知名端口号的綜合清單。
一個連結是由它兩端的套接字位址唯一确定的,稱為套接字對。
套接字接口
sockaddr_in的16位元組結構
sin_family成員是AF_INET
sin_port成員是一個16位的端口号
sin_addr成員是一個32位的IP位址。
IP位址和端口号總時以網絡位元組順序(大端法)存放的。
_in是網際網路絡的縮寫,不是輸入input的縮寫。
以下函數劉念老師的課上都說明過,僅作記錄
socket函數 p626
connect函數 p626
open_clientfd函數 p627
bind函數 p628
listen函數 p628
open_listenfd函數 p628
accept函數 p629
Web伺服器
Web伺服器使用HTTP協定和它們的用戶端(浏覽器等)彼此通信。
浏覽器向伺服器請求靜态或者動态的内容
對靜态内容的請求是通過從伺服器磁盤取得檔案并把它傳回給用戶端來服務的。
對動态内容的請求時通過在伺服器上一個子程序的上下文中運作一個程式并将它的輸出傳回給用戶端來服務的。
CGI标準提供了一組規則,來管理用戶端如何将程式參數傳遞給伺服器。伺服器如何将這些參數以及其他資訊傳遞給子程序,以及子程序如何将它的輸出發送回用戶端。
并發程式設計
使用應用級并發的應用程式稱為并發程式。
現代作業系統提供了三種基本的構造并發程式的方法:
程序
I/O多路複用
線程
關于程序的優劣
對于在父、子程序間共享狀态資訊,程序有一個非常清晰的模型:共享檔案表,但是不共享使用者位址空間。
獨立的抵制空間使得程序共享狀态資訊變得更加困難。為了共享資訊,它們必須使用顯式的IPC(程序間通信)機制。是以它們往往比較慢。
基于I/O多路複用的并發程式設計
i/O多路複用技術
使用select函數,要求核心挂起程序,隻有在一個或多個I/O事件發生後,才傳回控制給應用。
select函數處理類型為fd_set的集合,也叫做描述符集合。
select函數有兩個輸入
讀集合
讀集合的基數n(實際上是任何描述符集合的最大技術)。
select函數會一直阻塞,直到讀集合中至少有一個描述符準備好可以讀。
當且僅當一個從該描述符讀取一個位元組的請求不會阻塞時,描述符k就表示準備好可以讀了。
select函數的傳回值指明了準備好集合的基數。
我們必須每次調用select時都更新讀集合。
基于I/O多路複用的并發事件驅動伺服器
一個狀态機就是一組狀态、輸入事件和轉移
轉移就是将狀态和輸入事件映射到狀态。
自循環是同一個輸入和輸出狀态之間的轉移。
伺服器使用I/O多路複用,借助select函數檢測輸入事件的發生。
霍東閣用戶端的集合維護在一個pool結構中。
通過調用init_pool初始化池之後,伺服器進入一個無限循環。
在循環的每次疊代中,伺服器調用selct函數來檢測兩種不同類型的輸入事件:
來自一個新用戶端的連接配接請求到達
一個已存在的用戶端的已連接配接描述符準備号可以讀了。
當一個連接配接請求到達時,伺服器打開連接配接,并調用add_client函數,将該用戶端添加到池裡。
最後伺服器調用check_clients函數,把來自每個準備好的已連接配接描述符的一個文本行回送回去。
基于線程的并發程式設計
線程就是運作在程序上下文中的邏輯流。
程式都是由每個程序中一個現成組成的 。
每個現成都有自己的線程上下文,包括一個唯一的整數線程IP、棧、棧指針、程式計數器、通用目的寄存器和條件碼。所有的運作在一個程序裡的線程共享該流程的整個虛拟位址空間。
線程執行模型
每個程序開始生命周期時都是單一線程,這個線程稱為主線程。
在某個時刻,主線程建立一個對等線程,從這個時間點開始,兩個線程就并發地運作。
因為主線程執行一個慢速系統調用,控制就會通過上下文切換傳遞到對等線程。
Posix線程
Posix線程是在C程式中處理線程的一個标準接口。
線程的代碼和本地資料被封裝在一個線程例程中。
每個線程例程都以一個通用指針作為輸入,并傳回一個通用指針。
本地變量tid,可以用來存放對等線程的現成ID。
主線程通過調用pthread_create函數建立一個新的對等線程。
通過嗲用pthread_join,主線程等待對等線程終止。
主線程調用exit,終止當時運作在這個程序中的所有線程。
終止線程
一個線程是以下之一來終止的:
當頂層的線程例程傳回時,線程會隐式地終止。
通過調用pthread_exit函數,線程會顯示地終止。
如果主線程調用thread_exit,它會等待所有其他對等線程終止,然後再終止主線程和整個程序,傳回值為thread_return。
某個對等線程嗲用Unix地exit函數,該函數終止程序以及所有與該程序相關的線程。
另一個對等線程通過以目前線程ID作為參數調用pthread_cancle函數來終止目前線程。
分離線程
在任何一個時間點上,線程是可結合地或者是分離的。
pthread函數分離可結合線程tid。
線程能夠通過以pthread_self()為參數的pthread_detach調用來分離它們自己。
初始化線程
pthread_once函數允許你初始化與線程例程相關的狀态。
once_control變量是一個全局或者靜态變量,總是初始化為PTHREAD_ONCE_INIT。
将變量映射到存儲器
線程化的C程式中變量根據它們的存儲類型被映射到虛拟存儲器:
全局變量
本地自動變量
本地靜态變量
共享變量
一個變量可共享時,當且僅當它的一個執行個體被一個以上的線程引用。
将線程的循環代碼分解成五個部分:
Hi:循環頭部的指令塊
Li:加載cnt
Ui:更新cnt
Si:存儲cnt
Ti:尾部的指令塊
信号量 p668
程式通過調用sem_wait和sem_post函數來執行P和V操作。
P中測試和減1操作是不分割旳。
線程安全
不保護共享變量的函數
保護跨越多個調用的狀态的函數
傳回指向靜态變量指針的函數調用線程不安全函數的函數