今天看到乙個i/o效能的問題。就對這個問題思考了下。linux下幾種常見的i/o分析在本部落格此處
分析阻塞、非阻塞i/o,這兩種i/o乙個共同點是,很多i/o中無法確認那些i/o是準備好,只能通過乙個個輪詢的方式,這種方式下,準備好與沒有準備好的i/o均會被輪詢,這種效率極其低下。非同步i/o,提供了一種方式,準備好的i/o,就會傳送乙個訊號給核心,其他時間繼續進行其他的操作,這種方式乙個不好的就是多個i/o同時準備好,發出的訊號不好處理,不知道那個訊號對應於那個描述符。
這樣就思考,能不能有一種i/o呢。只考慮準備好的i/o,沒準備好的i/o,不進行操作。
其實是有的,這種i/o就是多路復用i/o。在處理i/o之前,我們需要呼叫函式進行處理所監控的i/o,判斷讀寫事件,等待其就緒後進行操作。這些函式有select、pselect、poll、epoll(linux2.6核心後支援)。
首先我們來看下select函式,分別需要看傳遞的引數與返回的結果。
傳遞給select的函式,主要告訴核心,我們感興趣的描述符,對於每個描述符,我們所關心的狀態,監控這些i/o需要等待多久。
select函式返回的時候,我們可以知道已經準備好的描述符的數量;對於讀、寫、異常這三個狀態的每乙個,那些描述符已經具備了。
select函式的原型如下:
#includeint select(int maxfdp1,fd_set *restirct readfds,
fd_set *restrict writefds,fd_set *restrict exceptfds,
struct timeval *resttrict tvptr );
這裡需要說明下這個等待時間tvptr。(ps:上面引數中每個指標都新增了關鍵字restrict,限定只有宣告的該指標才能訪問這塊記憶體,其他指標訪問都是無效的。)
當tvptr==null,select函式會永遠等待,直到捕捉到監控的描述符中有準備好的或者乙個出錯訊號,若出錯則返回-1,否則返回準備就緒的描述符的數量;
當tvptr->tv_sec==0 && tvptr->tv_usec==0,select函式不等待,測試所有指定的描述符立刻返回,得到每個描述符的狀態,非阻塞i/o輪詢每個描述符;
當tvptr->tv_sec!=0 || tvptr->tv_usec!=0,select函式會等待指定的時間,超時返回0。如果在等待的時間內有準備好的控制代碼,則返回準備好的控制代碼的數量。在等待的時間內,select函式可以被訊號打斷。
第乙個引數maxfdp1,這個引數的意思是最大的描述符加1,這樣的話就需要在三個集合中找出最大描述符,然後加一傳遞給這個變數。其實這個值可以傳遞乙個預設值,fd_setsize,這是核心維護的乙個常量,其值一般為1024,表示select函式最多可以處理的描述符的個數為1024個。若想要讓核心支援多一些,可以修改此引數。
然後,我們繼續看函式,裡面有三個引數需要注意readfds、writefds、exceptfds,這三個分別表示處於可讀、可寫、異常的控制代碼的集合,這些控制代碼都儲存在乙個叫做fd_set資料型別裡。現在看下關於這個集合的四種基本的操作:
#includeint fd_isset(int fd,fd_set *fdset);
void fd_set(int fd,fd_set *fdset);
void fd_clr(int fd,fd_set *fdset);
void fd_zero(fd_set *fdset);
首先看第乙個函式,fd_isset該函式用於測試控制代碼fd是否在fdset內,如果再返回乙個非零值,如果不在則返回0。fd_set將控制代碼fd新增到集合fdset中,fd_clr從集合fdset中清除控制代碼fd,fd_zero,初始化集合fdset,使得每個位置都為0.再以往select函式中新增引數為例,詳細了解下操作的過程,可以看下面的**:
fd_set readset,writeset;
fd_zero(&readset);
fd_zero(&writeset);
fd_set(0,&readset);
fd_set(3,&readset);
fd_set(2,&writeset);
fd_set(4,&writeset);
fd_set(1,&writeset);
select(5,&readset,&writeset,null,null);
下面說下pselect,這是select的乙個公升級版本,提供更加精細的定時操作與訊號遮蔽字,函式原型如下:
int pselect(int maxfdp1,fd_set *restirct readfds,
fd_set *restrict writefds,fd_set *restrict exceptfds,
const struct timespec *resttrict tsptr,const sigset_t *restrict sigmask);
這個函式與select不同的一點是,首先是定時器方面由timeval換成了timespec,這個更加精細,可以精確到微秒級別。還有乙個訊號遮蔽字sigmask。可以以原子的方式安裝訊號遮蔽字,函式返回時恢復以前的訊號遮蔽字。關於怎麼設定訊號遮蔽字,可以看看
這篇部落格。
上面所有的就是對select函式的全部理解。
下面看下另外乙個函式,叫做poll。
#includeint poll(struct pollfd fdarray,nfds_t nfds,int timeout);
poll與select不同的一點,select將描述符按照狀態編入乙個集合中,poll形成乙個pollfd結構陣列,陣列內每個元素指定乙個描述符編號以及對其所關心的狀態。
struct pollfd;
fdarray的元素個數由nfds決定。結構體中的events告訴核心對該描述符應當關心的是什麼,revents是核心操作完後返回值,用以說明核心對該描述符進行了什麼操作。
當乙個描述符被結束通話掉後,不能向描述符寫資料,但是可以讀取檔案描述符的資料。
poll函式的最後乙個timeout,當為-1時候,永遠等待,直到捕捉到訊號,poll返回-1。如果所指定的描述符準備好了,則返回準備好的檔案描述符的個數。當為0的時候,不等待,測試所有描述符並返回,這裡會以非阻塞輪詢的方式處理所有描述符。當timeout大於0,就等待timeout毫秒,超時返回0,否則返回準備好的個數。
最後一種epoll方式,可以看看本部落格前面的總結:epoll
I O多路復用
一 五種i o模型 1 阻塞i o模型 最流行的i o模型是阻塞i o模型,預設情形下,所有套介面都是阻塞的。我們以資料報套介面為例來講解此模型 我們使用udp而不是tcp作為例子的原因在於就udp而言,資料準備好讀取的概念比較簡單 要麼整個資料報已經收到,要麼還沒有。然而對於tcp來說,諸如套介面...
i o多路復用
最常見的i o多路復用就是 select poll epoll了,下面說說他們的一些特點和區別吧。select 可讀 可寫 異常三種檔案描述符集的申明和初始化。fd set readfds,writefds,exceptionfds fd zero readfds fd zero writefds ...
I O多路復用
我們都知道unix like 世界裡,一切皆檔案,而檔案是什麼呢?檔案就是一串二進位製流而已,不管socket,還是fifo 管道 終端,對我們來說,一切都是檔案,一切都是流。在資訊 交換的過程中,我們都是對這些流進行資料的收發操作,簡稱為i o操作 input and output 往流中讀出資料...