在不使用线程,独立处理文件的情况下,进程无法在多个文件描述符上阻塞。
同步阻塞IO:假如其中某个文件描述符没有准备好,进程就会阻塞,不能再处理其它文件,直到该文件描述符准备好。
同步非阻塞IO:假如其中某个文件描述符没有准备好,向进程返回一个错误信息,从而避免阻塞。
若是采用非阻塞IO,进程需要以某种不确定的方式不断发起IO操作,直到某个打开的文件描述符已经就绪。
IO多路复用允许应用在多个文件描述符上同时阻塞,直到有一个或者更多的文件描述符处于就绪状态,然后处理已经就绪的文件描述符。Linux提供了三种IO多路复用方案:select ,poll,epoll。
(1)select
select函数原型:
int select(int n,fd_set* readfds,fd_set* writefds,fd_set* exceptions,struct timeval* timeout);
n:所有集合中文件描述符的最大值加1.
writefds:监测writefds集合中,是否至少有一个写操作可以不阻塞的完成。
readfds:监测readfds集合中,是否至少有一个读操作可以不阻塞的完成。
exceptions:监测exceptions集合中是否有出现异常发生。
timeout:这是一个结构体,设定时间限制,超过该时间,select函数会返回。
成功返回时,每个集合中只包含对应类型的IO就绪的文件描述符,同时返回在三个集合中IO就绪的数目。
在每次使用调用select时,需要使用辅助宏FD_ZERO将制定集合中所有文件描述符移除。同时,可以利用
FD_SET向某个集合中添加文件描述符。
(2)poll
poll函数原型:
int poll(struct pollfd* fds,unsigned int nfds,int timeout);
fds:结构体数组指针,指定要监听的文件描述符集合。
struct pollfd//指定监听的单一文件描述符
{
int fd;//文件描述符
short events;//监听的集合,要监视的文件描述符事件的一组位掩码。
short revents;//返回的集合,发生在该文件描述符上的时间的位掩码。
}
nfds:需要监听的文件描述符的个数。
timeout:在任何IO就绪前等待的时间长度,超过该值,poll调用阻塞。
成功时,函数返回具有非零revents字段的文件描述符的个数。
(3) epoll
提供三个函数接口:
int epoll_create(int size)
创建一个epoll的句柄,size表示监听数目。创建完句柄后,会占用一个fd。在使用完epoll后,必须调用close()关闭,否则可能导致fd被耗尽。
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);//epoll的事件注册函数。
epfd:返回值
op:动作,可以取值EPOLL_CTL_ADD(注册新的fd);EPOLL_CTL_MODE(修改已经注册的fd监听时间);POLL_CTL_DEL(删除fd)。
fd:需要监听的fd
struct epoll_event结构如下:
typedef union epoll_data
{
void *ptr;struct epoll_event
{
__uint32_t events; /* Epoll events */3. int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
epfd:返回值。
events:从内核得到的事件的集合。
maxevents:events事件数目,不能超过size。
timeout:超时时间。超时,epoll会阻塞。
select与poll比较:
(1)select需要就算最大文件描述符值加1,并传递该参数,poll不需要。
(2)poll在文件描述符值较大时,效率更高。select需要从头到尾进行遍历.
(3)文件描述符集合在返回时会被重建。poll中输入数组不需要改变。
(4)select可移植性更好,某些unix系统不支持poll。
(5)select 上限为1024,poll要大很多。
(6)每次调用select或者poll时,都要将文件描述符拷贝到内核空间,然后都是从头到尾遍历。两个开销都很大。
epoll性能比select和poll都要好:
(1)在epoll_ctl中,每次注册新的文件描述符到epoll句柄中时,会把所有的文件描述符拷贝进内核(文件描述符存在红黑树中,不会被重复加入),而不是在epoll_wait时候重复拷贝。epoll保证了每个文件描述符只被拷贝一次。
(2)内核通过为每一个文件描述符指定一个回调函数,当文件描述符就绪时,回调函数将文件描述符加入一个就绪链表,epoll_wait查看这个就绪链表是否有就绪文件描述符。