基本 IO 模型
傳統(tǒng)的 CS 網(wǎng)絡(luò)模型
虛線標(biāo)識(shí)網(wǎng)絡(luò)連接的建立钾唬,實(shí)線表示鏈接建立后的請(qǐng)求/響應(yīng)。
多線程網(wǎng)絡(luò)模型
缺點(diǎn):
- 每個(gè)鏈接都需要一個(gè)獨(dú)立線程處理奕巍,鏈接的建立拆除伴隨線線程的創(chuàng)建儒士、銷(xiāo)毀;
- 網(wǎng)絡(luò) IO 延遲導(dǎo)致線程可能多數(shù)時(shí)間處于阻塞狀態(tài)诅福;
- 大量建立鏈接請(qǐng)求到達(dá)服務(wù)端可能導(dǎo)致服務(wù)端創(chuàng)建線程過(guò)多拖叙;
線程池網(wǎng)絡(luò)模型
多線程網(wǎng)絡(luò)模型導(dǎo)致的 缺點(diǎn)1,缺點(diǎn)3 可以解決咖气,但本質(zhì)上是引入池技術(shù)解決線上的挖滤,并沒(méi)有從 IO 技術(shù)層面解決阻塞問(wèn)題崩溪。
IO 多路復(fù)用
對(duì)于網(wǎng)絡(luò) IO斩松,一個(gè)基本操作需要分為兩步:
- 等待網(wǎng)絡(luò)數(shù)據(jù)分組到達(dá)惧盹,然后將其復(fù)制到內(nèi)核緩沖區(qū)
- 把數(shù)據(jù)從內(nèi)核緩沖區(qū)復(fù)制到應(yīng)用空間緩沖區(qū)
主要的阻塞發(fā)生在第一步奋救,而上線的模型中每個(gè)鏈接都需要單獨(dú)的線程去處理反惕。
如 Redis 這樣的單進(jìn)程應(yīng)用,如果使用上面的網(wǎng)絡(luò)模型姿染,當(dāng)某個(gè)網(wǎng)絡(luò)操作將導(dǎo)致整個(gè) IO 阻塞,其他的網(wǎng)絡(luò)操作都得不到響應(yīng)狡汉。
事實(shí)上 Redis 這類(lèi)應(yīng)用使用的是 IO 多路復(fù)用網(wǎng)絡(luò)模型(這里復(fù)用指的是復(fù)用一個(gè)線程去處理多個(gè)鏈接):
構(gòu)造一個(gè)感興趣的文件描述符集合闽颇,然后調(diào)用文件函數(shù),直到這些描述符中的一個(gè)已準(zhǔn)備好進(jìn)行 IO 時(shí)函數(shù)返回兵多。poll, pselect 和 select 這三個(gè)函數(shù)可以幫助我們實(shí)現(xiàn)剩膘。
select & pselct
select 函數(shù)原型:
#include <sys/select.h>
int select(int maxfdp1, fd_set *restrict readfds, fd_set *restrict writefds,
fd_set *restrict exceptfds, struct timeval * restrict tvptr);
傳遞給 select 函數(shù)參數(shù)告訴內(nèi)核:
- 關(guān)心的描述符
- 對(duì)每個(gè)描述符關(guān)心的的條件
- 原意等待的的時(shí)長(zhǎng)
從 select 返回時(shí)內(nèi)核返回:
- 已準(zhǔn)備好的描述符總數(shù)量
- 對(duì)于讀、寫(xiě)或異常這3個(gè)條件的每一個(gè)怠褐,哪些描述符已準(zhǔn)備好
select 函數(shù)的參數(shù)含義:
- tvptr:原意等待的事件長(zhǎng)度
- readfds, writefds, exceptfds:可讀、可寫(xiě)奠涌、處于異常條件的描述符集合
- maxfdpl:最大描述符編號(hào)加1磷杏,用于縮小查找范圍
這里需要說(shuō)明下 fd_set 數(shù)據(jù)類(lèi)型,它是一個(gè)字節(jié)數(shù)組达皿,每個(gè)文件描述符由一位表示贿肩。
pselect 函數(shù)最主要的改變是在 select 函數(shù)基礎(chǔ)上增加了一個(gè) sigmask 參數(shù),用于指向信號(hào)屏蔽字
select & pselect 的問(wèn)題:
- 單個(gè)進(jìn)程所打開(kāi)的描述符有限制汰规,由FD_SETSIZE設(shè)置
- 內(nèi)核會(huì)修改參數(shù),每次調(diào)用需要重置
- 每次調(diào)用都需要遍歷文件描述符
- 用戶&內(nèi)核空間數(shù)據(jù)拷貝
poll
select 的缺點(diǎn)很大程度是 fd_set 數(shù)據(jù)結(jié)構(gòu)引入的滔金,因此 poll 與 select 的差異也重點(diǎn)體現(xiàn)在了數(shù)據(jù)結(jié)構(gòu)的不同。
poll 函數(shù)原型:
#include <poll.h>
int poll(struct pollfd fdarray[], nfds_t nfds, int timeout);
與 select 不同餐茵,poll 不為每個(gè)條件構(gòu)造字符集,而是構(gòu)造一個(gè) pollfd 結(jié)構(gòu)的數(shù)組:
struct pollfd {
int fd;
short events;
short revents;
}
poll 本質(zhì)上與 select 相同锣笨,但解決了 select 的問(wèn)題1道批,2。但對(duì)于問(wèn)題3 并沒(méi)有解決
epoll
TODO