Linux高级IO_select、epoll

调用send/write、read/recv这些IO接口进行网络通信时,需要等待IO条件满足(IO事件就绪)才能正常拷贝数据。比如调用send/write需要等待TCP的发送缓冲区有剩余空间才能将数据拷贝到TCP发送缓冲区中,调用read/recv需要等待TCP的接收缓冲区有数据才能将数据拷贝到应用层,即IO=等+数据拷贝,而这些等待是由用户来完成的,高级IO就是降低用户等的时间以提高IO效率。常见的五种IO模型有:阻塞IO、非阻塞IO、信号驱动IO、多路复用/多路转接、异步IO(让操作系统进行IO)。前四种模型是同步IO,最后一种模型是异步IO。同步还是异步取决于进程有没有参与等+数据拷贝的过程。

一.阻塞IO

二.非阻塞IO

我们如果调用read函数从0号文件描述符中读数据时,如果不输入数据,那么程序就会进入阻塞状态。如果想让这个接口进行非阻塞IO,我们可以调用fcntl()系统调用接口将该文件描述符设置为非阻塞状态。
image.png

  1. int fd:想要设置的那个文件描述符
  2. int cmd:
    1. 如果传入F_GETFL :返回一个位图,获取当前文件属性
    2. 如果传入F_SETFL: 可以设置文件的属性,可以设置O_NONBLOCK将该文件描述符的属性设置为非阻塞
  3. 一旦将文件描述符设置为非阻塞状态,那么它的返回状态有4种情况
    1. 成功,返回值大于0
    2. 读到文件末尾,返回值等于0
    3. 读失败,IO事件没有就绪,错误码被设置为11(EAGAIN or EWOULDBLOCK)或者(EINTER)代表这次IO被信号中断,需要重新读取
    4. 读失败,真正读失败,错误码不等于EAGAIN or EWOULDBLOCK or EINTER
#include <iostream>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>int main()
{char buff[128] = {0};int sl = fcntl(0, F_GETFL);if (sl < 0) {perror("fcntl");abort();}fcntl(0, F_SETFL, O_NONBLOCK|sl);while (true){printf("please enter# ");fflush(stdout);ssize_t n = read(0, buff, sizeof(buff)-1);if (n > 0) {buff[n-1] = 0;printf("echo# %s\n", buff);  }   else if (n == 0){std::cout << "读到文件末尾!" << std::endl;break;}else{if (errno == EAGAIN || errno == EWOULDBLOCK){sleep(1);std::cout << "数据没有准备好 " << std::endl;continue;}else if (errno == EINTR){std::cout << "这次IO被信号中断,重新读取 " << std::endl;continue;}else {std::cout << "读取失败 " << std::endl;break;}}}return 0;
}

三.多路复用/多路转接

read/write系统调用,一次只能等待一个文件描述符,而接下来的select,poll、epoll可以一次等待多个文件描述符,这不同于read和write,因为select、poll、epoll只会进行等待,拷贝数据还是io系统调用完成。

3.1 select

如果要使用select进行等待多个文件描述符,我们就必须先得知道哪些文件描述符要被等待,所以我们使用select接口时,必须先设置文件描述符集。

#include <sys/select.h>
void FD_CLR(int fd, fd_set* set); 将set中的fd设置为0
void FD_ISSET(int fd, fd_set* set); 判断set中是否存在fd
void FD_SET(int fd, fd_set* set); 将set中的fd设置为1
void FD_ZERO(fd_set* set); set全部置0

int select (int nfds, fd_set* readfds, fd_set* writefds, fd_set* exceptfds, struct timeval* timeout);

  • 返回值:
    • n > 0 ,有n个fd就绪
    • n==0 ,timeout
    • n < 0 等待失败
  • int nfds:等待的多个fd中,最大的fd+1

下面四个参数是输入输出型参数,你可以设置值给系统,操作系统也可以设置值返回给用户。

  • fd_set* readfds:
    • 用户要告诉内核,哪些fd的读事件需要被等待
    • 内核要告诉用户,哪些fd的读事件已经就绪
  • fd_set* writefds:
  • fd_set* exceptfds:

struct timeval
{

time_t tv_sec;
suseconds_t tv_usec:
}

  • struct timeval* timeout:
    • 如果设置为nullptr,则阻塞等待,如果等待的文件描述符中有一个没有就绪,那么select就阻塞。
    • tv_sec:tv_usec = 0:0 ,非阻塞等待
    • tv_sec:tv_usec = n:0 , n秒以内阻塞等待,否则timeout一次
    • 输出:表示剩余时间

缺点:

  1. 每次调用select时,都需要手动设置fd集合,接口使用不方便
  2. 每次调用select时,都需要将fd集合从用户态拷贝到内核态
  3. 每次调用select时,都需要在内核中遍历传递进来的fd集合
  4. select支持的文件描述符数量太少

第二种多路转接方案poll一定程序上解决了select的缺点,将输入输出参数分离,于是就不需要手动设置fd集合,接口使用方便许多,同时也解决了select文件描述符数量太少的缺点。

3.2 poll

image.png

  1. 返回值:
    1. 大于0,几个fd就绪
    2. =0,timeout
    3. <0 ,等待出错

image.pngimage.png

  1. struct pollfd* fds :等待的fd集合,可以设置检测文件描述符的哪些事件,events可以设置关心哪些事件,revents表示哪些事件触发。
    1. POLLIN:读就绪
    2. POLLOUT:写就绪
  2. nfds_t nfds:fds集合的长度
  3. int timeout:单位毫秒
    1. -1:阻塞等待
    2. 0:非阻塞等待
    3. 0:阻塞等待n秒,然后timeout一次

第三种多路复用技术epoll,解决了上述select和poll的缺点。

3.3 epoll
  1. epoll接口

创建epoll模型:image.png

  • int size:设置一个大于0的值就行,这个参数被忽略
  • 返回值:返回一个epoll文件描述符

控制epoll模型:image.png

功能:用户告诉内核哪个fd的什么事件需要被关心

  • int epfd:epoll_create的返回值
  • int op:对epoll模型的操作
    • EPOLL_CTL_ADD
    • EPOLL_CTL_DEL
    • EPOLL_CTL_MOD
  • int fd:操作的目标fd
  • event:关心的事件

等待事件就绪:

image.png

  • int epfd:对某个epoll模型操作
  • struct epoll_event* events:输出型参数,告诉程序员哪些事件就绪
  • int maxevents:events的长度
  • int timeout:效果同timeout

image.png

events:填写下面
image.png

  1. epoll原理

在我们调用epoll_create时,操作系统会为我们创建一个epoll模型,这个模型中有包含三个机制:红黑树、就绪队列、回调机制。所谓epoll模型就是一个结构体,当你调用epoll_create时,os会创建一个结构体,然后创建一个struct file将这个epoll结构体放入这个文件结构体中,给用户返回一个文件描述符,用户就可以通过文件描述符来找到这个epoll模型,进而对这个epoll模型进行操作。这三个机制解决了select和poll的缺点。这颗红黑树就相当于在select和poll中用户定义的第三方fd表,程序员调用epoll_ctl可以操作这棵树;就绪队列保存了已经就绪的文件描述符。当底层有文件就绪时,文件结构体内的回调函数就会被调用,将红黑树中的节点链入就绪队列中,这不像select和poll还需要每次遍历文件描述符集才能确定哪个文件就绪,时间复杂度从n变为了1。当程序员调用epoll_wait接口时,其底层只需要判断就绪队列是否为空即可,也不需要遍历检测。
image.png
让我们回想下select的缺点,1.用户需要每次手动设置关心的fd 2.需要将第三方fd数组拷贝到内核 3.内核在底层要遍历fd集 4.fd数量太少 。其中1和4这两个缺点poll解决了,但是2和3这两个缺点是select和poll都有的,而epoll在底层实现了红黑树解决了缺点2,回调机制解决了缺点3。所以epoll的效率极高。

  1. epoll工作模式

在我们使用select、poll、epoll时,如果有事件就绪,但是没有取走这个数据,那么底层就会一直通知事件就绪。 这种方式是epoll默认的工作模式:LT模式(水平触发),另外epoll还有一种工作模式:ET(边缘触发)

  • ET模式:有效通知只有一次,数据变化时才会通知一次。这种通知方式的效率显然要比LT模式高,因为LT模式在单位时间内,一直在做重复的通知。因为ET会倒逼上层,尽快取走数据,即循环调用recv接口进行非阻塞读取,直到读到的数据小于期望值,那么说明底层数据已经读取完毕。

LT模式,既可以在阻塞模式下工作也可以在非阻塞模式下工作,ET模式就只能在非阻塞模式下工作。对于LT模式和ET模式的IO效率谁高谁低,是要看LT的工作模式是非阻塞还是阻塞。

只要缓冲区中的剩余空间变多,那么TCP报文给对方通知的窗口大小也会变化,对方的滑动窗口也有可能变大,对方发送的数据就会变多,IO效率就会变高

  • ET的应用场景是高IO,LT是要求响应及时的场景

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/543557.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java双非大二找实习记录

先说结论&#xff1a;2.22→3.6线上线下面了七家&#xff0c;最后oc两家小公司&#xff0c;接了其中一个。 本人bg&#xff1a; 真名不经传双非一本&#xff0c;无绩点无竞赛无奖项无实习&#xff0c;23年12月开始学java。若非要说一点相关的经历&#xff0c;就是有java基础&…

Python+Selenium- 环境搭建

一&#xff0c;Selenium 简介 Selenium是目前最流行的web自动化测试工具&#xff0c;也常用于网络爬虫&#xff0c;已经更新到3以上的版本。 1&#xff0c;组件 它提供了以下web自动化测试组件&#xff1a; Selenium IDE&#xff0c;Firefox浏览器的一个插件&#xff0c;提供…

CSS学习(2)-盒子模型

1. CSS 长度单位 px &#xff1a;像素。em &#xff1a;相对元素 font-size 的倍数。rem &#xff1a;相对根字体大小&#xff0c;html标签就是根。% &#xff1a;相对父元素计算。 注意&#xff1a; CSS 中设置长度&#xff0c;必须加单位&#xff0c;否则样式无效&#xff…

JavaScript入门-引入方式-基础语法

JavaScript-引入方式 引入方式1 <script >... ...</script> 在.html文件内部任何位置引入都可以 引入方式2 <script src"... ..."></script> 在.html文件外部创建js文件夹在文件夹里面创建.js文件 基础语法 书写语法 // 弹出警告窗 wind…

印度交易所股票行情数据API接口

1. 历史日线 # Restful API https://tsanghi.com/api/fin/stock/XNSE/daily?token{token}&ticker{ticker}默认返回全部历史数据&#xff0c;也可以使用参数start_date和end_date选择特定时间段。 更新时间&#xff1a;收盘后3~4小时。 更新周期&#xff1a;每天。 请求方式…

VMware虚拟机硬盘容量扩容方法

扩容后不会影响原文件。亲测有效&#xff0c;高效便捷 - 在关机状态下&#xff0c;先在VM上直接扩容硬盘容量&#xff0c;输入扩容后的硬盘最大容量 注意&#xff0c;如果想在原硬盘上增加容量&#xff0c;需要将原来的快照都删除 - 输入最大磁盘大小 运行虚拟机进入系统&…

docker搭建vulfocus靶场

靶场搭建的前提是具备docker容器的环境 环境准备&#xff1a; 在kali上安装docker 先是进行软件和源更新 sudo apt-get update开始安装 sudo apt-get install -y docker.io设置开机自启动 sudo systemctl enable docker --now查看状态 sudo systemctl status docker给当…

B端界面又丑又乱,也不会总结规范,来,我给5个规范模板,照着学

发5个别人总结的规范&#xff0c;一定会对你的B端系统改进&#xff0c;有帮助的。

leedcode刷题--day8

28 最长回文子串 采用动态规划的方式 首先如果子串长度为1的话&#xff0c;一定是回文子串如果子串长度为2&#xff0c;那如果第一个字母第二个字母&#xff0c;即为回文子串如果bababd中babab为回文子串&#xff0c;那么除去左边界和右边界后的也是回文子串 class Solution(…

ping和telnet的区别

ping是ICMP协议&#xff0c;只包含控制信息没有端口&#xff0c;用于测试两个网络主机之间网络是否畅通 telnet是TCP协议&#xff0c;用于查看目标主机某个端口是否开发。 总结&#xff1a;ping是物理计算机间的网络互通检查&#xff0c;telnet是应用服务间的访问连通检查&am…

应对磁盘管理挑战:Linux磁盘分区挂载命令实践指南

前言 在今天的技术世界中&#xff0c;Linux已成为广泛使用的操作系统之一&#xff0c;而对于运维人员和开发人员来说&#xff0c;磁盘分区挂载是一个至关重要的任务。正确地管理和配置磁盘分区挂载可以极大地提升系统的性能和可靠性&#xff0c;同时也能确保数据的安全性。 通…

18. UE5 RPG初始化角色的属性

在7. UE5 RPG修改GAS的Attribute的值中&#xff0c;我们创建AS的时候&#xff0c;也设置了默认值&#xff0c;但是这种方法是在初始化AS的时候创建的&#xff0c;无法进行个性化定义&#xff0c;因为我们创建的所有的角色的属性不可能相同&#xff0c;所以&#xff0c;我们需要…