20145312 《信息安全系統設計基礎》第13周學習總結

20145312 《信息安全系統設計基礎》第13周學習總結

教材內容總結

第11章 網絡編程

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

  • 每一個網絡應用都是基於客戶端-服務端編程模型
  • 客戶端和服務端是進程
    html

    11.2 網絡

  • 對於一個主機而言,網絡只是又一種I/O設備
  • 物理上而言,網絡是一個按照地理遠近組成的層次系統。
  • 最底層是LAN(局域網)
  • 適配器提供到網絡的物理接口
  • 以太網段(電纜+集線器)
  • 一臺主機能夠發送一段位,稱爲幀
  • 每一個主機適配器都能看到這個幀,可是隻有目的主機實際讀取它
  • 多個以太網段能夠鏈接成較大的局域網,稱爲橋接以太網
  • 網橋比集線器更充分的利用了電纜帶寬
  • 在層次較高的級別中,多個不兼容的局域網能夠經過路由器鏈接起來,組成互聯網絡
  • 每臺路由器對於它所鏈接到的每一個網絡都有一個適配器(端口)
  • 路由器能夠用來由各類局域網和廣域網構建互聯網絡
  • 網絡協議提供兩種基本能力:
命名機制
傳送機制
  • 封裝是關鍵

11.3 全球IP因特網

  • 因特網的客戶端和服務端混合使用套接字接口函數和Unix I/O函數來進行通訊
  • 因特網上的主機經過IP地址和域名來標識
  • TCP/IP其實是一個協議族
  • IP機制從某種意義上而言是不可靠的
  • TCP是一個構建在IP之上的複雜協議,提供了進程間可靠地全雙工鏈接
    linux

    11.3.1 IP地址
  • 一個IP地址就是一個32位無符號整數
  • IP/TCP爲任意整數數據項定義了統一的網絡字節順序(大端字節順序)
  • 對inet-aton的調用傳遞的是指向結構的指針,而對inet_ntoa的調用傳遞的是結構自己git

    11.3.2 因特網域名
  • 域名集合造成一個層次結構,子樹稱爲子域
  • 一個IP對多個域名,可供多個域名解析,但域名解析到的地址是一個對一個
  • 某些合法域名沒有映射到任何IP地址程序員

    11.3.3 因特網鏈接
  • 點對點、全雙工、可靠
  • 客戶端套接字地址中的端口是由內核自動分配的,稱爲臨時端口
  • 服務端套接字地址中的端口一般是某個知名端口(HTTP:80)
  • 套接字地址:(地址:端口)
    web

    11.4套接字接口

  • 套接字接口是一組函數,用以建立網絡應用
  • 套接字地址結構
sin_family成員是AF_INET
sin_port成員是一個16位端口
sin_addr成員是32位的IP地址
  • IP地址和端口號老是以網絡字節順序(大端法)存放的
socket函數

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

connect函數

創建和服務器的鏈接。數組

open_clientfd函數

將socket和connect函數包裝而成。客戶端能夠用它來和服務器創建鏈接。瀏覽器

bind函數

告訴內核將my_addr中的服務器套接字地址和套接字描述符sockfd聯繫起來安全

listen函數

將sockfd從一個主動套接字轉化爲一個監聽套接字服務器

accept函數

均被服務器用於和客戶端創建鏈接。

open_listenfd函數

socket、bind和listen函數結合。用於服務器建立一個監聽描述符。

11.5Web服務器

協議
  • Web 客戶端和服務器之間的交互用的是一個基於文本的應用級協議,叫作 HTTP (超文本傳輸協議).
  • HTTP 是一個簡單的協議。
  • 一個 Web 客戶端(即瀏覽器) 打開一個到服務器的因特網鏈接,而且請求某些內容。服務器響應所請求的內容,而後關閉鏈接。瀏覽器讀取這些內容,並把它顯示在屏幕上。
  • Web內容能夠用一種叫作 HTML(Hypertext Markup Language,超文本標記語言)的語言來編寫。一個 HTML 程序(頁)包含指令(標記),它們告訴瀏覽器如何顯示這頁中的各類文本和圖形對象。

    內容
  • 對於Web客戶端和服務端而言,內容是與一個MIME類型相關的字節序列
  • Web 服務器以兩種不一樣的方式向客戶端提供內容:
  • 取一個磁盤文件,並將它的內容返回給客戶端。磁盤文件稱爲靜態內容 , 而返回文件給客戶端的過程稱爲服務靜態內容
  • 運行一個可執行文件,並將它的輸出返回給客戶端。運行時可執行文件產生的輸出稱爲態內容 ,而運行程序並返回它的輸出到客戶端的過程稱爲服務動態內容
  • 每條由Web服務器返回的內容都是和他管理的某個文件相關聯的。這些文件每個都有一個惟一的名字,叫作:URL

    第12章 併發程序

  • 使用應用級併發的應用程序稱爲併發程序。現代操做系統提供了三種基本的構造併發程序的方法:
進程
I/O多路複用
線程

12.1 基於進程的併發進程

  • 構造併發程序最簡單的方法就是用進程
第一步:服務器接受客戶端的鏈接請求
第二步:服務器派生一個子進程爲這個客戶端服務
第三步:服務器接受另外一個鏈接請求
第四步:服務器派生另外一個子進程爲新的客戶端服務
12.1.1 基於進程的併發服務器
  • 首先,包括一個SIGCHLD處理程序,回收僵死子進程資源
  • 其次,父子進程必須關閉它們各自的connfd拷貝,以避免存儲器泄露
  • 最後,知道父子進程的connfd都關閉了,到客戶端的鏈接纔會終止

    12.1.2 關於進程的優劣
  • 父子進程間共享狀態信息,共享文件表,可是不共享用戶地址空間。
  • 進程有獨立的地址空間(既是優勢也是缺點)
優勢:一個進程不可能不當心覆蓋另外一個進程的虛擬存儲器
缺點:獨立的地址空間使得進程共享信息變得更加困難;IPC機制每每比較慢
12.2 基於I/O多路複用的併發進程
  • 基本思路就是使用select函數,要求內核掛起進程,在I/O事件發生後,纔將控制返回給應用程序。
  • select函數處理類型爲fd_set的集合,也叫做描述符集合
  • 只容許對描述符集合作三件事:
分配
將一個此種類型的變量賦值給另外一個變量
用FD_ZERO、FD_SET等宏指令來修改和檢查它們
12.2.1 基於I/O多路複用的併發事件驅動服務器
  • 一個狀態機就是:狀態、輸入事件和轉移(狀態機sk)
狀態:等待描述符dk準備好可讀
輸入事件:描述符dk準備好能夠讀了
轉移:從描述符dk讀一個文本行
  • 自循環是同一輸入和輸出狀態之間的轉移
  • init pool:初始化活動客戶端池
  • clientfd 數組表示已鏈接描述符的集合, 其中整數 -1 表示一個可用的槽位。初始時,已鏈接描述符集合是空的,並且監聽描述符是 select 讀集合中惟一的描述符。
  • add_client:向池中添加一個新的客戶端鏈接
  • select 函數檢測到輸入事件,而 add_client 函數建立 一個新的邏輯流(狀態機)。
  • check_clients:爲準備好的客戶端鏈接服務

    12.2.2 I/O多路複用技術的優劣
  • 優勢:它比基於進程的設計給了程序員更多的對程序行爲的控制,它是運行在單一進程上下文中的,所以每一個邏輯流都能訪問該進程的所有地址空間
  • 缺點:編碼複雜

12.3 基於線程的併發編程

  • 線程就是運行在進程上下文的邏輯流,由內核進行調度
  • 每一個線程都有它本身的線程上下文,包括一個惟一的整數線程ID、棧、棧指針、程序計數器、通用目的寄存器和條件碼
  • 全部的運行在一個進程裏的線程共享該進程的整個虛擬地址空間
12.3.1 線程執行模型
  • 主線程:每一個進程開始生命週期時第一個運行的線程
  • 對等線程:某時刻主線程建立的
  • 線程的上下文切換要比進程的上下文切換快得多。
  • 和一個進程相關的線程組成一個對等(線程)池 (pool),獨立於其餘線程建立的線程。
  • 線程不像進程同樣,不是按照嚴格的父子進程組織的
  • 每一個對等線程都能讀寫相同共享數據

    12.3.2 Posix線程
  • Posix線程是在C程序中處理線程的一個標準接口
  • Pthread容許程序建立、殺死和回收線程,與對等線程安全的共享數據,還能夠通知對等線程系統狀態的變化
  • 現成的代碼和本地數據被封裝在一個線程例程中

    12.3.3 建立線程
  • 線程經過調用pthread_create函數來建立其餘線程
  • 新線程能夠經過調用pthread_self函數來得到它本身的線程ID

    12.3.4 終止線程
  • 當頂層的線程例程返回時,線程會隱式的終止
  • 經過調用pthread_exit函數,線程會顯示的zhongzhi
  • 某個對等線程調用Unix的exit函數,該函數終止進程以及全部與該進程相關的線程
  • 另外一個對等線程經過調用pthread_cancle函數來終止當前線程

    12.3.5 回收已終止線程的資源
  • 線程經過調用pthread_join函數等待其餘線程終止
  • 與wait函數不一樣,pthread_join函數只能等待一個指定的線程終止

    12.3.6 分離線程
  • 一個分離的線程是不能被其餘線程回收或殺死的。它的存儲器資源在它終止時由系統自動釋放
  • 每一個可結合線程都應該要麼被其餘線程顯示的收回,要麼經過調用pthread_detach函數被分離
  • pthreaddetach 函數分離可結合線程 tid. 線程可以經過以 pthreadself()爲參數的 pthread_detach 調用來分離它們本身。

    12.3.7 初始化線程
  • pthread_once函數容許你初始化與線程例程相關的狀態
  • once_control變量是一個全局或者靜態變量

    12.3.8 一個基於線程的併發服務器
  • 主線程不斷地等待鏈接請求,而後建立一個對等線程處理該要求
  • 爲了不潛在的致命競爭,將每一個accept返回的已鏈接描述符分配到它本身的動態分配的存儲器塊
  • 另外一個問題是在線程例程中避免存儲器泄露
  • 調用 pthread_ create 時,如何將已鏈接描述符傳遞給對等線程。最明顯的方法就是傳遞一個指向這個描述符的指針。 對等線程間接引用這個指針,並將它賦值給一個局部變量。

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

12.4.1 線程存儲器模型
  • 每一個線程都有它本身獨立的線程上下文,每一個線程和其餘線程一塊兒共享進程上下文的剩餘部分
  • 寄存器是從不共享的,而虛擬存儲器老是共享的
  • 若是一個線程以某種方式獲得一個指向其餘線程棧的指針,那麼它就能夠讀寫這個棧的任何部分

    12.4.2 將變量映射到存儲器(根據存儲類型)
  • 全局變量:全局變量是定義在函數以外的變量
  • 本地自動變量:本地自動變量就是定義在函數內部可是沒有static屬性的變量
  • 本地靜態變量:本地靜態變量是定義在函數內部有static屬性的變量

    12.4.3 共享變量
  • 一個變量是共享的,當且僅當它的一個實例被一個以上線程引用
  • myid不是共享的,由於它的兩個實例中每個都只被一個線程引用
  • msgs這樣的本地自動變量也能被共享

12.5 用信號量同步線程

12.5.1進度圖
  • 進度圖是將n個併發線程的執行模型化爲一條n維笛卡爾空間中的軌跡線,原點對應於沒有任何線程完成一條指令的初始狀態。
  • 當n=2時,狀態比較簡單,是比較熟悉的二維座標圖,橫縱座標各表明一個線程,而轉換被表示爲有向邊
  • 轉換規則:
合法的轉換是向右或者向上,即某一個線程中的一條指令完成
兩條指令不能在同一時刻完成,即不容許出現對角線
程序不能反向運行,即不能出現向下或向左
而一個程序的執行歷史被模型化爲狀態空間中的一條軌跡線。
線程循環代碼的分解: H:在循環頭部的指令塊 L:加載共享變量cnt到線程i中寄存器%eax的指令。 U:更新(增長)%eax的指令 S:將%eax的更新值存回到共享變量cnt的指令 T:循環尾部的指令塊
  • 臨界區:對於線程i,操做共享變量cnt內容的指令L,U,S構成了一個關於共享變量cnt的臨界區。
  • 不安全區:兩個臨界區的交集造成的狀態
  • 安全軌跡線:繞開不安全區的軌跡線

    12.5.2信號量
  • 信號量是具備非負整數值的全局變量,只能由兩種特殊的操做來處理,這兩種操做稱爲P和V
P(s):若是s是非零的,那麼P將s-1,而且當即返回;若是s爲零,那麼就掛起這個線程,直到s變爲非零
V(s):V操做將s+1
  • 當有多個線程在等待同一個信號量時,你不能預測V操做要重啓哪個線程。
  • 信號量不變性:一個正在運行的程序毫不能進入這樣一種狀態,也就是一個正確初始化了的信號量有一個負值。

    12.5.3使用信號量來實現互斥
  • 二元信號量:將每一個共享變量與一個信號量聯繫起來,而後用而後用P(S)和V(s)操做將這種臨界區包圍起來,這種方式來保護共享變量的信號量。
  • 互斥鎖:以提供互斥爲目的的二元信號量
  • 加鎖:一個互斥鎖上執行P操做稱爲對互斥鎖加鎖,執行V操做稱爲對互斥鎖解鎖。對一個互斥鎖加了鎖但尚未解鎖的線程稱爲佔用了這個互斥鎖。
  • 計數信號量:一個唄用做一組可用資源的計數器的信號量

    代碼調試中的問題和解決過程

    countwithmutex.c

  • pthread庫不是linux系統默認的庫,所以pthread_creat建立線程時,在編譯中要加上-lpthread參數。
  • 代碼中涉及到的函數:
pthread_creat:建立線程,若成功則返回0,若失敗則返回出錯編號。第一個參數爲指向線程標識符的指針,建立成功時指向的內存單元被設置爲新建立線程的線程ID;第二個參數設置線程屬性;第三個參數是線程運行函數的起始地址;最後一個參數是運行函數的參數
pthread_join:用來等待一個線程的結束。當函數返回時,被等待線程的資源被收回。
pthread_mutex_lock:線程調用該函數讓互斥鎖上鎖。成功鎖定時返回0,其餘任何返回值都表示出現了錯誤。
pthread_mutex_unlock:與pthread_mutex_lock成對存在。釋放互斥鎖。
  • 因爲定義的NLOOP值爲5000,因此程序最後的輸出值爲10000。

count.c

  • 這個代碼用於與countwithmutex.c進行對比,差異在於本代碼doit函數的for循環中沒有引入互斥鎖,只進行了單純的計數,建立兩個線程共享同一變量都實現加一操做。

condvar.c

  • 這個代碼演示的是生產者生產和消費者消費交替進行的過程。是線程間同步的一種狀況。
  • 主函數中用srand(time(NULL))設置當前的時間值爲種子,在後面的producer和consumer函數中調用rand()函數產生隨機數。

    cp_t.c

  • 代碼中涉及到的函數:
mmap函數
`void* mmap(void* start,size_t length,int prot,int flags,int fd,off_t offset);`
將一個文件或者其餘對象映射進內存。文件被映射到多個頁上,若是文件的大小不是全部頁的大小之和,最後一個頁不被使用的空間將會清零。mmap在用戶空間映射調用系統中做用很大。
成功執行時,mmap()返回被映射區的指針,munmap()返回0.失敗時,mmap()返回MAP_FAILED,munmap返回-1.
lseek函數
off_t lseek(int fd,off_t offset,int whence);
fd表示要操做的文件描述符,offset是相對於whence(基準)的偏移量,whence能夠是SEEK_SET(文件指針開始),SEEK_CUR(文件指針當前位置),SEEK_END(文件指針尾)
lseek主要做用是移動文件讀寫指針,返回文件讀寫指針距文件開頭的字節大小,若出錯則返回-1.
  • 運行結果

    createthread.c

  • 程序主要演示了建立線程函數pthread_create()函數的使用,用來打印進程和線程的ID
  • 主函數中先利用pthread_create()函數建立一個線程,接着調用printids函數(打印標識符的函數)打印主線程號,最後線程函數thr_fn中打印出新建的線程號

    sieve.c

  • 編譯時出現錯誤
  • 按照錯誤提示發現是由於沒有鏈接數學庫引起的錯誤,因此在編譯時加上-lm,能夠成功編譯,運行結果提示了段錯誤。
  • 查找資料得知:
    段錯誤就是指訪問的內存超出了系統所給這個程序的內存空間,一般這個值是由gdtr來保存的,他是一個48位的寄存器,其中的32位是保存由它指 向的gdt表,後13位保存相應於gdt的下標,最後3位包括了程序是否在內存中以及程序的在cpu中的運行級別,指向的gdt是由以64位爲一個單位的 表,在這張表中就保存着程序運行的代碼段以及數據段的起始地址以及與此相應的段限和頁面交換還有程序運行級別還有內存粒度等等的信息。一旦一個程序發生了 越界訪問,cpu就會產生相應的異常保護,因而segmentation fault就出現了. 在編程中如下幾類作法容易致使段錯誤,基本是是錯誤地使用指針引發的
1)訪問系統數據區,尤爲是往 系統保護的內存地址寫數據    最多見就是給一個指針以0地址 
  2)內存越界(數組越界,變量類型不一致等
  3)訪問到不屬於你的內存區域
  • 閱讀代碼後仍沒找到錯誤,問題還沒有解決。

    semphore.c

  • 代碼中涉及到的函數:
sem_init函數
sem_init(sem_t *sem, int pshared, umsigned int value);
函數初始化一個定位在sem的匿名信號量;pshared參數爲0指明信號量是由進程內線程共享,若爲非0值則信號量在進程之間共享;value參數指定信號量的初始值。
sem_init()成功時返回0;錯誤時返回-1,並把errno設置爲合適的值。
sem_destroy()函數用於銷燬由sem指向的匿名信號量。只有經過sem_init()初始化的信號量才應該使用該函數銷燬。函數成功時返回0,錯誤時返回-1,並把errno設置爲合適的值。
這個函數和以前的condvar.c同樣都是展現生產者和消費者交替工做的過程。區別是本程序實現生產或消費的過程是利用sem_wait()和sem_post(),它們的做用分別是從信號量的值減去一個「1」和從信號量的值加上一個「1」
  • 運行結果截圖

    share.c

  • 代碼運行結果以下

    hello_multi.c

  • 程序中的print_msg()函數中:在printf後的fflush(stdout);說明要馬上將要輸出的內容輸出,每輸出一次停1秒,並循環5次。
  • 若想要使程序輸出像預期的打印出5個完整的helloworld,只須要將線程t1和t2的位置互換,修改代碼以下
  • 修改後代碼運行以下

    hello_multi1.c

  • 代碼運行結果以下

    hello_single.c

  • 根據代碼,先單獨執行print_msg("hello");——輸出5個hello,後輸出5個帶換行的world

    incprint.c

  • 因爲定義中NUM=5,因此輸出的count爲1——5

    twordcount.c

  • twordcount1.c運行以下
  • twordcount2.c運行以下
  • twordcount3.c運行以下
  • twordcount4.c運行以下

本週代碼託管截圖





學習進度條

代碼行數(新增/累積) 博客量(新增/累積) 學習時間(新增/累積) 重要成長
目標 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
第十週 646/2125 2/15 9/116
第十一週 421/2546 2/17 12/128
第十二週 752/3298 3/10 11/139
第十三週 1001/4299 1/21 12/151

參考資料

相關文章
相關標籤/搜索