五中IO模型:
1)阻塞IO,比如socket,一直阻塞到对方数据填充recv缓冲区,一直等到IO操作彻底返回才回到用户空间,
最简单的改进是使用多进程或者多线程,让一个连接的阻塞不影响其他
int accept(int fd,struct sockaddr *addr,socklen_t *addrlen); //accept接口原型
2)非阻塞IO:但是这种是一直轮询内核数据,并不阻塞,会造成CPU资源的浪费,一般不使用,被调用后立即
返回给用户一个状态值不必等到IO操作彻底完成
fcntl(fd,F_SETFL,O_NONBLOCK); //将句柄设置为非阻塞状态
连接池和线程池的概念:就是维持一定的连接数,意在降低创建和销毁进程的频率,让空闲的进程或线程
重新承担新的执行任务,但也只是在一定程度上缓解了频繁调用io接口带来的资源占用
3)IO复用:用select来管理多个文件描述符,一旦有一个有数据写入,select就返回,也就是阻塞在select,
不阻塞在recv,select可以看成管理者,用它来管理多个IO,一旦其中的一个或多个IO检测到我们感兴趣的事件,select就返回,返回值为检测到的事件的个数以及是哪些IO发生的事件,这样我们就可以遍历这些事件,进而
去处理它,若直到超时都未检测到事件,则返回0,它的优势不在于能够处理的更快,而在于能够处理更多的连接
在IO复用模型中,对于每一个socket,其实是非阻塞的,但是整个用户进程是阻塞的,只是它被阻塞在select,
而不是socket IO
select(int nfds, fd_set *restrict readfds, fd_set *restrict writefds,
fd_set *restrict errorfds, struct timeval *restrict timeout);
参数说明:1 读写异常集合中的文件描述符的最大值+1
2 读集合
3 写集合
4 异常集合
5 超时描述的结构体
select在检测到可读事件之后,即时响应recv()操作,并根据接收到的数据准备好待发送数据,并将
对应的句柄值假如到writefds,准备下次的可写事件的select检测,同样,如果select发现某个句柄
发现可写事件,会立即做send操作,并准备好下一次的可读事件操作
事件驱动模型:每一个执行周期都会检测或者探测一次或者一组事件,一个特定的事件会触发某个特定
的响应
但是,注意select模型接口并不是实现事件驱动的最好的选择,因为当需要探测的句柄值较大的时候,
select接口本身需要消耗大量的实际去轮询各个句柄,很多操作系统提供了更为高效的接口,
比如Linux提供了epoll,而BSD提供了kqueue,Solaris提供了/dev/poll等但由于各系统的epoll
接口实现差异较大,所以用epool实现的服务器实现跨平台比较难,libevent和libev库是属于事件
驱动库 ,实际上Linux2.6之后也引入了支持异步的io操作如aio_read , aio_write
4)信号驱动IO:安装一个信号处理程序sigaction,有数据写入信号到来,再用信号通知应用进程有数
据到来,执行recv接收,把数据从内核拉取到用户空间,也不常用
5)异步IO:效率最高,用aio_read来实现的,没有数据时阻塞,有数据到来,直接copy到用户空间buf,
应用进程直接处理数据即可,不用再调用recv函数拉取,是一种主动推送的机制
poll函数的返回值errno:
1)EBADF:一个或者多个结构体中定义的文件描述符无效
2)EFAULTfds:指针指向的地址超出进程的地址空间
3)EINTR:请求的事件之前产生的一个信号,调用可以重新发起
4)EINVALnfds:参数超出PLIMIT_NOFILE的值
5)ENOMEM:可用内存不足,无法完成请求
epoll是在Linux2.6内核提出的,是select和poll的增强版,更加灵活,没有文件描述符的限制
int epoll_create(int size) size是要监听的数目
int epoll_ctl(int epfd,int op,int fd,struct epoll_event *event) epoll的事件注册函数
int epoll_wait(int epfd,staruct epoll_event *events,int maxevents,int timeout);
一点解释:
1)size应为最大监听数fd+1,创建好epoll句柄后它就会占用一个fd的值,在Linux下,看
/proc/fd/是可以看到这个fd的值的,所以在使用完epoll后必须close关闭,否则可能造成fd被耗尽
2)ctl的参数说明:
a)create的返回值
b)表示动作,用epoll_ctl_add epoll_ctl_mod epoll_ctl_del三个宏来描述
c)要监听的fd
d)要监听的事件epollin可读 epollout可写 epollpri有紧急数据可读
epollerr 对应fd发生错误
e)fd被挂断
f)将epoll设为边沿处罚
g)epolloneshot只监听一次事件,若需要再次监听,需重新加入到epoll队列
select poll和epoll的区别:
三者都是多路IO复用机制,可以监视多个描述符,一旦某个描述符九尾,可以通知程序进行相应的读写操作,但本质上都是同步IO,他们都需要在读写事件就绪后自己负责读写,而异步IO会把主动数据copy到用户空间
1)一般认为poll比select高效的原因:
a.poll不要求开发组在计算最大文件描述符(fd)时进行+1的操作
b.在应付大数目的fd时速度更快,因为select内核需要大量检测fd对应的fd_set中每一个bit位,会比较耗时
c.select可监控的fd是固定的,也相对较少。poll可创建特定大小的数组来保存fd,不受fd大小的影响,而且可以监控的fd的数目远大于select
d.对select来说,所监控的fd_set在select返回之后会发生变化,在下次进入select之前需要重新初始化需要监控的fd_set,poll将输入和输出分块,允许备件款的文件数组被复用而无需重新初始化
f.select的超时参数在返回时也是未定义的,为考虑移植性,每次超时在下次进入select之前需重新设置超时参数
2)select的优点:
a.可移植性更好,在某些unix系统不支持poll
b.对超时提供了更好的精度,而poll精度较差
3)epoll的优点:
a.支持一个进程打开最大数目的socket描述符FD,可以使用cat /proc/sys/fd/file-max查看
b. IO效率不随FD数目的增加线性下降
c.使用mmap家属内核与用户控件的消息传递