緬懷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。
第四條: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 的個數。