第一条:概述
复用是个伟大的概念呀!什么是I / O复用(I / O multiplexing)呢?具体点就是当你编写的程序需要同时处理多个描数字(socket或file或device),你又不知道什么时候应该(比方说有数据可以读了)去操作(读/写)哪个描数字。这时候I / O复用就需要登场了。
UNPv1给出了定义.I / O复用是一种让进程预先“警告”内核能力,使得内核一旦发现进程预先告知时指定的一个或多个I / O条件(就是描述符)就绪(可以读/写了),内核就通知进程.linux有4个调用可实现I / O复用:选择,调查继承自Unix的系统.pselect是选择到的Posix版.epoll是LINUX2.6内核特有的。
第二条:I / O模型
(0)一般输入操作通常包含如下两个阶段,请谨记:
1,等待数据准备好。
2,内核到进程的数据拷贝。
(0.1)例如对于插座上的输入操作:
1,数据从网络到达,当数据到达时,收集到内核中相应的缓冲区。
2,从内核缓冲区拷贝数据到进程端口缓冲区中。
(1)UNPv1当中总结了的Unix的I / O模型:
1,阻塞I / O:进程被阻塞I / O系统调用上(读,写,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(同步I / O操作,异步I / O(异步I / O)操作这两个术语的定义如下:
同步I / O操作:导致请求进程阻塞,直到I / O操作完成。
异步I / O操作:不导致进程阻塞。
注:鉴别是否是异步I / O操作,只要看I / O操作是谁执行。
第三条:PSELECT,选择
#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); //选择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条件就绪的描述符,未就绪的描述被清楚了所以每次调用选择时都要重新设置这三个参数,这非常重要。
(0.3)参数超时:选择和PSELECT中地类型,超时间类型分别:
struct timespec {//使用时加上-D__need_timespec
__time_t tv_sec;
long int tv_nsec; 纳秒。
};
struct timeval {
__time_t tv_sec;
__suseconds_t tv_usec; 微妙。
};
(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。
第四条:调查
#include <poll.h>
(0)int poll(struct pollfd fds [],nfds_t nfds,int timeout); // epoll是poll的增强版
(0.1)参数fds是指向struct pollfd的数组。他是poll的核心。
struct pollfd {//每个描述符对应一个struct pollfd结构。
int fd; //我们关心的描述符。
短整型事件; //描述符关心的事件
简短的诠释; //民调返回时,返回描述符FD发生的时间。
};
成员的活动和revents中的值如下:
#define POLLIN 0x001 //可读
#define POLLPRI 0x002 //带外数据到达
#define POLLOUT 0x004可写
#if定义__USE_XOPEN || 定义__USE_XOPEN2K8 // 很少 用
#定义POLLRDNORM 0x040 //一般数据可读
#定义POLLRDBAND 0x080 //优先级带数据可读
#define POLLWRNORM 0x100 //一般数据可读
#define POLLWRBAND 0x200 //优先级数据可写
#万一
#ifdef __USE_GNU
#定义POLLMSG 0x400
#定义POLLREMOVE 0x1000
#定义POLLRDHUP 0x2000
#万一
#define POLLERR 0x008错误
#define POLLHUP 0x010挂起
#define POLLNVAL 0x020 //无效描述符等
(0.2)参数NFDs的:指名数组大小。
(0.3)参数超时:超时时间取值如下,单位为毫秒。:
INFTIM:永远等待.linux没有定义此值,手动定义为-1即可。
0:立即返回,不阻塞进程。
> 0:等待指定毫秒数。
第五条:常见问题解答
1,epoll的是民意调查的加强版。