這是學(xué)習(xí)網(wǎng)絡(luò)編程后寫(xiě)的一個(gè)練手的小程序,可以幫助復(fù)習(xí)I/O模型,epoll使用,線程池,HTTP協(xié)議等內(nèi)容。

程序代碼是基于《Linux高性能服務(wù)器編程》一書(shū)編寫(xiě)的。

首先回顧程序中的核心內(nèi)容和主要問(wèn)題,最后給出相關(guān)代碼。

0. 功能和I/O模型

實(shí)現(xiàn)簡(jiǎn)易的HTTP服務(wù)端,現(xiàn)僅支持GET方法,通過(guò)瀏覽器訪問(wèn)可以返回相應(yīng)內(nèi)容。

I/O模型采用Reactor(I/O復(fù)用 + 非阻塞I/O) + 線程池。 使用epoll事件循環(huán)用作事件通知,如果listenfd上可讀,則調(diào)用accept,把新建的fd加入epoll中;

是已連接sockfd,將其加入到線程池中由工作線程競(jìng)爭(zhēng)執(zhí)行任務(wù)。

 

1. 線程池怎么實(shí)現(xiàn)?

程序采用c++編寫(xiě),要自己封裝一個(gè)簡(jiǎn)易的線程池類。大致思路是創(chuàng)建固定數(shù)目的線程(如跟核數(shù)相同),然后類內(nèi)部維護(hù)一個(gè)生產(chǎn)者—消費(fèi)者隊(duì)列。

提供相應(yīng)的添加任務(wù)(生產(chǎn)者)和執(zhí)行任務(wù)接口(消費(fèi)者)。按照操作系統(tǒng)書(shū)中典型的生產(chǎn)者—消費(fèi)者模型維護(hù)增減隊(duì)列任務(wù)(使用mutex和semaphore)。

mutex用于互斥,保證任意時(shí)刻只有一個(gè)線程讀寫(xiě)隊(duì)列,semaphore用于同步,保證執(zhí)行順序(隊(duì)列為空時(shí)不要讀,隊(duì)列滿了不要寫(xiě))。

 

2. epoll用條件觸發(fā)(LT)還是邊緣觸發(fā)(ET)?

考慮這樣的情況,一個(gè)工作線程在讀一個(gè)fd,但沒(méi)有讀完。如果采用LT,則下一次事件循環(huán)到來(lái)的時(shí)候,又會(huì)觸發(fā)該fd可讀,此時(shí)線程池很有可能將該fd分配給其他的線程處理數(shù)據(jù)。

這顯然不是我們想要看到的,而ET則不會(huì)在下一次epoll_wait的時(shí)候返回,除非讀完以后又有新數(shù)據(jù)才返回。所以這里應(yīng)該使用ET。

當(dāng)然ET用法在《Tinychatserver: 一個(gè)簡(jiǎn)易的命令行群聊程序》也有總結(jié)過(guò)。用法的模式是固定的,把fd設(shè)為nonblocking,如果返回某fd可讀,循環(huán)read直到EAGAIN。

 

3. 繼續(xù)上面的問(wèn)題,如果某個(gè)線程在處理fd的同時(shí),又有新的一批數(shù)據(jù)發(fā)來(lái)(不是老數(shù)據(jù)沒(méi)讀完,是來(lái)新數(shù)據(jù)了),即使使用了ET模式,因?yàn)樾聰?shù)據(jù)的到來(lái),仍然會(huì)觸發(fā)該fd可讀,所以仍然存在將該fd分給其他線程處理的情況。

這里就用到了EPOLLONESHOT事件。對(duì)于注冊(cè)了EPOLLONESHOT事件的文件描述符,操作系統(tǒng)最大觸發(fā)其上注冊(cè)的一個(gè)可讀、可寫(xiě)或者異常事件,且只觸發(fā)一次。

延伸閱讀

學(xué)習(xí)是年輕人改變自己的最好方式-Java培訓(xùn),做最負(fù)責(zé)任的教育,學(xué)習(xí)改變命運(yùn),軟件學(xué)習(xí),再就業(yè),大學(xué)生如何就業(yè),幫大學(xué)生找到好工作,lphotoshop培訓(xùn),電腦培訓(xùn),電腦維修培訓(xùn),移動(dòng)軟件開(kāi)發(fā)培訓(xùn),網(wǎng)站設(shè)計(jì)培訓(xùn),網(wǎng)站建設(shè)培訓(xùn)學(xué)習(xí)是年輕人改變自己的最好方式