




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
第七講
守護(hù)進(jìn)程與其他網(wǎng)絡(luò)服務(wù)器編程技術(shù)任立勇電子科技大學(xué)計(jì)算機(jī)學(xué)院2/5/20231目錄守護(hù)進(jìn)程和inetd超級(jí)服務(wù)器概述syslogd守護(hù)進(jìn)程和syslog函數(shù)創(chuàng)建守護(hù)進(jìn)程inetd守護(hù)進(jìn)程幾種服務(wù)器技術(shù)的比較;tcp并發(fā)服務(wù)器-每個(gè)客戶一個(gè)子進(jìn)程;tcp預(yù)先派生子進(jìn)程服務(wù)器程序,accept無上鎖保護(hù);tcp預(yù)先派生子進(jìn)程服務(wù)器程序,accept使用文件鎖保護(hù);tcp預(yù)先派生子進(jìn)程服務(wù)器程序,accept使用線程互斥鎖保護(hù);tcp預(yù)先派生子進(jìn)程服務(wù)器程序,傳遞描述字;tcp并發(fā)服務(wù)器程序,每個(gè)客戶一個(gè)線程;tcp預(yù)先創(chuàng)建線程服務(wù)器程序,每個(gè)線程各自accept;tcp預(yù)先創(chuàng)建線程服務(wù)器程序,主線程統(tǒng)一accept;2Networkprogramming‘03守護(hù)進(jìn)程概述守護(hù)進(jìn)程是在后臺(tái)運(yùn)行不受終端控制的進(jìn)程(如輸入、輸出等),一般的網(wǎng)絡(luò)服務(wù)都是以守護(hù)進(jìn)程的方式運(yùn)行。守護(hù)進(jìn)程脫離終端的主要原因有兩點(diǎn):用來啟動(dòng)守護(hù)進(jìn)程的終端在啟動(dòng)守護(hù)進(jìn)程之后,需要執(zhí)行其他任務(wù)。(如其他用戶登錄該終端后,以前的守護(hù)進(jìn)程的錯(cuò)誤信息不應(yīng)出現(xiàn))由終端上的一些鍵所產(chǎn)生的信號(hào)(如中斷信號(hào)),不應(yīng)對(duì)以前從該終端上啟動(dòng)的任何守護(hù)進(jìn)程造成影響。要注意守護(hù)進(jìn)程與后臺(tái)運(yùn)行程序(即加&啟動(dòng)的程序)的區(qū)別。3Networkprogramming‘03啟動(dòng)守護(hù)進(jìn)程的方法在系統(tǒng)啟動(dòng)是由系統(tǒng)初始化腳本啟動(dòng),這些腳本一般在/etc或/etc/rc開頭的目錄。如inet超級(jí)服務(wù)器,web服務(wù)器等;許多網(wǎng)絡(luò)服務(wù)器是由inet超級(jí)服務(wù)器啟動(dòng)的,如Telnetd、FTPd等;cron守護(hù)進(jìn)程按一定的規(guī)則執(zhí)行一些程序,由它啟動(dòng)的程序也以守護(hù)進(jìn)程的方式運(yùn)行。守護(hù)進(jìn)程可以在用戶終端上啟動(dòng),這是測(cè)試守護(hù)進(jìn)程或重新啟動(dòng)守護(hù)進(jìn)程常用的方法。4Networkprogramming‘03用戶守護(hù)進(jìn)程登記出錯(cuò)信息創(chuàng)建一個(gè)Unix域數(shù)據(jù)報(bào)套接口,并向syslogd守護(hù)進(jìn)程綁定的路徑名發(fā)送我們的消息,我們就能從自己的守護(hù)進(jìn)程向syslogd發(fā)送登記信息??梢詣?chuàng)建一個(gè)UDP套接口,將日志消息發(fā)到回饋地址及端口號(hào)514;更簡(jiǎn)單的方法是利用syslog函數(shù);5Networkprogramming‘03syslogd守護(hù)進(jìn)程syslogd是一個(gè)系統(tǒng)守護(hù)進(jìn)程,它主要負(fù)責(zé)接收系統(tǒng)或用戶守護(hù)進(jìn)程的輸出消息,并根據(jù)配置信息作出相應(yīng)處理。syslogd在啟動(dòng)時(shí)執(zhí)行以下操作:讀入配置文件,通常是/etc/syslogd.conf,它設(shè)定守護(hù)進(jìn)程對(duì)接收的各種登記消息如何處理。這些消息可能被寫入一個(gè)文件(一種特殊文件是/dev/console,這將把消息寫到控制臺(tái)上)。或發(fā)給指定的用戶,或轉(zhuǎn)發(fā)給另一臺(tái)主機(jī)上的syslogd進(jìn)程。創(chuàng)建一個(gè)Unix域套接字,給它捆綁路徑名/var/run/log創(chuàng)建一個(gè)udp套接字,給它捆綁端口514打開路徑名/dev/klog,內(nèi)核中的所有出錯(cuò)消息作為這個(gè)設(shè)備的輸入出現(xiàn);在此之后syslogd進(jìn)程運(yùn)行一個(gè)無限循環(huán),循環(huán)中調(diào)用select,等待三個(gè)描述字(以上2、3、4創(chuàng)建)之一變?yōu)榭勺x,并按配置文件對(duì)消息進(jìn)行處理6Networkprogramming‘03openlog函數(shù)voidopenlog(constchar*ident,intoption,intfacility);voidcloselog(void);openlog函數(shù)在第一次調(diào)用syslog函數(shù)之前調(diào)用,當(dāng)不再需要發(fā)生登記消息時(shí)可調(diào)用closelog函數(shù);ident是一個(gè)字符串,它將被加到每條登記消息前面;option參數(shù)由下頁圖中的值組合而成。facility參數(shù)為后面沒有設(shè)置設(shè)施的syslog調(diào)用設(shè)置一個(gè)缺省值。7Networkprogramming‘03openlog的選項(xiàng)選項(xiàng)(options) 描述LOG_CONS 如果不能發(fā)往syslogd守護(hù)進(jìn)程,則登記到 控制臺(tái)上LOG_NDELAY 不延遲打開,立即創(chuàng)建套接口LOG_PERROR 既發(fā)往syslogd守護(hù)進(jìn)程,又登記到 標(biāo)準(zhǔn)錯(cuò)誤輸出LOG_PID 登記每條消息的進(jìn)程ID8Networkprogramming‘03syslog函數(shù)#include<syslog.h>voidsyslog(intpriority,constchar*message,…);參數(shù)message與printf所用的格式化字符串類似,同時(shí)增加了%m,它將由對(duì)應(yīng)的當(dāng)前errno值的出錯(cuò)消息所取代;參數(shù)priority是級(jí)別(level)和設(shè)施(facility)的組合。設(shè)施和級(jí)別的目的是,允許在/etc/syslog.conf文件中進(jìn)行配置,使得對(duì)相同設(shè)施的消息得到同樣的處理,或使相同級(jí)別的消息得到同樣的處理。9Networkprogramming‘03登記消息的級(jí)別級(jí)別(level) 值描述LOG_EMERG 0系統(tǒng)不可用(優(yōu)先級(jí)最高)LOG_ALERT 1必須立即進(jìn)行處理LOG_CRIT 2危險(xiǎn)情況LOG_ERR 3出錯(cuò)情況LOG_WARNING 4警告性情況LOG_NOTICE 5常見但值得注意的情況(缺省)LOG_INFO 6通告消息LOG_DEBUG 7調(diào)試消息(優(yōu)先級(jí)最低)10Networkprogramming‘03登記消息的設(shè)施設(shè)施(facility) 描述 設(shè)施(facility)描述LOG_AUTH 安全/授權(quán)消息 LOG_LOCAL6 本地使用LOG_AUTHPRIV 安全/授權(quán)消息(私有) LOG_LOCAL7 本地使用LOG_CRON cron守護(hù)進(jìn)程 LOG_LPR 行式打印機(jī)系統(tǒng)LOG_DAEMON 系統(tǒng)守護(hù)進(jìn)程 LOG_MAIL 郵件系統(tǒng)LOG_FTP FTP守護(hù)進(jìn)程 LOG_NEWS 網(wǎng)絡(luò)新聞系統(tǒng)LOG_KERN 內(nèi)核消息 LOG_SYSLOG 由syslogd內(nèi)部消息LOG_LOCAL0 本地使用 LOG_USER任意的用戶消息(缺省LOG_LOCAL1 本地使用 LOG_UUCP UUCP消息LOG_LOCAL2 本地使用LOG_LOCAL3 本地使用LOG_LOCAL4 本地使用LOG_LOCAL5 本地使用11Networkprogramming‘03syslog函數(shù)的例子當(dāng)調(diào)用rename函數(shù)失敗時(shí),守護(hù)進(jìn)程可能會(huì)調(diào)用:if(rename(file1,file2)==-1)syslog(LOG_INFO|LOG_LOCAL2,”rename(%s,%s):%m”,file1,file2);如果配置文件中有以下兩行:kern* /dev/consolelocal2.debug /var/log/cisco.log /var/log/info.log則指定內(nèi)核的所有消息登記到控制臺(tái)上,所有設(shè)施為local2的調(diào)試消息將添加到/var/log/cisco文件的末尾;12Networkprogramming‘03setsid()函數(shù)#include<sys/types.h>#include<unistd.h>pid_tsetsid(void);返回值:若成功則為進(jìn)程組ID,出錯(cuò)則為-1該函數(shù)是實(shí)現(xiàn)守護(hù)進(jìn)程必須調(diào)用和非常重要的函數(shù)。如果調(diào)用進(jìn)程不是一個(gè)進(jìn)程組的組長(zhǎng),則此函數(shù)創(chuàng)建一個(gè)新的會(huì)話:此進(jìn)程變成該會(huì)話的首進(jìn)程,同時(shí)是該會(huì)話的唯一進(jìn)程;此進(jìn)程成為一個(gè)新進(jìn)程組的組長(zhǎng)進(jìn)程。新進(jìn)程組ID是此調(diào)用進(jìn)程的進(jìn)程ID;此進(jìn)程沒有控制終端,如果在調(diào)用setsid之前此進(jìn)程有一個(gè)控制終端,那么這種聯(lián)系也被解除。如果調(diào)用進(jìn)程已經(jīng)是一個(gè)進(jìn)程組長(zhǎng),則函數(shù)出錯(cuò)。為了保證不處于這種情況,通常首先調(diào)用fork,然后使父進(jìn)程終止。13Networkprogramming‘03創(chuàng)建守護(hù)進(jìn)程#include <syslog.h>#define MAXFD 64externint daemon_proc; /*definedinerror.c*/voiddaemon_init(constchar*pname,intfacility){ int i; pid_t pid; if((pid=Fork())!=0) exit(0); /*parentterminates*/ /*1stchildcontinues*/
setsid(); /*becomesessionleader*/調(diào)試目的14Networkprogramming‘03創(chuàng)建守護(hù)進(jìn)程(續(xù)) Signal(SIGHUP,SIG_IGN); if((pid=Fork())!=0) exit(0); /*1stchildterminates*/ /*2ndchildcontinues*/ daemon_proc=1; /*forourerr_XXX()functions*/ chdir("/"); /*changeworkingdirectory*/ umask(0); /*clearourfilemodecreationmask*/ for(i=0;i<MAXFD;i++) close(i); openlog(pname,LOG_PID,facility);}15Networkprogramming‘03程序說明第一次調(diào)用fork的目的是保證調(diào)用setsid的調(diào)用進(jìn)程不是進(jìn)程組長(zhǎng)。(而setsid函數(shù)是實(shí)現(xiàn)與控制終端脫離的唯一方法);setsid函數(shù)使進(jìn)程成為新會(huì)話的會(huì)話頭和進(jìn)程組長(zhǎng),并與控制終端斷開連接;第二次調(diào)用fork的目的是:即使守護(hù)進(jìn)程將來打開一個(gè)終端設(shè)備,也不會(huì)自動(dòng)獲得控制終端。(因?yàn)樵赟VR4中,當(dāng)沒有控制終端的會(huì)話頭進(jìn)程打開終端設(shè)備時(shí),如果這個(gè)終端不是其他會(huì)話的控制終端,該終端將自動(dòng)成為這個(gè)會(huì)話的控制終端),這樣可以保證這次生成的進(jìn)程不再是一個(gè)會(huì)話頭。忽略SIGHUP信號(hào)的原因是,當(dāng)?shù)谝淮紊傻淖舆M(jìn)程(會(huì)話頭)終止時(shí),該會(huì)話中的所有進(jìn)程(第二次生成的子進(jìn)程)都會(huì)收到該信號(hào);16Networkprogramming‘03守護(hù)進(jìn)程方式運(yùn)行的時(shí)間服務(wù)器#include <time.h>intmain(intargc,char**argv){ int listenfd,connfd; socklen_t addrlen,len; structsockaddr *cliaddr; char buff[MAXLINE]; time_t ticks; daemon_init(argv[0],0); if(argc==2) listenfd=Tcp_listen(NULL,argv[1],&addrlen); elseif(argc==3) listenfd=Tcp_listen(argv[1],argv[2],&addrlen); 17Networkprogramming‘03時(shí)間服務(wù)器 else err_quit("usage:daytimetcpsrv2[<host>]<serviceorport>"); cliaddr=Malloc(addrlen); for(;;){ len=addrlen; connfd=Accept(listenfd,cliaddr,&len); err_msg("connectionfrom%s",Sock_ntop(cliaddr,len)); ticks=time(NULL); snprintf(buff,sizeof(buff),"%.24s\r\n",ctime(&ticks)); Write(connfd,buff,strlen(buff)); Close(connfd); }}18Networkprogramming‘03編寫守護(hù)進(jìn)程的注意事項(xiàng)當(dāng)程序開始時(shí),盡快調(diào)用daemon_init,使之變成守護(hù)進(jìn)程,否則容易受控制終端影響;守護(hù)進(jìn)程必須避免調(diào)用printf和fprintf函數(shù),而調(diào)用syslog函數(shù)。19Networkprogramming‘03配置守護(hù)進(jìn)程由于守護(hù)進(jìn)程沒有控制終端,因而無法通過用戶輸入來進(jìn)行相應(yīng)配置,在編程時(shí)通常采用以下幾種方法來配置守護(hù)進(jìn)程:配置文件:將所有的配置參數(shù)存入一個(gè)配置文件,當(dāng)守護(hù)進(jìn)程啟動(dòng)時(shí)可以自動(dòng)讀取配置信息進(jìn)行配置;環(huán)境變量:守護(hù)進(jìn)程通過讀取環(huán)境變量而獲得配置信息同樣,因?yàn)槭刈o(hù)進(jìn)程運(yùn)行時(shí)沒有控制終端,所以它不會(huì)收到來自內(nèi)核的SIGHUP信號(hào),因此很多守護(hù)進(jìn)程將該信號(hào)作為管理員通知其配置文件已修改之用。守護(hù)進(jìn)程收到該信號(hào)后應(yīng)重新讀入配置文件。另外兩個(gè)守護(hù)進(jìn)程不應(yīng)收到的信號(hào)是SIGINT和SIGWINCH,這些信號(hào)也可以作為通知用。20Networkprogramming‘03inetd:超級(jí)網(wǎng)絡(luò)服務(wù)器4.3BSD以前的版本中的每個(gè)網(wǎng)絡(luò)服務(wù)有一個(gè)與之對(duì)應(yīng)的進(jìn)程,它們的啟動(dòng)幾乎完全一樣(如創(chuàng)建套接字,綁定地址,監(jiān)聽端口…),這種模型存在以下問題:這些守護(hù)進(jìn)程有幾乎相同的代碼,首先是創(chuàng)建套接字,還要考慮變成守護(hù)進(jìn)程;每個(gè)守護(hù)進(jìn)程在進(jìn)程表中要占一項(xiàng),但它們?cè)诖蠖鄶?shù)時(shí)間處于睡眠狀態(tài);21Networkprogramming‘03inetd:超級(jí)網(wǎng)絡(luò)服務(wù)器(續(xù))4.3BSD通過一個(gè)網(wǎng)絡(luò)超級(jí)服務(wù)器inet守護(hù)進(jìn)程簡(jiǎn)化了上述問題:大部分啟動(dòng)時(shí)要做的工作由inetd處理,所有守護(hù)進(jìn)程的編寫得到簡(jiǎn)化。這避免了每個(gè)服務(wù)器程序都要調(diào)用daemon_init函數(shù);單個(gè)進(jìn)程(inetd)能為多個(gè)服務(wù)等待客戶的請(qǐng)求,取代了每個(gè)服務(wù)一個(gè)進(jìn)程的方式,這樣減少了系統(tǒng)中的進(jìn)程數(shù);22Networkprogramming‘03inetd守護(hù)進(jìn)程的工作流程啟動(dòng)時(shí)讀取/etc/inetd.conf文件并給文件中指定的所有服務(wù)創(chuàng)建一個(gè)相應(yīng)類型的套接字,inetd能處理的服務(wù)器數(shù)目依賴于它最多能創(chuàng)建的描述字的數(shù)目。每個(gè)創(chuàng)建的套接字都被加入到select調(diào)用的描述字集中;為每個(gè)套接字調(diào)用bind,給它們捆綁服務(wù)器的眾所周知端口和通配地址。對(duì)tcp套接字調(diào)用listen,以接受外來的連接請(qǐng)求;所有套接字建立后,調(diào)用select等待這些套接字變?yōu)榭勺x;Select返回一個(gè)可讀的套接字后,如果是一個(gè)tcp套接字,就調(diào)用accept接受這個(gè)新的連接;inetd守護(hù)進(jìn)程fork,由子進(jìn)程處理服務(wù)請(qǐng)求。子進(jìn)程關(guān)閉除連接套接字以外的所有描述字;如果是tcp套接字,父進(jìn)程必須關(guān)閉連接套接字。23Networkprogramming‘03inetd守護(hù)進(jìn)程的工作流程socket()bind()listen()如果是tcp套接字select()等待可讀條件accept()如果是tcp套接字fork()close已連接套接字(如果是tcp)close已連接套接字之外的所有描述字將套接字描述字dup到描述字0、1、2,然后close原來的套接字setgid()setuid()(如果不是root)exec()服務(wù)程序父進(jìn)程子進(jìn)程對(duì)每個(gè)在/etc/inetd.conf文件中列出的每個(gè)服務(wù)24Networkprogramming‘03inetd的配置文件ftpstreamtcpnowaitroot/usr/bin/ftpdftpd–ltelnetstreamtcpnowaitroot/usr/bin/telnetdtelnetdloginstreamtcpnowaitroot/usr/bin/rlogindrlogind–stftpstreamudpwaitroot/usr/bin/tftpdtftpd–s/tftpboot25Networkprogramming‘03inetd的等待方式對(duì)tcp服務(wù)器設(shè)置nowait方式,意味著inetd在接受請(qǐng)求同一服務(wù)的其他連接之前不需要等待該服務(wù)的子進(jìn)程終止。給數(shù)據(jù)報(bào)服務(wù)設(shè)置wait標(biāo)志,需要對(duì)父進(jìn)程的操作步驟作一定的修改。即inetd在該UDP套接字上再次選擇之前,必須等待在該套接字上服務(wù)的子進(jìn)程終止:父進(jìn)程中的fork返回時(shí),記錄子進(jìn)程的進(jìn)程號(hào),以便父進(jìn)程可以用waitpid等待它終止;父進(jìn)程用FD_CLR宏關(guān)閉select使用的描述字集中與該這個(gè)套接字相關(guān)的位。當(dāng)子進(jìn)程終止時(shí),父進(jìn)程收到一個(gè)SIGCHLD信號(hào),父進(jìn)程的信號(hào)處理程序得到終止子進(jìn)程的進(jìn)程號(hào),父進(jìn)程通過打開描述字集中相應(yīng)的位恢復(fù)對(duì)該套接字的select。父進(jìn)程需要等待數(shù)據(jù)報(bào)服務(wù)的子進(jìn)程終止是因?yàn)椋阂粋€(gè)數(shù)據(jù)報(bào)服務(wù)只有一個(gè)套接字,如果不關(guān)閉相應(yīng)的位,則隨后到來的數(shù)據(jù)將會(huì)使select返回可讀條件,從而fork一個(gè)錯(cuò)誤的子進(jìn)程。26Networkprogramming‘03常見的網(wǎng)絡(luò)服務(wù)器模型最簡(jiǎn)單的迭代服務(wù)器模型并發(fā)服務(wù)器(服務(wù)器為每個(gè)客戶創(chuàng)建一個(gè)新進(jìn)程)并發(fā)服務(wù)器(服務(wù)器為每個(gè)客戶創(chuàng)建一個(gè)新線程)在一個(gè)進(jìn)程內(nèi)select多個(gè)客戶本講將介紹兩種新的并發(fā)服務(wù)器程序設(shè)計(jì)方法預(yù)先派生子進(jìn)程預(yù)先派生線程27Networkprogramming‘03存在的問題對(duì)于預(yù)先創(chuàng)建進(jìn)程或線程的服務(wù)器技術(shù),客戶程序一般不作特殊處理,因?yàn)楹苌儆羞M(jìn)程控制問題。但對(duì)服務(wù)器而言,則存在許多新的問題,如:如果池中的進(jìn)程或線程不夠怎么辦?過多又怎么辦?父子進(jìn)程、父子線程,以及進(jìn)程之間、線程之間如何同步?28Networkprogramming‘03試驗(yàn)說明我們針對(duì)每個(gè)服務(wù)器運(yùn)行同一客戶程序的多個(gè)實(shí)例,測(cè)量服務(wù)固定數(shù)目的客戶請(qǐng)求所需的CPU時(shí)間,而迭代服務(wù)器是我們的基準(zhǔn),我們把它從實(shí)際的CPU時(shí)間中減去就得到用于進(jìn)程控制那部分CPU時(shí)間,因?yàn)榈?wù)器沒有進(jìn)程控制開銷。所有數(shù)據(jù)都是在與服務(wù)器主機(jī)處于同一子網(wǎng)的兩臺(tái)不同主機(jī)上運(yùn)行同一客戶程序。每個(gè)客戶派生5個(gè)子進(jìn)程,對(duì)服務(wù)器開5個(gè)連接,因此服務(wù)器在任意時(shí)刻最多有10個(gè)連接。每個(gè)客戶請(qǐng)求從服務(wù)器返回4000個(gè)字節(jié)的數(shù)據(jù)量。預(yù)先派生子進(jìn)程或線程個(gè)數(shù)為15個(gè)。29Networkprogramming‘03各種類型的服務(wù)器的耗時(shí)比較編 服務(wù)器描述 進(jìn)程控制CPU時(shí)間(與基準(zhǔn)之差)號(hào) solarisDUinxBSD/OS0 迭代服務(wù)器(測(cè)量基準(zhǔn),無進(jìn)程控制) 0.0 0.00.01 簡(jiǎn)單并發(fā)服務(wù)器,為每個(gè)客戶請(qǐng)求fork一個(gè)進(jìn)程504.2168.929.62 預(yù)先派生子進(jìn)程,每個(gè)子進(jìn)程調(diào)用accept 6.2 1.83 預(yù)先派生子進(jìn)程,用文件上鎖方式保護(hù)accept25.210.02.74 預(yù)先派生子進(jìn)程,用線程互斥鎖保護(hù)accept 21,5 預(yù)先派生子進(jìn)程,由父進(jìn)程向子進(jìn)程傳遞套接36.710.96.1
字描述字6 并發(fā)服務(wù)器,為每個(gè)客戶請(qǐng)求創(chuàng)建一個(gè)線程 18.7 4.77 預(yù)先派生子線程,用互斥鎖上鎖方式保護(hù)accept8.63.58 預(yù)先派生子線程,由主線程調(diào)用accept 14.55.030Networkprogramming‘03#define MAXN 16384 /*max#bytestorequestfromserver*/intmain(intargc,char**argv){ int i,j,fd,nchildren,nloops,nbytes; pid_t pid; ssize_t n; char request[MAXLINE],reply[MAXN]; if(argc!=6) err_quit("usage:client<hostnameorIPaddr><port><#children>" "<#loops/child><#bytes/request>"); nchildren=atoi(argv[3]); nloops=atoi(argv[4]); nbytes=atoi(argv[5]); snprintf(request,sizeof(request),"%d\n",nbytes);/*newlineatend*/ for(i=0;i<nchildren;i++){ if((pid=Fork())==0){ /*child*/TCP測(cè)試用客戶程序31Networkprogramming‘03TCP測(cè)試用客戶程序(續(xù)) for(j=0;j<nloops;j++){ fd=Tcp_connect(argv[1],argv[2]); Write(fd,request,strlen(request)); if((n=Readn(fd,reply,nbytes))!=nbytes) err_quit("serverreturned%dbytes",n); Close(fd);/*TIME_WAITonclient,notserver*/ } printf("child%ddone\n",i); exit(0); } /*parentloopsaroundtofork()again*/ } while(wait(NULL)>0) /*nowparentwaitsforallchildren*/ ; if(errno!=ECHILD) err_sys("waiterror"); exit(0);}32Networkprogramming‘03TCP并發(fā)服務(wù)器:
每個(gè)客戶一個(gè)子進(jìn)程intmain(intargc,char**argv){ int listenfd,connfd; pid_t childpid; void sig_chld(int),sig_int(int),web_child(int); socklen_t clilen,addrlen; structsockaddr *cliaddr; if(argc==2) listenfd=Tcp_listen(NULL,argv[1],&addrlen); elseif(argc==3) listenfd=Tcp_listen(argv[1],argv[2],&addrlen); else err_quit("usage:serv01[<host>]<port#>"); cliaddr=Malloc(addrlen); Signal(SIGCHLD,sig_chld); Signal(SIGINT,sig_int);33Networkprogramming‘03TCP并發(fā)服務(wù)器:
每個(gè)客戶一個(gè)子進(jìn)程(續(xù)) for(;;){ clilen=addrlen; if((connfd=accept(listenfd,cliaddr,&clilen))<0){ if(errno==EINTR) continue; /*backtofor()*/ else err_sys("accepterror"); } if((childpid=Fork())==0){ /*childprocess*/ Close(listenfd); /*closelisteningsocket*/ web_child(connfd); /*processtherequest*/ exit(0); } Close(connfd); /*parentclosesconnectedsocket*/ }}34Networkprogramming‘03TCP并發(fā)服務(wù)器:
每個(gè)客戶一個(gè)子進(jìn)程(續(xù))#define MAXN 16384 /*max#bytesthataclientcanrequest*/voidweb_child(intsockfd){ int ntowrite; ssize_t nread; char line[MAXLINE],result[MAXN]; for(;;){ if((nread=Readline(sockfd,line,MAXLINE))==0) return; /*connectionclosedbyotherend*/ /*4linefromclientspecifies#bytestowriteback*/ ntowrite=atol(line); if((ntowrite<=0)||(ntowrite>MAXN)) err_quit("clientrequestfor%dbytes",ntowrite); Writen(sockfd,result,ntowrite); }}35Networkprogramming‘03TCP并發(fā)服務(wù)器:
每個(gè)客戶一個(gè)子進(jìn)程(續(xù))voidsig_int(intsigno){ void pr_cpu_time(void); pr_cpu_time(); exit(0);}#include <sys/resource.h>#ifndef HAVE_GETRUSAGE_PROTOint getrusage(int,structrusage*);#endifvoidpr_cpu_time(void){ double user,sys; structrusage myusage,childusage;36Networkprogramming‘03TCP并發(fā)服務(wù)器:
每個(gè)客戶一個(gè)子進(jìn)程(續(xù)) if(getrusage(RUSAGE_SELF,&myusage)<0) err_sys("getrusageerror"); if(getrusage(RUSAGE_CHILDREN,&childusage)<0) err_sys("getrusageerror"); user=(double)myusage.ru_utime.tv_sec+ myusage.ru_utime.tv_usec/1000000.0; user+=(double)childusage.ru_utime.tv_sec+ childusage.ru_utime.tv_usec/1000000.0; sys=(double)myusage.ru_stime.tv_sec+ myusage.ru_stime.tv_usec/1000000.0; sys+=(double)childusage.ru_stime.tv_sec+ childusage.ru_stime.tv_usec/1000000.0; printf("\nusertime=%g,systime=%g\n",user,sys);}37Networkprogramming‘03TCP預(yù)先派生子進(jìn)程服務(wù)器程序客戶1客戶2子進(jìn)程1子進(jìn)程2父進(jìn)程子進(jìn)程3…子進(jìn)程N(yùn)可用子進(jìn)程池forkforkforkfork38Networkprogramming‘03TCP預(yù)先派生子進(jìn)程服務(wù)器程序這種技術(shù)的優(yōu)點(diǎn)在于:不需要引入父進(jìn)程執(zhí)行fork的開銷,新客戶就可以得到處理。而缺點(diǎn)在于,每次啟動(dòng)服務(wù)器時(shí),父進(jìn)程必須猜測(cè)到底需要預(yù)先派生多少個(gè)子進(jìn)程。除此以外,如不考慮再派生子進(jìn)程,一旦所有子進(jìn)程都為客戶請(qǐng)求所占用,此時(shí)新的請(qǐng)求將被暫時(shí)忽略,直到有一個(gè)新的進(jìn)程可用??蛻舻母杏X就是服務(wù)器的響應(yīng)變慢。解決上述問題的辦法是:由父進(jìn)程監(jiān)視可用子進(jìn)程個(gè)數(shù),一旦低于某個(gè)閾值,再派生額外的子進(jìn)程,反之,則終止部分新派生的進(jìn)程。39Networkprogramming‘03TCP預(yù)先派生子進(jìn)程服務(wù)器程序:
accept無上鎖保護(hù)staticint nchildren;staticpid_t *pids;intmain(intargc,char**argv){ int listenfd,i; socklen_t addrlen; void sig_int(int); pid_t child_make(int,int,int); if(argc==3) listenfd=Tcp_listen(NULL,argv[1],&addrlen); elseif(argc==4) listenfd=Tcp_listen(argv[1],argv[2],&addrlen); else err_quit("usage:serv02[<host>]<port#><#children>");40Networkprogramming‘03TCP預(yù)先派生子進(jìn)程服務(wù)器程序:
accept無上鎖保護(hù)(續(xù)) nchildren=atoi(argv[argc-1]); pids=Calloc(nchildren,sizeof(pid_t)); for(i=0;i<nchildren;i++) pids[i]=child_make(i,listenfd,addrlen); /*parentreturns*/ Signal(SIGINT,sig_int); for(;;) pause(); /*everythingdonebychildren*/}41Networkprogramming‘03TCP預(yù)先派生子進(jìn)程服務(wù)器程序:
accept無上鎖保護(hù)(續(xù))pid_tchild_make(inti,intlistenfd,intaddrlen){ pid_t pid; void child_main(int,int,int); if((pid=Fork())>0) return(pid); /*parent*/ child_main(i,listenfd,addrlen); /*neverreturns*/}/*endchild_make*/42Networkprogramming‘03TCP預(yù)先派生子進(jìn)程服務(wù)器程序:
accept無上鎖保護(hù)(續(xù))voidchild_main(inti,intlistenfd,intaddrlen){ int connfd; void web_child(int); socklen_t clilen; structsockaddr *cliaddr; cliaddr=Malloc(addrlen); printf("child%ldstarting\n",(long)getpid()); for(;;){ clilen=addrlen; connfd=Accept(listenfd,cliaddr,&clilen); web_child(connfd); /*processtherequest*/ Close(connfd); }}43Networkprogramming‘03TCP預(yù)先派生子進(jìn)程服務(wù)器程序:
accept無上鎖保護(hù)(續(xù))voidsig_int(intsigno){ int i; void pr_cpu_time(void); /*4terminateallchildren*/ for(i=0;i<nchildren;i++) kill(pids[i],SIGTERM); while(wait(NULL)>0) /*waitforallchildren*/ ; if(errno!=ECHILD) err_sys("waiterror"); pr_cpu_time(); exit(0);}44Networkprogramming‘03TCP預(yù)先派生子進(jìn)程服務(wù)器程序:
accept無上鎖保護(hù)(續(xù))父進(jìn)程listenfd子進(jìn)程listenfd子進(jìn)程listenfd子進(jìn)程listenfd…...file{}socket{}45Networkprogramming‘03TCP預(yù)先派生子進(jìn)程服務(wù)器程序:
accept無上鎖保護(hù)(續(xù))當(dāng)程序啟動(dòng)后,N個(gè)子進(jìn)程被派生,它們分別調(diào)用accept并由內(nèi)核置入睡眠狀態(tài)。當(dāng)一個(gè)客戶連接請(qǐng)求到達(dá)時(shí),N個(gè)睡眠進(jìn)程均被喚醒,但只有最先被調(diào)度執(zhí)行的進(jìn)程才能獲得客戶連接,而其他N-1個(gè)進(jìn)程再次睡眠。這種情況稱之為“驚群”問題,它將會(huì)導(dǎo)致系統(tǒng)服務(wù)性能下降。(測(cè)試“驚群”現(xiàn)象)為了解決上述問題,可以增加一個(gè)監(jiān)控進(jìn)程,監(jiān)視空閑子進(jìn)程的數(shù)量。46Networkprogramming‘03TCP預(yù)先派生子進(jìn)程服務(wù)器程序:
accept使用文件鎖保護(hù)源自BSD的內(nèi)核允許多個(gè)進(jìn)程在同一監(jiān)聽描述字上調(diào)用accept,但對(duì)于基于SVR4(如Solaris2.5)則會(huì)在客戶連接到該服務(wù)器后不久,某個(gè)子進(jìn)程的accept就會(huì)返回EPROTO錯(cuò)誤,表示協(xié)議錯(cuò)。解決上述問題的方法就是讓應(yīng)用進(jìn)程在調(diào)用accept前后設(shè)置某種形式的鎖,以保證只有一個(gè)子進(jìn)程阻塞在accept調(diào)用,而其他子進(jìn)程則阻塞在試圖獲取提供調(diào)用accept權(quán)力的鎖上。實(shí)現(xiàn)這種鎖的方式有很多,如文件鎖,信號(hào)量,互斥鎖等等。本節(jié)主要介紹文件鎖保護(hù)accept的方式47Networkprogramming‘03TCP預(yù)先派生子進(jìn)程服務(wù)器程序:
accept使用文件鎖保護(hù)(續(xù))在main函數(shù)中的唯一改動(dòng)是在子進(jìn)程的循環(huán)前增加一個(gè)函數(shù)調(diào)用:my_lock_init:
my_lock_init(“/tmp/lock.XXXX”); for(i=0;i<nchildren;i++) pids[i]=child_make(i,listenfd,addrlen);child_make函數(shù)不變,child_main函數(shù)的唯一改動(dòng)是在調(diào)用accept前獲取文件鎖,在accept返回后釋放文件鎖。 for(;;){ clilen=addrlen;
my_lock_wait(); connfd=accept(listenfd,cliaddr,&clilen);
my_lock_release(); close(connfd); }48Networkprogramming‘03TCP預(yù)先派生子進(jìn)程服務(wù)器程序:
accept使用文件鎖保護(hù)(續(xù))staticstructflock lock_it,unlock_it;staticint lock_fd=-1; /*fcntl()willfailifmy_lock_init()notcalled*/voidmy_lock_init(char*pathname){char lock_file[1024]; /*4mustcopycaller'sstring,incaseit'saconstant*/strncpy(lock_file,pathname,sizeof(lock_file));Mktemp(lock_file);lock_fd=Open(lock_file,O_CREAT|O_WRONLY,FILE_MODE);Unlink(lock_file); /*butlock_fdremainsopen*/49Networkprogramming‘03TCP預(yù)先派生子進(jìn)程服務(wù)器程序:
accept使用文件鎖保護(hù)(續(xù)) lock_it.l_type=F_WRLCK; lock_it.l_whence=SEEK_SET; lock_it.l_start=0; lock_it.l_len=0; unlock_it.l_type=F_UNLCK; unlock_it.l_whence=SEEK_SET; unlock_it.l_start=0; unlock_it.l_len=0;}/*endmy_lock_init*/50Networkprogramming‘03TCP預(yù)先派生子進(jìn)程服務(wù)器程序:
accept使用文件鎖保護(hù)(續(xù))voidmy_lock_wait(){int rc;while((rc=fcntl(lock_fd,F_SETLKW,&lock_it))<0){ if(errno==EINTR) continue; else err_sys("fcntlerrorformy_lock_wait"); }}voidmy_lock_release(){if(fcntl(lock_fd,F_SETLKW,&unlock_it)<0) err_sys("fcntlerrorformy_lock_release");}51Networkprogramming‘03TCP預(yù)先派生子進(jìn)程服務(wù)器程序:
accept使用文件鎖保護(hù)(續(xù))從本節(jié)的第1章表中可以看出,文件上鎖增加了服務(wù)器的進(jìn)程控制CPU時(shí)間。同時(shí),文件上鎖方式涉及到文件系統(tǒng)操作,這是很耗時(shí)的。但加鎖機(jī)制卻是SVR4系統(tǒng)中多子進(jìn)程accept同一監(jiān)聽套接字的唯一方法。ApacheWeb服務(wù)器程序1.1版利用了以上兩節(jié)的技術(shù)。預(yù)先派生子進(jìn)程后,如果實(shí)現(xiàn)允許所有子進(jìn)程都阻塞在accept調(diào)用上,那么使用不加鎖機(jī)制,否則就使用本節(jié)介紹的文件鎖技術(shù)。52Networkprogramming‘03TCP預(yù)先派生子進(jìn)程服務(wù)器程序:
accept使用線程互斥鎖保護(hù)本節(jié)介紹的線程互斥鎖不僅適用于同一進(jìn)程內(nèi)各線程間的上鎖,同時(shí)也適用于不同進(jìn)程間的上鎖。為使用線程鎖,main,child_make,child_main函數(shù)都不用改動(dòng),只需要修改3個(gè)上鎖函數(shù)。為了在多個(gè)進(jìn)程之間使用線程鎖,應(yīng)該作到:(1)互斥鎖變量必須存儲(chǔ)在為所有進(jìn)程共享的內(nèi)存中;(2)必須通知線程函數(shù)庫互斥鎖是在不同進(jìn)程間共享的。(這同樣要求線程庫支持PTHREAD_PROCESS_SHARED屬性)53Networkprogramming‘03TCP預(yù)先派生子進(jìn)程服務(wù)器程序:
accept使用線程互斥鎖保護(hù)(續(xù))#include "unpthread.h"#include <sys/mman.h>staticpthread_mutex_t *mptr; /*actualmutexwillbeinsharedmemory*/voidmy_lock_init(char*pathname){ int fd; pthread_mutexattr_t mattr; fd=Open("/dev/zero",O_RDWR,0); mptr=Mmap(0,sizeof(pthread_mutex_t),PROT_READ|PROT_WRITE, MAP_SHARED,fd,0); Close(fd);
Pthread_mutexattr_init(&mattr); Pthread_mutexattr_setpshared(&mattr,PTHREAD_PROCESS_SHARED); Pthread_mutex_init(mptr,&mattr);}54Networkprogramming‘03TCP預(yù)先派生子進(jìn)程服務(wù)器程序:
accept使用線程互斥鎖保護(hù)(續(xù))voidmy_lock_wait(){ Pthread_mutex_lock(mptr);}voidmy_lock_release(){ Pthread_mutex_unlock(mptr);}55Networkprogramming‘03TCP預(yù)先派生子進(jìn)程服務(wù)器程序:
傳遞描述字對(duì)預(yù)先派生子進(jìn)程服務(wù)器程序的最后一種改動(dòng)就是由父進(jìn)程調(diào)用accept,然后再將所接收的連接描述字傳遞給子進(jìn)程。這樣就繞過了子進(jìn)程中調(diào)用accept可能需要上鎖保護(hù)的問題。但為了實(shí)現(xiàn)給子進(jìn)程傳遞描述字,父進(jìn)程必須跟蹤所有子進(jìn)程的忙閑狀態(tài),以便給空閑子進(jìn)程傳遞新的描述字。為此,需要為每個(gè)子進(jìn)程維護(hù)一個(gè)信息結(jié)構(gòu),用來管理子進(jìn)程。56Networkprogramming‘03TCP預(yù)先派生子進(jìn)程服務(wù)器程序:
傳遞描述字(續(xù))typedefstruct{ pid_t child_pid; /*processID*/ int child_pipefd;/*parent'sstreampipeto/fromchild*/ int child_status; /*0=ready*/ long child_count; /*#connectionshandled*/}Child;Child *cptr; /*arrayofChildstructures;calloc'ed*/57Networkprogramming‘03TCP預(yù)先派生子進(jìn)程服務(wù)器程序:
傳遞描述字(續(xù))pid_tchild_make(inti,intlistenfd,intaddrlen){ int sockfd[2]; pid_t pid; void child_main(int,int,int); Socketpair(AF_LOCAL,SOCK_STREAM,0,sockfd); if((pid=Fork())>0){ Close(sockfd[1]); cptr[i].child_pid=pid; cptr[i].child_pipefd=sockfd[0]; cptr[i].child_status=0; return(pid); /*parent*/ }58Networkprogramming‘03TCP預(yù)先派生子進(jìn)程服務(wù)器程序:
傳遞描述字(續(xù)) Dup2(sockfd[1],STDERR_FILENO);/*child'sstreampipeto parent*/ Close(sockfd[0]); Close(sockfd[1]); Close(listenfd); /*childdoesnotneedthisopen*/ child_main(i,listenfd,addrlen); /*neverreturns*/}59Networkprogramming‘03TCP預(yù)先派生子進(jìn)程服務(wù)器程序:
傳遞描述字(續(xù))voidchild_main(inti,intlistenfd,intaddrlen){ char c; int connfd; ssize_t n; void web_child(int); printf("child%ldstarting\n",(long)getpid()); for(;;){ if((n=Read_fd(STDERR_FILENO,&c,1,&connfd))==0) err_quit("read_fdreturned0"); if(connfd<0) err_quit("nodescriptorfromread_fd"); web_child(connfd); /*processtherequest*/ Close(connfd); Write(STDERR_FILENO,"",1); /*tellparentwe'rereadyagain*/ }}60Networkprogramming‘03TCP預(yù)先派生子進(jìn)程服務(wù)器程序:
傳遞描述字(續(xù))staticint nchildren;intmain(intargc,char**argv){ int listenfd,i,navail,maxfd,nsel,connfd,rc; void sig_int(int); pid_t child_make(int,int,int); ssize_t n; fd_set rset,masterset; socklen_t addrlen,clilen; structsockaddr *cliaddr; if(argc==3) listenfd=Tcp_listen(NULL,argv[1],&addrlen); elseif(argc==4) listenfd=Tcp_listen(argv[1],argv[2],&addrlen); else err_quit("usage:serv05[<host>]<port#><#children>");61Networkprogramming‘03TCP預(yù)先派生子進(jìn)程服務(wù)器程序:
傳遞描述字(續(xù)) FD_ZERO(&masterset); FD_SET(listenfd,&masterset); maxfd=listenfd; cliaddr=Malloc(addrlen); nchildren=atoi(argv[argc-1]); navail=nchildren; cptr=Calloc(nchildren,sizeof(Child)); /*preforkallthechildren*/ for(i=0;i<nchildren;i++){ child_make(i,listenfd,addrlen); /*parentreturns*/ FD_SET(cptr[i].child_pipefd,&masterset); maxfd=max(maxfd,cptr[i].child_pipefd); } Signal(SIGINT,sig_int);62Networkprogramming‘03TCP預(yù)先派生子進(jìn)程服務(wù)器程序:
傳遞描述字(續(xù)) for(;;){ rset=masterset; if(navail<=0) FD_CLR(listenfd,&rset); /*turnoffifnoavailablechildren*/ nsel=Select(maxfd,&rset,NULL,NULL,NULL); /*4checkfornewconnections*/ if(FD_ISSET(listenfd,&rset)){ clilen=addrlen; connfd=Accept(listenfd,cliaddr,&clilen); for(i=0;i<nchildren;i++) if(cptr[i].child_status==0) break; /*available*/ if(i==nchildren) err_quit("noavailablechildren"); cptr[i].child_status=1; /*markchildasbusy*/ cptr[i].child_count++; navail--;63Networkprogramming‘03TCP預(yù)先派生子進(jìn)程服務(wù)器程序:
傳遞描述字(續(xù)) n=Write_fd(cptr[i].child_pipefd,"",1,connfd); Close(connfd); if(--nsel==0) continue; /*alldonewithselect()results*/ } /*4findanynewly-availablechildren*/ for(i=0;i<nchildren;i++){ if(FD_ISSET(cptr[i].child_pipefd,&rset)){ if((n=Read(cptr[i].child_pipefd,&rc,1))==0) err_quit("child%dterminatedunexpectedly",i); cptr[i].child_status=0; navail++; if(--nsel==0) break; /*alldonewithselect()results*/ } } }}64Networkprogramming‘03TCP預(yù)先派生子進(jìn)程服務(wù)器程序:
傳遞描述字(續(xù))voidsig_int(intsigno){ int i; void pr_cpu_time(void); /*4terminateallchildren*/ for(i=0;i<nchildren;i++) kill(cptr[i].child_pid,SIGTERM); while(wait(NULL)>0) /*waitforallchildren*/ ; if(errno!=ECHILD) err_sys("waiterror"); pr_cpu_time(); for(i=0;i<nchildren;i++) printf("child%d,%ldconnections\n",i,cptr[i].child_count); exit(0);}65Networkprogramming‘03TCP預(yù)先派生子進(jìn)程服務(wù)器程序:
傳遞描述字(續(xù))通過字節(jié)流管道傳遞描述字給每個(gè)子進(jìn)程,由子進(jìn)程通過字節(jié)流管道寫回1字節(jié)的數(shù)據(jù)以表示可用,要比無論是共享內(nèi)存中的互斥鎖還是文件鎖的上鎖和解鎖更費(fèi)時(shí);通過統(tǒng)計(jì)各個(gè)子進(jìn)程的連接數(shù),發(fā)現(xiàn)越是排在前頭的子進(jìn)程所處理的客戶請(qǐng)求就越多,出現(xiàn)不公平現(xiàn)象。這與以前幾種技術(shù)靠?jī)?nèi)核來調(diào)度子進(jìn)程接收連接是公平的情況不一樣。66Networkprogramming‘03TCP并發(fā)服務(wù)器程序:
每個(gè)客戶一個(gè)線程intmain(intargc,char**argv){ int listenfd,connfd; void sig_int(int); void *doit(void*); pthread_t tid; socklen_t clilen,addrlen; structsockaddr *cliaddr; if(argc==2) listenfd=Tcp_listen(NULL,argv[1],&addrlen); elseif(argc==3) listenfd=Tcp_listen(argv[1],argv[2],&addrlen); else err_quit("usage:serv06[<host>]<port#>"); cliaddr=Malloc(addrlen);67Networkprogramming‘03TCP并發(fā)服務(wù)器程序:
每個(gè)客戶一個(gè)線程(續(xù)) Signal(SIGINT,sig_int); for(;;){ clilen=addrlen; connfd=Accept(listenfd,cliaddr,&clilen); Pthread_create(&tid,NULL,&doit,(void*)connfd); }}void*doit(void*arg){ void web_child(int); Pthread_detach(pthread_self()); web_child((int)arg); Close((int)arg); return(NULL);}68Networkprogramming‘03TCP并發(fā)服務(wù)器程序:
每個(gè)客戶一個(gè)線程(續(xù))voidsig_int(intsigno){ void pr_cpu_time(void); pr_cpu_time(); exit(0);}69Networkprogramming‘03tcp預(yù)先創(chuàng)建線程服務(wù)器程序:
每個(gè)線程各自accept我們不是讓每個(gè)線程都阻塞在accept調(diào)用上,而是直接使用互斥鎖來保證線程間互斥地調(diào)用accept,此處我們直接使用線程鎖(因?yàn)樗鼈兪菍儆谕贿M(jìn)程)。為維護(hù)線程的信息,定義如下結(jié)構(gòu):typedefstruct{ pthread_t thread_tid; /*threadID*/ long thread_count; /*#connectionshandled*/}Thread;Thread *tptr; /*arrayofThreadstructures;calloc'ed*/int listenfd,nthreads;socklen_t addrlen;pthread_mutex_t mlock;70Networkprogramming‘03tcp預(yù)先創(chuàng)建線程服務(wù)器程序:
每個(gè)線程各自accept(續(xù))#include "unpthread.h"#include "pthread07.h"pthread_mutex_t mlock=PTHREAD_MUTEX_INITIALIZER;intmain(intargc,char**argv){ int i; void sig_int(int),thread_make(int); if(argc==3) listenfd=Tcp_listen(NULL,argv[1],&addrlen); elseif(argc==4) listenfd=Tcp_listen(argv[1],argv[2],&addrlen); else err_quit("usage:serv07[<host>]<port#><#threads>");71Networkprogramming‘03tcp預(yù)先創(chuàng)建線程服務(wù)器程序:
每個(gè)線程各自accept(續(xù)) nthreads=atoi(argv[argc-1]); tptr=Calloc(nthreads,sizeof(Thread)); for(i=0;i<nthreads;i++) thread_make(i); /*onlymainthreadreturns*/ Signal(SIGINT,sig_int); for(;;) pause(); /*everythingdonebythreads*/}72Networkprogramming‘03tcp預(yù)先創(chuàng)建線程服務(wù)器程序:
每個(gè)線程各自accept(續(xù))voidsig_int(intsigno){ int i; void pr_cpu_time(void); pr_cpu_time(); for(i=0;i<nthreads;i++) printf("thread%d,%ldconnections\n",i,tptr[i].thread_count); exit(0);}73Networkprogramming‘03tcp預(yù)先創(chuàng)建線程服務(wù)器程序:
每個(gè)線程各自accept(續(xù))voidthread_make(inti){ void *thread_main(void*); Pthread_create(&tptr[i].thread_tid,NULL,&thread_main,(void*)i); return; /*mainthreadreturns*/}74Networkprogramming‘03tcp預(yù)先創(chuàng)建線程服務(wù)器程序:
每個(gè)線程各自accept(續(xù))void*thread_main(void*arg){ int connfd; void web_child(int); socklen_t clilen; structsockaddr *cliaddr; cliaddr=Malloc(addrlen); printf("thread%dstarting\n",(int)arg); for(;;){ clilen=addrlen; Pthread_mutex_lock(&mlock); connfd=Accept(listenfd,cliaddr,&clilen); Pthread_mutex_unlock(&mlock); tptr[(int)arg].thread_count++; web_child(connfd); /*processtherequest*/ Close(connfd); }}75Networkprogramming‘03tcp預(yù)先創(chuàng)建線程服務(wù)器程序:
主線程統(tǒng)一accept主要的設(shè)計(jì)問題是如何將已連接的描述字傳遞給線程池中的空閑線程。我們可以用以前的
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 中國(guó)電建合同范本
- 烹飪?cè)现R(shí)練習(xí)題庫(附答案)
- 個(gè)人廣告公司年終總結(jié)
- 修路購銷合同范本
- 小學(xué)英語職稱考試試卷
- 作坊小廠轉(zhuǎn)讓合同范本
- 上半年工作總結(jié)和下半年工作計(jì)劃
- 醫(yī)生專家聘用合同范本
- 南京 汽車銷售合同范本
- 個(gè)人經(jīng)營(yíng)合作合同范本
- 會(huì)計(jì)信息化練習(xí)題庫+參考答案
- 武漢2025年湖北武漢市教育系統(tǒng)專項(xiàng)招聘教師679人筆試歷年參考題庫附帶答案詳解
- 高中主題班會(huì) 借哪吒精神燃開學(xué)斗志!課件-高一下學(xué)期開學(xué)第一課班會(huì)
- 2024年12月2025浙江湖州市長(zhǎng)興縣綜合行政執(zhí)法局公開招聘輔助執(zhí)法人員8人筆試歷年典型考題(歷年真題考點(diǎn))解題思路附帶答案詳解
- 水產(chǎn)養(yǎng)殖尾水處理技術(shù)-第1篇-深度研究
- 財(cái)務(wù)管理畢業(yè)論文
- 二零二五年度醫(yī)療援助派駐服務(wù)協(xié)議4篇
- 合同簽訂培訓(xùn)課件
- 1投影的形成和分類投影的形成投影的分類工程中常用的投影圖28課件講解
- 2025屆廣東省佛山一中石門中學(xué)高考臨考沖刺數(shù)學(xué)試卷含解析
- 大模型關(guān)鍵技術(shù)與應(yīng)用
評(píng)論
0/150
提交評(píng)論