个人学习-Linux-IO多路复用

[1]confirmwz博客:Epoll原理解析https://blog.csdn.net/armlinuxww/article/details/92803381;

[2]hechen知乎专栏: 一文看懂IO多路复用https://zhuanlan.zhihu.com/p/115220699;

weixin_39934085博客: io多路复用的原理和实现_彻底理解 IO 多路复用实现机制https://blog.csdn.net/weixin_39934085/article/details/110715861;

[3]UNIX网络编程:卷1[M].[美]W.Richard Stevens,Bill Fenner, Andrew M.Rudoff著;

IO多路复用,实际就是通过单线程或者单进程检测若干个文件描述符时候可以执行IO的能力;

Linux作为服务端,使用socket和客户端进行通信时,执行顺序是,服务端socket启动,调用监听套接字,然后等待服务器链接;

// 伪代码
int lfd = socket();
// 绑定;
bind();
// 监听;
listen(lfd, 128);
// 等待链接:
int cfd = accept();
/* 使用cfd进行通信 */

​ 多路IO的思路,实际就是把所有需要操作的文件描述符交付内核监控,一旦文件描述符准备就绪,就通知应用程序进行处理,没有就绪,则应用程序阻塞,Linux主要存在三种方式select, poll, epoll 进行管理。(多路指多路网络连接,复用指同一进程)

函数原型:

SYNOPSIS
     #include
     void
     FD_CLR(fd, fd_set *fdset);  // 把文件描述符从fd_set中清楚
     void
     FD_COPY(fd_set *fdset_orig, fd_set *fdset_copy);
     int
     FD_ISSET(fd, fd_set *fdset);  // 判断fd是否在set中
     void
     FD_SET(fd, fd_set *fdset);   // 把fd插入fd_set
     void
     FD_ZERO(fd_set *fdset);  //  把fd_set全赋0进行初始化;
     int
     select(int nfds, fd_set *restrict readfds, fd_set *restrict writefds,
         fd_set *restrict errorfds, struct timeval *restrict timeout);
        /*
            nfds 最大文件描述符;
            readfds : 读文件描述符集;
            writefds: 写文件描述符集;
            errorfds: 标准错误文件描述符集;
            timeout: 检索遍历超时时间;
        */

RETURN VALUES
     select() returns the number of ready descriptors that are contained in the
     descriptor sets, or -1 if an error occurred.  If the time limit expires,
     select() returns 0.  If select() returns with an error, including one due
     to an interrupted call, the descriptor sets will be unmodified and the
     global variable errno will be set to indicate the error.

调用过程:

  • 使用copy_from_user 从客户空间拷贝fd_set到内核空间;
  • 注册回调函数_pollwait();
  • 遍历所有fd, 调用相应poll方法;
  • __pollwait的主要工作,是把current挂进等待队列,不同设备存在不同的等待队列,对于tcp而言,对应port接收一条网络连接就会唤醒队列;
  • poll给fd_set赋值,用来描述文件是否就绪;
  • 把fd_set从内核空间拷贝回用户空间;

内核监控的fd_set被唤起的条件:

​ readfds:该文件描述符里的读缓冲区存在可读数据;

​ writers: 该描述符写缓冲区可写;

​ errorfds: 见识文件错误异常;

缺点:

select本质上是通过设置或者检查存放fd标志位的数据结构来进行下一步处理。这样所带来的缺点是:

  • 单个进程所打开的FD是有限制的,通过 FD_SETSIZE 设置,默认1024 ;
  • 每次调用 select,都需要把 fd 集合从用户态拷贝到内核态,这个开销在 fd 很多时会很大;
  • 对 socket 扫描时是线性扫描,采用轮询的方法,效率较低(高并发)

相关API原型:

 #include
int epoll_create(int size);
int epoll_create1(int flags);
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
typedef union epoll_data {
               void        *ptr;
               int          fd;
               uint32_t     u32;
               uint64_t     u64;
           } epoll_data_t;

struct epoll_event {
               uint32_t     events;      /* Epoll events */
               epoll_data_t data;        /* User data variable */
};
op:
EPOLL_CTL_ADD  // 添加文件描述符和相应event到epfd;
EPOLL_CTL_MOD  // 修改对应文件描述符事件;
EPOLL_CTL_DEL  // 删除相应文件描述符;
events:
EPOLLIN
The associated file is available for read(2) operations.

EPOLLOUT
The associated file is available for write(2) operations.

EPOLLRDHUP (since Linux 2.6.17)
Stream socket peer closed connection, or shut down writing half of connection.  (This flag is especially useful for writing simple  code to detect peer shutdown when using Edge Triggered monitoring.)
EPOLLPRI
There is urgent data available for read(2) operations.

EPOLLERR
Error  condition  happened on the associated file descriptor.  epoll_wait(2) will always wait for this event; it is not necessary to set it in events.

EPOLLHUP
Hang up happened on the associated file descriptor.  epoll_wait(2) will always wait for this event; it is not necessary to set it in events.

EPOLLET
Sets  the  Edge Triggered behavior for the associated file descriptor.  The default behavior for epoll is Level Triggered.  See epoll(7) for more detailed information about Edge and Level Triggered event distribution architectures.

int epoll_wait(int epfd, struct epoll_event *events,int maxevents, int timeout);

调用过程:

1.调用poll_create,Linux会创建一个结构体,每一个epoll对象都会又个epoll_event结构体,来存放添加进来的事件,挂在红黑树下;

2.所有事件都会和设备驱动程序建立回调,相应驱动事件发生时,回调用相应的回调函数;

3.当epoll_ctl添加的fd和event发生时,就把发生的事件复制回用户空间。

优点:

  • 没有最大并发连接的限制,能打开的FD的上限远大于1024(1G的内存上能监听约10万个端口);
  • 效率提升,不是轮询的方式,不会随着FD数目的增加效率下降。只有活跃可用的FD才会调用callback函数;即Epoll最大的优点就在于它只管你”活跃”的连接,而跟连接总数无关,因此在实际的网络环境中,Epoll的效率就会远远高于select和poll;

epoll 有 EPOLLLT 和 EPOLLET 两种触发模式,LT 是默认的模式(水平),ET 是 “高速” 模式(边沿)。

  • LT 模式下,只要这个 fd 还有数据可读,每次 epoll_wait 都会返回它的事件,提醒用户程序去操作;
  • ET 模式下,它只会提示一次,直到下次再有数据流入之前都不会再提示了,无论 fd 中是否还有数据可读。所以在 ET 模式下,read 一个 fd 的时候一定要把它的 buffer 读完,或者遇到 EAGIN 错误。

Original: https://www.cnblogs.com/Albert-lihai/p/16593436.html
Author: Albert_禄遥
Title: 个人学习-Linux-IO多路复用

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/577710/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • gitlab服务yum源安装详细步骤(centos7)

    gitlab服务yum源安装详细步骤(centos7) 概述 GitLab是利用Ruby on Rails一个开源的版本管理系统,实现一个自托管的Git项目仓库,可通过Web界面进…

    Linux 2023年6月8日
    094
  • 【Linux】socket通信编程

    socket通信 * – socket简介 – socket操作API函数 – 代码实现 socket简介 网络层的”ip地址&#8…

    Linux 2023年6月13日
    093
  • 前端之JavaScript—BOM和DOM

    一、BOM和DOM概述 通过之前的两篇文章,相信大家已经掌握了JavaScript的一些简单的语法。但是这些简单的语法,并没有和浏览器有任何交互。也就是我们还不能制作一些我们经常看…

    Linux 2023年6月14日
    087
  • 【MySQL篇】Navicat导入SQL大文件报错终极解决方案

    面对 大数据库文件(一般50M以上),使用Navicat导入的时候容易出现 [ERR]2006等报错问题,此文提供了几种办法,包括修改MySQL的配置参数在网上也有很多详细教程介绍…

    Linux 2023年6月13日
    0106
  • 前端Web实训项目-教务系统成绩查询

    通过暑期前半个月实训,我们选的方向是Web前端(虽然我想选Java全栈的),所以我们最终确立的主题是做一个网页。 这个项目是我们组四个人做的,因为技术水品都不咋样,所以有很多地方需…

    Linux 2023年6月7日
    0118
  • shell脚本

    1、什么是shell 什么是shell ? Shell(外壳) 是一个用 C 语言编写的程序,它是用户使用 Linux 的桥梁。 Shell 既是一种命令语言, 又是一种程序设计语…

    Linux 2023年5月27日
    0102
  • Linux02:Vim使用及账号、磁盘、进程管理(狂神说)

    五、Vim编译器 1.什么是vim编译器 Vim相当于vi的升级版 Vim是从 vi 发展出来的一个文本编辑器。代码补完、编译及错误跳转等方便编程的功能特别丰富,在程序员中被广泛使…

    Linux 2023年5月27日
    0114
  • Java基础系列–02_运算符和程序的语句

    运算符:(1)算术运算符:+,-,*,/,%,++,–(加、减、乘、除、取余、自增,自减)++和–的注意事项:a:他们的作用是自增或者自减b:使用1.单独使…

    Linux 2023年6月7日
    093
  • cache和内存屏障

    1 cache简介 1.1 cache缓存映射规则 tag查看cache是否匹配,set index |tag |set index |block offset ||20-bit …

    Linux 2023年6月6日
    0109
  • 高等代数:5 矩阵的相抵与相似

    5 矩阵的相抵与相似 1、设S,M是两个集合,则集合 ({(a,b)|a \in S,b \in W}) 称为S与M的 笛卡儿积,记作:(S \times M)。 2、定义1:设S…

    Linux 2023年6月8日
    0114
  • JVM学习 类加载子系统

    JVM 哔哩哔哩 尚硅谷视频 宋红康老师 Java代码执行流程 简图 详细图 1、类加载子系统 类加载器子系统的作用 类加载器子系统负责从文件系统或者网络中加载Class文件,cl…

    Linux 2023年6月7日
    0104
  • 在Linux命令行内的大小写转换

    在编辑文本时大小写常常是需要注意的地方,大小写的转换是很枯燥而繁琐的工作,所幸,Linux 提供了很多能让这份工作变得容易的命令。接下来让我们看看都有哪些完成大小写转换的命令。 t…

    Linux 2023年6月14日
    0117
  • B站学习斯坦福大学Swift 语言教程 iOS11 开发【第一集】踩到的几个坑(XCode 13.2.1版本)

    在Xcode 13.2.1 中,找不到从哪里拖拽添加button控件 Xcode13起,添加UI控件需要点击右上方的➕号 button的title属性设置成ghost的emoji后…

    Linux 2023年6月13日
    0103
  • Java常见知识点总结

    1 重载 && 重写 重载: 发生在同一个类中, 方法名必须相同,参数类型不同、个数不同、顺序不同,方法返回值和访问修饰符可以不同,发生在编译时。 重写: 发生在父…

    Linux 2023年6月7日
    099
  • 拓扑排序

    拓扑排序 简介 拓扑排序是将偏序的数据线性化的一种排序方法。复习下偏序和全序的概念: 全序关系是偏序关系的一个子集。 全序是集合内任何一对元素都是可比较的,比如数轴上的点都具有一个…

    Linux 2023年6月13日
    0113
  • Redis配置参数详解

    Redis是一个应用非常广泛的高性能Key-Value型数据库,与memcached类似,但功能更加强大!本文将按照不同功能模块的方式,依次对各个功能模块的配置参数进行详细介绍。 …

    Linux 2023年5月28日
    089
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球