IO复用:
因为在linux中,一切皆文件,而文件就是一串二进制流。IO操作即是对文件进行读写(系统调用read/write)。
同步和异步针对应用程序来说,关注的是程序中间的协作关系;阻塞与非阻塞更关注的是单个进程的执行状态。
同步有阻塞和非阻塞之分,阻塞、非阻塞、多路IO复用,都是同步IO;异步必定是非阻塞的。
同步:执行一个操作之后,进程触发IO操作并等待(也就是我们说的阻塞)或者轮询的去查看IO操作(也就是我们说的非阻塞)是否完成,等待结果,然后才继续执行后续的操作。
异步:执行一个操作后,可以去执行其他的操作,然后等待通知再回来执行刚才没执行完的操作。
阻塞:进程给CPU传达一个任务之后,一直等待CPU处理完成,然后才执行后面的操作。——阻塞忙等
非阻塞:进程给CPU传达任务后,继续处理后续的操作,隔断时间再来询问之前的操作是否完成。这样的过程其实也叫轮询。——非阻塞忙
异步IO即是用户线程需要IO操作时,线程不等待CPU的IO结果(IO的执行由CPU完成),直接返回继续执行下面的操作,等待一个IO完成的信号后,再处理数据。也就是select、poll、epoll都是同步IO。
一、select
1、select简述
select是网络IO模型中的IO复用。
select使用描述字集,典型地是一个整数数组,其中每个整数中的每一位对应一个文件描述字。
select的问题:
select 中由于FD_SETSIZE设置,默认值是1024,所以对于那些需要支持的上万连接数目服务器来说显然太少了。而且即使选择修改这个宏然后重新编译内核,有资料也指出这样会带来网络效率下降。
另外,在内核中的select实现中,select是采用无差别轮询来处理的,即每次检测都会遍历所有fd_set中的句柄,所以select具有O(n)的无差别轮询复杂度,同时处理的流(fd)越多,无差别轮询时间就越长。
3、函数原型
#include
int select (int maxfd+1, fd_set *readset, fd_set *writeset, fd_set *exceptset, const struct timeval * timeout);
struct fd_set可以理解为一个集合,这个集合中存放的是文件描述符(filedescriptor),即文件句柄。
fd_set集合可以通过一些宏由人为来操作,比如:
清空集合FD_ZERO(fd_set*);
将一个给定的文件描述符加入集合之中,FD_SET(int,fd_set*);
将一个给定的文件描述符从集合中删除,FD_CLR(int, fd_set*);
检查集合中指定的文件描述符是否可以读写,FD_ISSET(int,fd_set*)。
参数解释:
参数一:是指集合中所有文件描述符的范围,且文件描述符的最大值加1。
参数二:用于检查可读性,是否可以读了,如果这个集合中有一个文件可读,select就会返回一个大于0的值,表示有文件可读,如果没有可读的文件,则根据timeout参数再判断是否超时,若超出timeout的时间,select返回0,若发生错误返回负值。可以传入NULL值,表示不关心任何文件的读变化。
参数三:用于检查可写性,是否可以写了,如果这个集合中有一个文件可写,select就会返回一个大于0的值,表示有文件可写,如果没有可写的文件,则根据timeout参数再判断是否超时,若超出timeout的时间,select返回0,若发生错误返回负值。可以传入NULL值,表示不关心任何文件的写变化。
参数四:用于检查带外数据,用来监视文件错误异常。
参数五:一个指向timeval结构的指针,用于决定select等待I/o的最长时间,如果为空将一直等待。
timeval结构的定义:
struct timeval{
long tv_sec; // seconds
long tv_usec; // microseconds
}
返回值
>0:就绪描述字的正数目
-1:出错
0 :超时
二、poll
1、poll简述
相⽐于select,poll的实现功能差不多,但poll效率更高。
2、函数原型
unsigned int (*poll)(struct file * fp, struct poll_table_struct * table)
#include
int poll(struct pollfd fds[], nfds_t nfds, int timeout);
struct pollfd的结构:
struct pollfd {
int fd; /*文件描述符*/
short events; /* 等待的需要测试事件 */
short revents; /* 实际发生了的事件,也就是返回结果 */
};
当返回正值时,代表满足响应事件的文件描述符的个数,如果返回0则代表在规定时间内没有事件发生。如发现返回为负则应该立即查看 errno,因为这代表有错误发生。
三、epoll
1、epoll简述
相⽐于select,epoll最大的好处在于它不会随着监听fd数目的增⻓长而降低效率。
epoll可以理解为event poll,不同于忙轮询和无差别轮询,epoll会把哪个流发生了怎样的I/O事件通知我们。所以我们说epoll实际上是事件驱动(每个事件关联上fd)的,对这些流操作的复杂度降低到了O(1)。
所以,select和epoll最大的区别就是:select只是告诉你一定数目的流有事件了,至于哪个流有事件,还得你一个一个地去轮询,而epoll会把发生的事件告诉你,通过发生的事件,就自然而然定位到哪个流了。epoll跟select相比,也是一种牺牲空间,换取时间的思想。
epoll的两种工作模式:
(1)、ET模式——Edge Triggered边沿触发
ET模式仅当状态发生变化的时候才能获得通知,这⾥所谓的状态的变化并不包括缓冲区中还有未处理的数据,也就
是说,ET模式需要一直read/write直到出错为止。
ET是高速工作方式,只支持no-block socket。
(2)、LT模式——Level Triggered水平触发
LT模式是缺省的工作方式,并且同时支持block和no-block socket。
LT模式是只要数据没有处理就会一直通知下去,相较于ET模式,LT模式编程出错的概率可能性要小一点。
而select/poll都是LT模式典型代表。
参考链接:
https://baike.so.com/doc/5398007-5635387.html
https://www.cnblogs.com/aspirant/p/6877350.html
https://blog.csdn.net/sxtobj/article/details/53158016