五种IO模型
- 01.五种IO模型介绍
首先我们必须了解,任何IO过程都需要两个部分的来完成,第一步等待数据的到来,将数据从内核空间拷贝到用户空间。
1.阻塞式IO
a. 可以看到阻塞是IO整个过程都在等待,等到数据从内核拷贝到用户空间,才进行返回,处理数据。
b. 往往等待数据这个时间花费时间最多。
2.非阻塞式IO
a.非阻塞式IO往往需要我们系统不能阻塞的等待数据,需要向下进行一些必要的逻辑
b.由于非阻塞的原因,往往需要轮询的方式来进行调用。
c. 可以看到非阻塞式并没有一直阻塞在系统调用,而是不断返回,但是只有数据从内核拷贝到用户才返回成功。
3.信号驱动IO
a. 信号驱动IO是通过信号的捕捉来提示数据准备好了,而不是IO胸痛调用自己去等待,这样就节省了大量的等待时间
b. 信号驱动只花费了将数据从内核拷贝到用户空间的时间。
4.IO多路复用
a. 可以看到IO多路复用似乎和阻塞式没有什么不同,都是先等待数据的准备,然后将数据从内核拷贝到用户。
b. 但是可以发现select可以监听多个文件描述符的就绪
5.异步IO
a. 可以看到异步IO是在内核拷贝到用户空间之后,才通知应用程序,你该去处理数据了。
- 02.三种IO多路复用
- select
a. fd_set,这个结构其实就相当于一个位图,通过一系列函数将想要监听的文件描述符设置上去。
b. nfds是检测文件描述符中最大值+1,用fds来进行存储,那么就说明检测的文件描述符有上限,即 = sizeof(fd_set) * 8
c. 每次将fd加入集合中时需要一个额外的数组array将fd进行存储
d.一是, 每次select返回时需要用array进行比较,判断哪个文件描述符就绪,
e.二是,每次select调用时都需要将array数据拷贝到内核,遍历这个array,将fd重新设置上去,因为每次select调用时都会默认清空其所有集合,于此同时将最大的fd选出来+1,最为其第一个参数。
综上select的缺点:
1.可检测的文件描述符有上限。
2.每次调用select调用都需要遍历一遍fd集合将fd重新设置上去
3.每次都需要将fd集合从用户态拷贝到内核态,消耗很多
4.select的接口比较麻烦,需要手动设置fd
注意:之所以每次都需要把fd集合拷贝到内核是因为,每次select返回都会清空原来的设置的,将就绪文件描述符设置到fd_set上的返回(这里便将原来的设置的覆盖了),所以就必须要遍历一遍,完成重新设置。fd_set是输入输出型参数。
2.poll
pollfd的取值:
a. 从pollfd的结构可以知道,他将select的三个结构整合成一个结构
b. 应为是一个列表,nfds描述列表的长处,所以理论上检测文件描述符没有限制
pol的优缺点:
1. 和select相比,它是通过列表和列表长度来决定检测文件描述符个数,所以理论上讲检测的文件描述符没有上限(内存足够大)。
2. 但是和select一样,poll返回式需要遍历pollfd列表来确定哪个文件描述符就绪了。
3. 每次调用poll时都需要将大量pollfd从用户态拷贝到内核态,完成pollfd的初始化。
3.epoll
event机构
a. 每次调用一个epoll_create,Linux都会在内核创建一个eventpoll结构体,即每个epoll对象都会有一个对应的eventpoll
b. 每个epoll对象对每一个事件都会创建一个epitem结构。
c. 每次将检测的fd的事件加入到eventpoll机构中红黑树。
d. 所有添加到epoll中的事件都通过与网卡建立回调函数,当事件发生时,就会调用回调函数,然后回调函数将事件加入队列中。
e. epoll_wait在队列中查找有没有又没有epitem元素即可,如果不为空则直接到队列中将发生的事件拷贝到用户态即可(O(1)).
epoll的优缺点
1. 更好的接口,用了三个函数完成
2. 采用回调机制,一旦文件描述符就绪了,就会立刻激活这个文件描述符,而不是遍历数组,才知道哪路文件描述符就绪。
3.每次文件描述符就绪了,调用epoll_wait去在就绪队列中取事件,这个操作时(O(1))
4.用了m’map的方式将就绪队列映射到用户态,这样就不用将事件拷贝到用户态了。