【Linux】高级IO模型

高级IO模型

  • 五种常见的高级IO模型
  • 阻塞IO
  • 非阻塞IO
  • 信号驱动IO
  • 异步IO
  • 多路转接
    • select
    • poll
    • epoll

五种常见的高级IO模型

常见的IO模型为:阻塞IO,非阻塞IO,信号驱动IO,多路转接IO,异步IO。

阻塞IO

在socket编程中,调用recv函数或者是recvfrom函数这类阻塞类型函数时,如果内核并未将数据准备好(数据处于未就绪状态),该函数就会阻塞等待,直到内核已经将数据准备好之后(数据处于就绪状态),函数才会返回。
当操作系统内核没有将数据准备好,函数就会一直阻塞等待,这种IO模型就称之为阻塞IO

非阻塞IO

在进程等待时,调用wait_pid函数,若函数设置option为WNOHANG,那么当调用wait_pid时,如果制定的子进程没有结束,那么函数也会返回,因此需要我们搭配循环来进行使用。
当操作系统内核没有将数据准备好,函数不会等待,也会返回,这种IO模型称之为非阻塞IO

信号驱动IO

对于UDP编程而言,调用recvfrom函数,函数本身是阻塞IO的,但是我们可以将其调整为信号驱动IO
1、将SIGIO信号的信号处理函数进行自定制,和recvfrom函数进行绑定。
2、数据到达接收缓冲器之后,进行SIGIO信号的注册
3、该信号会调用recvfrom函数进行处理
这样的依靠信号来进行IO操作的模型,我们称之为信号驱动IO

异步IO

异步IO是与同步IO相对的
如果一个线程正在进行执行代码,但是遇到一个IO操作,那么就需要等待IO操作完成之后才能进行后续的代码,而在执行IO时,该线程被挂起,而其他需要CPU执行的线程也无法获取CPU,也就是一个IO操作阻塞了当前的线程,并且导致所有的线程都无法继续执行,这就是同步IO。

当代码需要执行一个耗时的IO操作,CPU只需要发送IO指令即可,无需等待IO结果,而是去执行其他代码,当IO返回结果时,再通知CPU去处理。

多路转接

主要有三种方式:select,poll,epoll
其目的都是帮助用户监控文件描述符对应的事件,如果产生了对应的事件,则通知用户进行处理

select

使用事件集合的方式,分为读事件集合,写事件集合,异常事件集合,监控上线是1024个文件描述符,监控成功后返回就绪的事件集合,并且将未就绪的文件描述符从事件集合中去掉,再次监控时需要重新添加
缺点:监控效率低,O(n),监控的文件描述符有上限1024个

优点:支持跨平台

poll

使用事件结构的方式,需要监控的文件描述符以及关心的事件一起放入事件结构中,事件结构数组的大小不受限
缺点:监控效率低,O(n),不支持跨平台

epoll

使用事件结构的方式,需要监控的文件描述符和关系的事件一起放入事件结构中,并且多个事件结构是被放在红黑树中的,未就绪的事件结构是在红黑树中的,当某个事件就绪之后,就会放入一个就绪的双向链表中,程序员调用epoll_wait函数从双向链表中拿就绪的事件结构即可

优点:使用事件结构的方式,无需重新赋值,采用红黑树进行遍历,遍历的效率高,并且使用双向链表存储已就绪的事件,后续只要调用epoll_wait函数查看双向链表中是否有数据即可

缺点:不支持跨平台

epoll的两种工作模式:ET模式(边缘触发),LT模式(水平触发)

ET模式:如果某个事件触发epoll监控,用户去处理了,但是没有完全处理完,那么下次再次进行监控时,该事件不会再被放入双向链表中,也就是说下次epoll不会再返回该事件结构了。
例如:当监控的文件描述符有了我们关心的读事件就绪,如果有10k的数据被写到了该文件描述符对应的缓冲区,那么调用epoll_wait函数时,会收到对应的事件结构,如果我们只拿走了5k,那么下次再进行epoll_wait读取时,该事件结构就不会再被获取到

LT模式:如果某个事件触发epoll监控,用户去处理了,但是没有完全处理完,那么下次再次进行监控时,该事件会再次被放入双向链表中,也就是说下次epoll依然会再返回该事件结构。
例如:当监控的文件描述符有了我们关心的读事件就绪,如果有10k的数据被写到了该文件描述符对应的缓冲区,那么调用epoll_wait函数时,会收到对应的事件结构,如果我们只拿走了5k,那么下次再进行epoll_wait读取时,该事件结构会再被获取到

你可能感兴趣的:(linux,linux,服务器)