Linux——I/O複用

 

緬懷Stevens大師。

最好的參考資料:

1.師從互聯網。

2.UNP v1第6章 。

3,man select、man pselect、man epoll、man poll

第一條:概述

複用是個偉大的概念呀!什麼是I/O複用(I/O multiplexing)呢?具體點就是當你編寫的程序需要同時處理多個描數字(socket或file或device),你又不知道什麼時候應該(比方說有數據可以讀了)去操作(讀/寫)哪個描數字。這時候I/O複用就需要登場了。

 

UNPv1給出了定義。I/O複用是一種讓進程預先“警告”內核能力,使得內核一旦發現進程預先告知時指定的一個或多個I/O條件(就是描述符)就緒(可以讀/寫了),內核就通知進程。linux有4個調用可實現I/O複用:select、poll繼承自Unix系統。pselect是select到Posix版。epoll是linux2.6內核特有的。

第二條:I/O模型

(0)一般輸入操作通常包含如下兩個階段,請謹記:

1、等待數據準備好。

2、內核到進程的數據拷貝。

(0.1)例如對於socket上的輸入操作:

1、數據從網絡到達,當數據到達時,收集到內核中相應的緩衝區。

2、從內核緩衝區拷貝數據到進程端口緩衝區中。

(1)UNPv1當中總結了Unix的I/O模型:

1、阻塞I/O:進程被阻塞I/O系統調用上(read、write、sendto、recvfrom等等)直到I/O條件就緒(可讀/寫或異常)處理完,處理後從I/O系統調用返回進程。

2、非阻塞I/O。就是採用輪詢方式調用非阻塞I/O系統調用。

 

3、I/O複用。

4、多線程阻塞I/O。把要處理的描數字分配到多個線程中,每個線程獨立地調用I/O系統調用,獨立處理。

 

5、信號驅動I/O。類似於I/O複用,當I/O條件就緒時使用信號通知進程去調用I/O系統調用處理。

 

6、異步I/O。通過aio_read告訴內核怎麼處理,我們就不管了,內核處理完了後發個信號告訴我們,也可以用其他方式在處理完通知我們。

注:I/O系統調用是否阻塞,僅取決於他們操作的描數字是否是阻塞的。fcntl可設定。

(2)這6種I/O模型分爲2類:

同步I/O模型:第1~5 I/O模型。這5種I/O模型,說到底,I/O調用還是要用戶進程自己調用,被阻塞。

異步I/O模型:第6種I/O模型。I/O調用由內核執行,進程不被阻塞。

 

Posix關於同步I/O(synchronous I/O操作、異步I/O(asynchronous I/O)操作這兩個術語的定義如下:

 

同步I/O操作:導致請求進程阻塞,直到I/O操作完成。

 

異步I/O操作:不導致進程阻塞。

注:鑑別是否是異步I/O操作,只要看I/O操作是誰執行。

第三條:pselect、select

#include <sys/select.h>

(0)int pselect(int nfds, fd_set *restrict readfds, fd_set *restrict writefds, fd_set *restrict errorfds,

                              const struct timespec *restrict timeout, const sigset_t *restrict sigmask);//select的Posix版。

 

 

int select(int nfds, fd_set *restrict readfds, fd_set *restrict writefds, fd_set *restrict errorfds,             

 

                              struct timeval *restrict timeout);

(0.1)參數nfds:指定待測試描述字(就是描述符)的個數(不是最大值,概念上一定要區分),因爲描述符是從0開始的,所以他的值就是待測試的最大描述符加1(多一個0)。

(0.2)參數readfds、writefds、errorfds:3個參數都是fd_set類型。這3個參數在函數調用地不同時段有兩種用途:

(0.2.1)作爲參數傳入:我們把關心的描述符按照關心事件的事件類型分別放入其中。怎麼放?見下。

(0.2.2)作爲函數返回時的返回值:這3個參數裝載這我們關心並且I/O條件就緒的描述符,未就緒的描述被清楚了。所以每次調用select時都要重新設置這三個參數,這非常重要。

(0.3)參數timeout:select和pselect中地類型,超時間類型分別:

/* POSIX.1b structure for a time value.  This is like a `struct timeval' but  has nanoseconds instead of microseconds.  */

struct timespec  {// 使用時加上 -D__need_timespec

    __time_t tv_sec; /* Seconds.  */

    long int tv_nsec; /* Nanoseconds.  */納秒。

  };

/* A time value that is accurate to the nearest microsecond but also has a range of years.  */  

struct timeval  {

    __time_t tv_sec; /* Seconds.  */

    __suseconds_t tv_usec; /* Microseconds.  */微妙。

  };

(0.4)參數sigmask:信號屏蔽掩碼。

 

 

 

 

(1)描述符fd和描述符集合fdset操作宏:

void FD_CLR(int fd, fd_set *fdset);//congfdset刪除fd。

int FD_ISSET(int fd, fd_set *fdset);//測試fd是否在fdset當中

void FD_SET(int fd, fd_set *fdset);//向fdset添加fd

void FD_ZERO(fd_set *fdset);//清零,類似bzero。

(2)select的FD_SETSIZE限制。
現在的linux所能打開的描述符個數,只受內存和管理性的限制。但由於在select函數設計之初,select的內部實現參考了FD_SETSIZE,就是select最多能處理的描述符個數是FD_SETSIZE個,一般爲1024。
/* Number of descriptors that can fit in an `fd_set'.  */
#define __FD_SETSIZE 1024

 

第四條:poll

#include <poll.h>

(0)int poll(struct pollfd fds[], nfds_t nfds, int timeout);//epoll是poll的增強版

(0.1)參數fds是指向struct pollfd的數組。他是poll的核心。  

struct pollfd{/* Data structure describing a polling request.  *///每個描述符對應一個struct pollfd結構。

    int fd; /* File descriptor to poll.  *///我們關心的描述符。

    short int events; /* Types of events poller cares about.  */ //描述符關心的事件

    short int revents; /* Types of events that actually occurred.  *///poll返回時,返回描述符fd發生的時間。

  };

成員events和revents的值如下:

/* Event types that can be polled for.  These bits may be set in `events' to indicate the interesting event types; they will appear in `revents' to indicate the status of the file descriptor.  */

#define POLLIN 0x001 /* There is data to read.  *///可讀

#define POLLPRI 0x002 /* There is urgent data to read.  *///帶外數據到達

#define POLLOUT 0x004 /* Writing now will not block.  */可寫

 

#if defined __USE_XOPEN || defined __USE_XOPEN2K8  /* These values are defined in XPG4.2.  *///很少用

# define POLLRDNORM 0x040 /* Normal data may be read.  *///一般數據可讀

# define POLLRDBAND 0x080 /* Priority data may be read.  *///優先級帶數據可讀

# define POLLWRNORM 0x100 /* Writing now will not block.  *///一般數據可讀

# define POLLWRBAND 0x200 /* Priority data may be written.  *///優先級數據可寫

#endif

 

#ifdef __USE_GNU/* These are extensions for Linux.  */

# define POLLMSG 0x400

# define POLLREMOVE 0x1000

# define POLLRDHUP 0x2000

#endif

 

/* Event types always implicitly polled for.  These bits need not be set in `events', but they will appear in `revents' to indicate the status of the file descriptor.  */

#define POLLERR 0x008 /* Error condition.  */錯誤

#define POLLHUP 0x010 /* Hung up.  */掛起

#define POLLNVAL 0x020 /* Invalid polling request.  *///無效描述符等

(0.2)參數nfds:指名數組大小。

(0.3)參數timeout:超時時間。取值如下,單位爲毫秒:

INFTIM:永遠等待。linux沒有定義此值,手動定義爲-1即可。

0:立即返回,不阻塞進程。

>0:等待指定毫秒數。

第五條:FAQ

1、epoll是poll的加強版。

 

 

2、poll沒有select 的描述符上限限制。描述符個數上限只和內存大小結構,即內存中struct pollfd 的個數。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章