第05章并發(fā)服務器_第1頁
第05章并發(fā)服務器_第2頁
第05章并發(fā)服務器_第3頁
第05章并發(fā)服務器_第4頁
第05章并發(fā)服務器_第5頁
已閱讀5頁,還剩31頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

第05章并發(fā)服務器第一頁,共36頁。目錄服務器分類技術進程與線程多進程服務器多線程服務器并發(fā)服務器第二頁,共36頁。服務器分類按連接類型分類面向連接的服務器(如tcp)面向無連接的服務器(如udp)按處理方式分類迭代服務器并發(fā)服務器第三頁,共36頁。迭代服務器vs.并發(fā)服務器綁定地址監(jiān)聽連接接收連接處理連接斷開連接接收請求處理請求返回響應綁定地址監(jiān)聽連接接收連接創(chuàng)建子進程關閉連接套接字處理連接關閉連接套接字終止子進程關閉監(jiān)聽套接字服務器主進程服務器子進程TCP迭代服務器TCP并發(fā)服務器第四頁,共36頁?!斑M程”基本概念進程定義了一個計算的基本單元,可以認為是一個程序的一次運行。它是一個動態(tài)實體,是獨立的任務。它擁有獨立的地址空間、執(zhí)行堆棧、文件描述符等。每個進程擁有獨立的地址空間,進程間正常情況下,互不影響,一個進程的崩潰不會造成其他進程的崩潰。當進程間共享某一資源時,需注意兩個問題:同步問題和通信問題。第五頁,共36頁。創(chuàng)建進程#include<sys/types.h>#include<unistd.h>pid_tfork(void)返回:父進程中返回子進程的進程ID,子進程返回0,-1-出錯fork后,子進程和父進程繼續(xù)執(zhí)行fork()函數(shù)后的指令。子進程是父進程的副本。子進程擁有父進程的數(shù)據(jù)空間、堆棧的副本。但父、子進程并不共享這些存儲空間部分。如果代碼段是只讀的,則父子進程共享代碼段。如果父子進程同時對同一文件描述字操作,而又沒有任何形式的同步,則會出現(xiàn)混亂的狀況;父進程中調用fork之前打開的所有描述字在函數(shù)fork返回之后子進程會得到一個副本。fork后,父子進程均需要將自己不使用的描述字關閉。第六頁,共36頁。創(chuàng)建進程(cont.)#include<sys/types.h>#include<unistd.h>pid_tvfork(void);該系統(tǒng)調用基本上與fork相同,在BSD3.0中開始出現(xiàn),主要為了解決fork昂貴的開銷。兩者的基本區(qū)別在于當使用vfork()創(chuàng)建新進程時,父進程將被暫時阻塞,而子進程則可以借用父進程的地址空間,直到子進程退出,至此父進程才繼續(xù)執(zhí)行。第七頁,共36頁。終止進程進程的終止存在兩個可能:父進程先于子進程終止(init進程領養(yǎng))子進程先于主進程終止對于后者,系統(tǒng)內核為子進程保留一定的狀態(tài)信息:進程ID、終止狀態(tài)、CPU時間等;當父進程調用wait或waitpid函數(shù)時,獲取這些信息;(什么叫“僵尸進程”?)當子進程正?;虍惓=K止時,系統(tǒng)內核向其父進程發(fā)送SIGCHLD信號;缺省情況下,父進程忽略該信號,或者提供一個該信號發(fā)生時即被調用的函數(shù)。第八頁,共36頁。終止進程(續(xù))#include<stdlib.h>voidexit(intstatus);本函數(shù)終止調用進程。關閉所有子進程打開的描述符,向父進程發(fā)送SIGCHLD信號,并返回狀態(tài)。第九頁,共36頁。獲取子進程終止信息#include<sys/types.h>#include<sys/wait.h>pid_twait(int*stat_loc);返回:終止子進程的ID-成功;-1-出錯;stat_loc存儲子進程的終止狀態(tài)(一個整數(shù));如果沒有終止的子進程,但是有一個或多個正在執(zhí)行的子進程,則該函數(shù)將堵塞,直到有一個子進程終止或者wait被信號中斷時,wait返回。當調用該系統(tǒng)調用時,如果有一個子進程已經終止,則該系統(tǒng)調用立即返回,并釋放子進程所有資源。第十頁,共36頁。獲取子進程終止信息使用wait()函數(shù)可能會出現(xiàn)一個問題SIGCHLD服務器父進程服務器子進程服務器子進程服務器子進程客戶FINFINFINSIGCHLDSIGCHLD由于linux信號不排隊,在SIGCHLD信號同時到來后,信號處理程序中調用了wait函數(shù),其只執(zhí)行一次,這樣將留下2個僵尸進程??梢允褂脀aitpid函數(shù)解決這個問題。第十一頁,共36頁。獲取子進程終止信息pid_twaitpid(pid_tpid,int*stat_loc,intoptions);返回:終止子進程的ID-成功;-1-出錯;stat_loc存儲子進程的終止狀態(tài);當pid=-1,option=0時,該函數(shù)等同于wait,否則由參數(shù)pid和option共同決定函數(shù)行為,其中pid參數(shù)意義如下:-1:要求知道任何一個子進程的返回狀態(tài)(等待第一個終止的子進程);>0:要求知道進程號為pid的子進程的狀態(tài);<-1:要求知道進程號為pid的絕對值的子進程的終止狀態(tài)Options最常用的選項是WNOHANG,它通知內核在沒有已終止進程時不要堵塞。第十二頁,共36頁。獲取子進程終止信息(cont.)調用wait或waitpid函數(shù)時,正常情況下,可能會有以下幾種情況:阻塞(如果其所有子進程都還在運行);獲得子進程的終止狀態(tài)并立即返回(如果一個子進程已終止,正等待父進程存取其終止狀態(tài));出錯立即返回(如果它沒有任何子進程)第十三頁,共36頁。多進程并發(fā)服務器狀態(tài)圖服務器客戶connect()函數(shù)listenfd客戶/服務器狀態(tài)圖(調用accept函數(shù)時)連接請求第十四頁,共36頁。多進程并發(fā)服務器狀態(tài)圖(cont.)服務器客戶connect()函數(shù)listenfd客戶/服務器狀態(tài)圖(調用accept函數(shù)后)connfd連接建立第十五頁,共36頁。多進程并發(fā)服務器狀態(tài)圖(cont.)服務器(父進程)客戶connect()函數(shù)listenfd客戶/服務器狀態(tài)圖(調用fork函數(shù)后)connfd連接建立服務器(子進程)listenfdconnfdfork()函數(shù)第十六頁,共36頁。多進程并發(fā)服務器狀態(tài)圖(cont.)服務器(父進程)客戶connect()函數(shù)listenfd客戶/服務器狀態(tài)圖(父進程關閉連接套接字,子進程關閉監(jiān)聽套接字)連接建立服務器(子進程)connfd第十七頁,共36頁。多進程并發(fā)服務器實例該實例包括服務器程序和客戶程序,具體功能如下:服務器等待接收客戶的連接請求,一旦連接成功則顯示客戶地址,接著接收客戶端的名稱并顯示;然后接收來自該客戶的字符串,每當收到一個字符串時,顯示該字符串,并將字符串按照愷撒密碼的加密方式(K=3)進行加密,再將加密后的字符發(fā)回客戶端;之后,繼續(xù)等待接收該客戶的信息,直到客戶關閉連接。要求服務器具有同時處理多個客戶請求的能力??蛻羰紫扰c相應的服務器建立連接;接著接收用戶輸入的客戶端名稱,并將其發(fā)送給服務器;然后繼續(xù)接收用戶輸入的字符串,再將字符串發(fā)送給服務器,同時接收服務器發(fā)回的加密后的字符串并顯示。之后,繼續(xù)等待用戶輸入字符串,直到用戶輸入Ctrl+D,客戶關閉連接并退出。

第十八頁,共36頁。多進程服務器的問題雖然多進程并發(fā)服務器模式很多年來都使用得很好,但使用fork生成子進程存在一些問題。首先,fork占用大量的資源,內存映像要從父進程拷貝到子進程,所有描述符要在子進程中復制等。雖然當前采用寫時拷貝(copy-on-write)技術,將真正的拷貝推遲到子進程有寫操作時,但fork仍然需要占用大量資源。其次,fork子進程后,需要用進程間通信(IPC)在父子進程間傳遞信息。由于子進程從一開始就有父進程數(shù)據(jù)空間及所有描述符的拷貝,所以fork之前的信息容易傳遞,但是從子進程返回信息給父進程就需要做很多工作。

第十九頁,共36頁?!熬€程”基本概念線程是進程內的獨立執(zhí)行實體和調度單元,又稱為“輕量級”進程(lightwightprocess);創(chuàng)建線程比進程快10~100倍。一個進程內的所有線程共享相同的內存空間、全局變量等信息(這種機制又帶來了同步問題)。而且它們還共享以下信息:

共享信息私有信息進程指令 線程ID大多數(shù)數(shù)據(jù) 寄存器集合(包括程序計數(shù)器和棧指針)打開的文件描述字 棧(用于存放局部變量)信號處理程序和信號處置 error當前工作目錄 信號掩碼用戶ID和組ID 優(yōu)先級第二十頁,共36頁。線程調用函數(shù)(1)#include<pthread.h>intpthread_create(pthread_t*tid,constpthread_attr_t*attr,void*(*func)(void*),void*arg);返回:成功時為0;出錯時為正的Exxx值當一個程序開始運行時,系統(tǒng)會創(chuàng)建一個初始線程或主線程的單個線程。額外線程由上述函數(shù)創(chuàng)建;新線程由線程id標識:tid,新線程的屬性attr包括:優(yōu)先級、初始棧大小、是否應該是守護線程等等。線程的執(zhí)行函數(shù)和調用參數(shù)分別是:func和arg;由于線程的執(zhí)行函數(shù)的參數(shù)和返回值類型均為void*,因此可傳遞和返回指向任何類型的指針;常見的返回錯誤值:EAGAIN:超過了系統(tǒng)線程數(shù)目的限制。ENOMEN:沒有足夠的內存產生新的線程。EINVAL:無效的屬性attr值。第二十一頁,共36頁。線程函數(shù)調用(2)#inlcude<pthread.h>intpthread_join(pthread_ttid,void**status);返回:成功時為0;出錯時為正的Exxx值,不設置error該函數(shù)類似與waitpid函數(shù),但必須指定等待線程的ID,該函數(shù)不能等待任意一個線程結束(如wait);被等待線程必須是當前進程的成員,并且不是分離的線程和守護線程。pthread_tpthread_self(void);返回:調用線程的線程id;第二十二頁,共36頁。線程函數(shù)調用(3)#include<pthread.h>intpthread_detach(pthread_ttid)返回:成功時為0;出錯時為正Exxx值;線程或者是可匯合的(joinable)(默認),或者是脫離的(detached)。當可匯合的線程終止時,其線程id和退出狀態(tài)將保留,直到另外一個線程調用pthread_join。脫離的線程則像守護進程,當它終止時,釋放所有資源,我們不能等待它終止。該函數(shù)將指定的線程變?yōu)槊撾x的。pthread_detach(pthread_self());第二十三頁,共36頁。線程函數(shù)調用(4)#include<pthread.h>voidpthread_exit(void*status);無返回值;如果線程為可匯合的,將保留線程id和退出狀態(tài)供pthread_join()函數(shù)調用;指針status:指向線程的退出狀態(tài)。不能指向一個局部變量,因為線程終止時其所有的局部變量將被撤銷;還有其他兩種方法可使線程終止啟動線程的函數(shù)(pthread_create的第3個參數(shù))返回。其返回值便是線程的終止狀態(tài);如果進程的main函數(shù)返回,或者當前進程中,任一線程調用了exit()函數(shù),將終止該進程中所有線程。第二十四頁,共36頁。給新線程傳遞參數(shù)由于同一個進程內的所有線程共享內存和變量,因此在傳遞參數(shù)時需作特殊處理,下面參考如下幾種方法:傳遞參數(shù)的普通方法通過指針傳遞參數(shù)通過分配arg的空間來傳遞參數(shù)第二十五頁,共36頁。傳遞參數(shù)的普通方法由于線程創(chuàng)建函數(shù)只允許傳遞一個參數(shù),因此當需要傳遞多個數(shù)據(jù)時,應首先將這些數(shù)據(jù)封裝在一個結構中。void*start_routine(void*arg);structARG{ intconnfd; intother;}intmain(){ARGarg; …While(1){if((connfd=accept(sockfd,NULL,NULL))==-1){…}arg.connfd=connfd; pthread_create(&tid,NULL,start_routine,(void*)&arg); …}}void*start_routine(void*arg){ ARGinfo; info.connfd=((ARG*)arg)->connfd; info.other=((ARG*)arg)->other; …}//這種方法有問題,對一個客戶可以工作,但多個客戶則可能出現(xiàn)問題。

第二十六頁,共36頁。通過指針傳遞參數(shù)這種方法首先將要傳遞的數(shù)據(jù)轉換成通用指針類型,然后傳遞給新線程,新線程再將其還原成原數(shù)據(jù)類型:void*start_routine(void*arg);intmain(void){ intconnfd; … pthread_create(&tid,NULL,start_routine,(void*)connfd); …}void*start_routine(void*arg){ intconnfd; connfd=(int)arg; …}這種方法雖然簡單,但卻有很大的局限性。如:要求arg的類型必須能被正確地轉換成通用指針類型,而且可傳遞的參數(shù)只有一個。第二十七頁,共36頁。通過分配arg的空間來傳遞主線程首先為每個新線程分配存儲arg的空間,再將arg傳遞給新線程使用,新線程使用完后要釋放該空間。void*start_routine(void*arg);intmain(void){ ARG*arg; intconnfd;… loop{ …if((connfd=accept(sockfd,NULL,NULL))==-1){…} arg=newARG; arg->connfd=connfd; pthread_create(&tid,NULL,start_routine,(void*)arg); … }}第二十八頁,共36頁。線程安全問題線程安全問題是一個非常復雜的問題。簡單地說,就是多個線程在操作共享數(shù)據(jù)時出現(xiàn)的混亂情況,這種情況可能導致不可預測的后果。解決線程安全問題的方法主要有兩種:一是使用線程安全函數(shù):posix定義的以”_r”結尾的函數(shù),二是使用線程專用數(shù)據(jù)(TSD)。第二十九頁,共36頁。線程專用數(shù)據(jù):TSD從上例可以看出,在多線程環(huán)境中,應避免使用靜態(tài)變量。在linux環(huán)境中,用線程專用數(shù)據(jù)TSD取代靜態(tài)變量。它類似于全局數(shù)據(jù),只不過它是線程私有的,是以線程為界限的。TSD是定義線程私有全局數(shù)據(jù)的唯一方法。每個TSD由進程內唯一的關鍵字(key)來標志,用這個關鍵字,線程可以存取線程私有的數(shù)據(jù)。第三十頁,共36頁。pthread_key_create函數(shù)#include<pthread.h>intpthread_key_create(pthread_key_t*key,void(*destructor)(void*value));返回值:正常執(zhí)行后返回0,否則返回錯誤碼該函數(shù)在進程內部分配一個標志TSD的關鍵字,關鍵字是進程內部唯一的,所有線程在創(chuàng)建時關鍵字值是NULL。key指向創(chuàng)建的關鍵字;destructor是一個可選的析構函數(shù),用于每個線程終止時調用該析構函數(shù)。第三十一頁,共36頁。pthread_setspecific函數(shù)#include<pthread.h>intpthread_setspecific(pthread_key_tkey,constvoid*value);

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經權益所有人同意不得將文件中的內容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
  • 6. 下載文件中如有侵權或不適當內容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論