網絡應用隨處可見。任什麼時候候你瀏覽Web、發送郵件,你就在使用一個網絡應用程序。有趣的是,全部網絡應用都是基於相同的基本編程模型,有着類似的總體邏輯結構,而且依賴相同的編程接口。git
客戶端和服務器一般運行在不一樣的主機上,而且經過計算機網絡的硬件和軟件資源來通訊。
對於一個主機而言,網絡只是又一種I/O設備,做爲數據源和數據接收方。一個插到I/O總線擴展槽的適配器提供了到網絡的物理接口。從網絡上接收到的數據從適配器通過I/O和存儲器總線拷貝到存儲器,典型地是經過DMA傳送。
web
listen函數將sockfd從一個主動套接字轉化爲一個監聽套接字。該套接字能夠接受來自客戶端的鏈接請求。backlog參數暗示了內核在開始拒絕鏈接請求以前,該放入隊列中等待的未完成鏈接請求的數量。數據庫
Web客戶端和服務器之間的交互用的是一個基於文本的應用級協議,叫作HTTP。HTTP是一個簡單的協議。一個web客戶端(即瀏覽器)打開一個到服務器的因特網鏈接。瀏覽器讀取這些內容,並請求某些內容。服務器響應所請求的內容,而後關閉鏈接。瀏覽器讀取並把它顯示在屏幕內主要的區別是Web內容能夠用HTML來編寫。一個HTML程序(頁)包含指令(標記)它們告訴瀏覽器如何顯示這頁中的各類文本和圖形對象。編程
到目前爲止,咱們主要將併發看作是一種操做系統內核用來運行多個應用程序的機制。可是,併發不只僅侷限於內核。它也能夠在應用程序中扮演重要角色。例如,咱們已經看到Unix信號處理程序如何容許應用響應異步事件,例如用戶鍵入。或者程序訪問虛擬存儲器的個未定義的區域.應用級併發在其餘狀況下也是頗有用的。瀏覽器
在接受鏈接請求以後,服務器派生一個子進程,這個子進程得到服務器描述符表的完整拷貝。子進程關閉它的拷貝中的監聽描述符3,而父進程關閉它的已鏈接描述符4的拷貝,由於再也不須要這些描述符了。這就獲得了圖中的狀態,其中子進程正忙於爲客戶端提供服務。由於父子進程中的已鏈接描述符都指向同一個文件表表項,因此父進程關閉它的已鏈接描述符的拷貝是相當重要的。不然,將永遠不會釋放已鏈接描述符4的文件表條目,並且由此引發的存儲器泄漏將最終消耗盡可用的存儲器,使系統崩潰。如今假設在父進程爲客戶端1建立了子進程以後,它接受一個新的客戶端2的鏈接請求,並返回一個新的已鏈接描述符(好比描述符5)如圖所示。而後,父進程又派生另外一個子進程,這個子進程用已鏈接描述符5爲它的客戶端提供服務,如圖所示。此時,父進程正在等待下一個鏈接請求,而兩個子進程正在形地爲它們各自的客戶端提供服務。
安全
一個服務器,它有兩個I/O事件:1)網絡客戶端發起鏈接請求,2)用戶在鍵盤上鍵入命令行。咱們先等待那個事件呢?沒有那個選擇是理想的。若是accept中等待鏈接,那麼沒法相應輸入命令。若是在read中等待一個輸入命令,咱們就不能響應任何鏈接請求(這個前提是一個進程)。
針對這種困境的一個解決辦法就是I/O多路複用技術。基本思想是:使用select函數,要求內核掛起進程,只有在一個或者多個I/O事件發生後,纔將控制返給應用程序。如圖所示:橫向的方格能夠看做是一個n位的描述符向量。如今,咱們定義第0位描述是「標準輸入」,第3位描述符是「監聽描述符」。服務器
每一個線程都有本身的線程上下文,包括一個線程ID、棧、棧指針、程序計數器、通用目的寄存器和條件碼。全部的運行在一個進程裏的線程共享該進程的整個虛擬地址空間。因爲線程運行在單一進程中,所以共享這個進程虛擬地址空間的整個內容,包括它的代碼、數據、堆、共享庫和打開的文件。
1、線程執行模型網絡
2、Posix線程多線程
Posix線程是C程序中處理線程的一個標準接口。併發
萬能函數:
void func(void parameter)
typedef void (uf)(void para)
3、建立線程
1.建立線程:
pthread_create函數
#include <pthread.h>
typedef void (func)(void );int pthread_create(pthread_t tid, pthread_attr_t attr, func f, void arg);
功能:建立一個新的線程,帶着一個輸入變量arg,在新線程的上下文運行線程例程f。
2.查看線程ID
pthread_self函數
#include <pthread.h>
pthread_t pthread_self(void);
功能:返回調用者的線程ID(TID)
4、終止線程
1.終止線程的方式:隱式終止、顯示終止
2.pthread_exit函數
#include <pthread.h>
void pthread_exit(void *thread_return);
3.pthread_cancle函數
#include <pthread.h>
void pthread_cancle(pthread_t tid);
5、回收已終止線程的資源
pthread_join函數:
#include <pthread.h>
int pthread_join(pthread_t tid,void **thrad_return);
6、分離線程
pthread_detach函數
#include <pthread.h>
void pthread_detach(pthread_t tid);
功能:分離可結合線程tid。
7、初始化線程
pthread_once函數
#include <pthread.h>
pthread_once_t once_control = PTHREAD_ONCE_INIT;int pthread_once(pthread_once_t once_control, void (init_routine)(void));
全局變量和static變量是存儲在數據段,因此多線程共享之!因爲線程的棧是獨立的,全部線程中的自動變量是獨立的。即便多個線程運行同一段代碼總的自動變量,那麼他們的值也是根據線程的不一樣而不一樣。
信號量一般稱之爲PV操做,雖然它的思想是將臨界代碼保護起來,達到互斥效果。這裏面操做系統使用到了線程掛起。
將線程i的循環代碼分解成五個部分:
到目前爲止,在對併發的研究中,咱們都假設併發線程是在單處許多現代機器具備多核處理器。併發程序一般在這樣的機器上運理器系統上執行的。然而,在多個核上並行地調度這些併發線程,而不是在單個核順序地調度,在像繁忙的Web服務器、數據庫服務器和大型科學計算代碼這樣的應用中利用這種並行性是相當重要的。
condvar.c
運行結果:
mutex
用於保護資源,wait
函數用於等待信號,signal
函數用於通知信號,wait
函數中有一次對`mutex的釋放和從新獲取操做,所以生產者和消費者並不會出現死鎖。
share.c
運行結果:
得到線程的終止狀態,thr_fn 1
,thr_fn 2
和thr_fn 3
三個函數對應終止線程的三種方法,即從線程函數return
,調用pthread_exit
終止本身和調用pthread_cancel
終止同一進程中的另外一個線程。
countwithmutex.c
運行結果:
引入互斥鎖(Mutex),得到鎖的線程能夠完成」讀-修改-寫」的操做,而後釋放鎖給其它線程,沒有得到鎖的線程只能等待而不能訪問共享數據。
semphore.c
運行結果:
semaphore
表示信號量,semaphore
變量的類型爲sem_t,sem_init()
初始化一個semaphore
變量,value
參數表示可用資源 的數量,pshared
參數爲0表示信號量用於同一進程的線程間同步。
count.c
運行結果:
這是一個不加鎖的建立兩個線程共享同一變量都實現加一操做的程序,在這個程序中雖然每一個線程都給count加了5000,但因爲結果的互相覆蓋,最終輸出值不是10000,而是5000。
代碼行數(新增/累積) | 博客量(新增/累積) | 學習時間(新增/累積) | 重要成長 | |
---|---|---|---|---|
目標 | 5000行 | 30篇 | 400小時 | |
第一週 | 200/200 | 2/2 | 20/20 | |
第二週 | 200/400 | 2/4 | 18/38 | |
第三週 | 100/500 | 1/5 | 10/48 | |
第四周 | 250/750 | 1/6 | 10/58 | |
第五週 | 100/850 | 1/7 | 10/68 | |
第六週 | 100/950 | 1/8 | 12/80 | |
第七週 | 200/1150 | 1/9 | 12/92 | |
第八週 | 124/1274 | 2/11 | 10/102 | |
第九周 | 205/1479 | 2/13 | 5/107 | |
第十週 | 333/1712 | 2/15 | 10/117 | |
第十一週 | 758/2470 | 3/18 | 12/129 | |
第十二週 | 25/2495 | 3/21 | 10/139 | |
第十三週 | 956/3451 | 1/22 | 10/149 |