首页 > 系统相关 >Linux之select、poll、epoll讲解

Linux之select、poll、epoll讲解

时间:2023-05-09 14:47:20浏览次数:50  
标签:epoll 描述符 fd 内核 poll select

目录

 

1 select、poll、epoll

1.1 引言

操作系统在处理io的时候,主要有两个阶段:

  • 等待数据传到io设备
  • io设备将数据复制到user space

我们一般将上述过程简化理解为:

  • 等到数据传到kernel内核space
  • kernel内核区域将数据复制到user space(理解为进程或者线程的缓冲区)

selectpollepoll都是IO多路复用的机制。I/O多路复用就通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。但selectpollepoll本质上都是同步I/O,因为他们都需要在读写事件就绪后自己负责进行读写,也就是说这个读写过程是阻塞的,而异步I/O则无需自己负责进行读写,异步I/O的实现会负责把数据从内核拷贝到用户空间

1.2 IO和Linux内核发展

1.2.1 整体概述

整体关系流程:
请添加图片描述

查看进程文件描述符:

获取pid进程号
ps -ef 
查看文件描述符
cd  /proc/进程号/fd ; ll

或者查看当前进程的fd  
$$ 表示 Shell 本身的 PID  (ProcessID)
cd  /proc/$$/fd ; ll

1.2.2 阻塞IO

计算机是有内核(kernel)的,内核向下连接很多的客户端,内核向上连接进程或线程,早先内核通过read命令读取文件描述符(fd),在这个时期socketblocking(阻塞的)BIO

如下图所示:线程通过内核读取文件fd8,读取到用户空间后,在通过内核写入文件fd9,如果fd8阻塞了,它会阻挡后面的操作
请添加图片描述

1.2.3 非阻塞IO

socket fd nonblock(非阻塞),进程/线程用一个,用循环遍历文件描述符(轮询发生在用户空间),这个时期是同步非阻塞时期NIO
这是由于内核socket本身就是nio,同步非阻塞IO
请添加图片描述

1.2.4 select

如果有1000个文件描述符fd,代表用户进程轮询调用1000次内核(kernel),造成成本很大的问题。于是在内核中增加了一个系统调用select,用户空间调用新的系统调用,统一将所有的文件描述符传给select,内核监控文件描述符的完成度,文件描述符完成之后返回,返回之后还有系统调用,再调用read(有数据的文件描述符),这个叫多路复用NIO,在这个时期,文件描述符考来考去成为累赘;
在这里插入图片描述

1.2.5 共享空间

共享空间是进程用户空间一部分,也是内核空间的一部分
引入一个共享空间mmap,将文件描述符放在共享空间里,文件描述符放在共享空间的红黑树里,将资源齐全的文件描述符放到链表
在这里插入图片描述

1.2.6 零拷贝

什么是零拷贝

在操作系统中,使用传统的方式,数据需要经历几次拷贝,还要经历用户态/内核态切换

  1. 从磁盘复制数据到内核态内存;
  2. 从内核态内存复制到用户态内存;
  3. 然后从用户态内存复制到网络驱动的内核态内存;
  4. 最后是从网络驱动的内核态内存复制到网卡中进行传输。
    在这里插入图片描述
    所以,可以通过零拷贝的方式,减少用户态与内核态的上下文切换和内存拷贝的次数,用来提升I/O的性能。零拷贝比较常见的实现方式是mmap,这种机制在Java中是通过MappedByteBuffer实现的。
    在这里插入图片描述
    sendfile,是完成零拷贝的命令,两个参数一个写出io,一个读入io
    在之前是先读取文件到用户空间,再写到内核中去,有了sendfile后,用这一个命令就可以了,不用读取写入
    在这里插入图片描述

1.3 select

1.3.1 简介

单个进程就可以同时处理多个网络连接的io请求(同时阻塞多个io操作)。基本原理就是程序呼叫select,然后整个程序就阻塞状态,这时候,kernel内核就会轮询检查所有select负责的文件描述符fd,当找到其中那个的数据准备好了文件描述符,会返回给selectselect通知系统调用,将数据从kernel内核复制到进程缓冲区(用户空间)
在这里插入图片描述
下图为select同时从多个客户端接受数据的过程
虽然服务器进程会被select阻塞,但是select会利用内核不断轮询监听其他客户端的io操作是否完成
在这里插入图片描述

1.3.2 select缺点

select的几大缺点:

  • 每次调用select,都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多时会很大
  • 同时每次调用select都需要在内核遍历传递进来的所有fd,这个开销在fd很多时也很大
  • select支持的文件描述符数量太小,默认是1024
  • select返回的是含有整个句柄的数组, 应用程序需要遍历整个数组才能发现哪些句柄发生了事件

1.4 poll介绍

1.4.1 与select差别

poll的原理与select非常相似,差别如下:

  • 文件描述符fd集合的方式不同,poll使用pollfd 结构而不是select结构fd_set结构,所以poll是链式的,没有最大连接数的限制
  • poll有一个特点是水平触发,也就是通知程序fd就绪后,这次没有被处理,那么下次poll的时候会再次通知同个fd已经就绪。

1.4.2 poll缺点

poll的几大缺点:

  • 每次调用poll,都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多时会很大
  • 每次调用poll都需要在内核遍历传递进来的所有fd,这个开销在fd很多时也很大

1.5 epoll

1.5.1 epoll相关函数

epoll:提供了三个函数:

  • int epoll_create(int size);
    建立一个 epoll 对象,并传回它的id
  • int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
    事件注册函数,将需要监听的事件和需要监听的fd交给epoll对象
  • int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
    等待注册的事件被触发或者timeout发生

1.5.2 epoll优点

epoll解决的问题:

  • epoll没有fd数量限制
    epoll没有这个限制,我们知道每个epoll监听一个fd,所以最大数量与能打开的fd数量有关,一个g的内存的机器上,能打开10万个左右
    cat /proc/sys/fs/file-max可以查看文件数量
  • epoll不需要每次都从用户空间将fd 复制到内核kernel
    epoll在用epoll_ctl函数进行事件注册的时候,已经将fd复制到内核中,所以不需要每次都重新复制一次
  • select 和 poll 都是主动轮询机制,需要遍历每一个fd
    epoll被动触发方式,给fd注册了相应事件的时候,我们为每一个fd指定了一个回调函数,当数据准备好之后,就会把就绪的fd加入一个就绪的队列中,epoll_wait的工作方式实际上就是在这个就绪队列中查看有没有就绪的fd,如果有,就唤醒就绪队列上的等待者,然后调用回调函数。
  • 虽然epoll 需要查看是否有fd就绪,但是epoll之所以是被动触发,就在于它只要去查找就绪队列中有没有fd,就绪的fd是主动加到队列中,epoll不需要一个个轮询确认。
    换一句话讲,就是selectpoll只能通知有fd已经就绪了,但不能知道究竟是哪个fd就绪,所以selectpoll就要去主动轮询一遍找到就绪的fd。而epoll则是不但可以知道有fd可以就绪,而且还具体可以知道就绪fd的编号,所以直接找到就可以,不用轮询。
  • 我们在调用epoll_create时,内核除了帮我们在epoll文件系统里建了个file结点,在内核cache里建了个红黑树用于存储以后epoll_ctl传来的socket外,还会再建立一个list链表,用于存储准备就绪的事件,当epoll_wait调用时,仅仅观察这个list链表里有没有数据即可。有数据就返回,没有数据就sleep,等到timeout时间到后即使链表没数据也返回。所以,epoll_wait非常高效

这个准备就绪list链表是怎么维护的呢?

当我们执行epoll_ctl时,除了把socket放到epoll文件系统里file对象对应的红黑树上之外,还会给内核中断处理程序注册一个回调函数,告诉内核,如果这个句柄的中断到了,就把它放到准备就绪list链表里;当一个socket上有数据到了,内核在把网卡上的数据copy到内核中后就来把socket插入到准备就绪链表里了

​ 一颗红黑树,一张准备就绪句柄链表,少量的内核cache,就帮我们解决了大并发下的socket处理问题。执行epoll_create时,创建了红黑树就绪链表,执行epoll_ctl时,如果增加socket句柄,则检查在红黑树中是否存在,存在立即返回,不存在则添加到树干上,然后向内核注册回调函数,用于当中断事件来临时向准备就绪链表中插入数据。执行epoll_wait时立刻返回准备就绪链表里的数据即可

标签:epoll,描述符,fd,内核,poll,select
From: https://www.cnblogs.com/Bkxk/p/17384968.html

相关文章

  • jquery select 操作
    //jQuery获取Select选择的Text和Value:varcheckText=jQuery("#select_id").find("option:selected").text();//获取Select选择的TextvarcheckValue=jQuery("#select_id").val();//获取Select选择的optionValuevarcheckIndex=jQuery("#sel......
  • CodeForces - 630F Selection of Personnel (组合数学)
    TimeLimit: 500MS MemoryLimit: 65536KB 64bitIOFormat: %I64d&%I64uCodeForces-630FSelectionofPersonnelSubmit StatusDescriptionOnecompanyofITCitydecidedtocreateagroupofinnovativedevelopmentsconsistingfrom 5 to 7 peopleandhir......
  • Linux - IO多路复用之select
    1.IO多路转接(复用) IO多路转接也称为IO多路复用,它是一种网络通信的手段(机制),通过这种方式可以同时监测多个文件描述符并且这个过程是阻塞的,一旦检测到有文件描述符就绪(可以读数据或者可以写数据)程序的阻塞就会被解除,之后就可以基于这些(一个或多个)就绪的文件描述符进行......
  • Linux - IO多路复用之epoll
    1.epoll概述epoll全称eventpoll,是linux内核实现IO多路转接/复用(IOmultiplexing)的一个实现。IO多路转接的意思是在一个操作里同时监听多个输入输出源,在其中一个或多个输入输出源可用的时候返回,然后对其的进行读写操作。epoll是select和poll的升级版,相较于这两个......
  • Linux - IO多路复用之poll
    1.poll函数poll的机制与select类似,与select在本质上没有多大差别,使用方法也类似,下面的是对于二者的对比:内核对应文件描述符的检测也是以线性的方式进行轮询,根据描述符的状态进行处理poll和select检测的文件描述符集合会在检测过程中频繁的进行用户区和内核区的拷贝,......
  • elSelect点击空白处无法收起下拉框(失去焦点并隐藏)
    学习记录,为了以后有同样的问题,省得再百度了,方便自己也方便你们element中多选的select有个问题,就是点击空白或者关闭弹窗,下拉还会一直展示出来百度了好一会,觉得下面两位大佬说的最合理,然后就搬运了下由于我这边业务很简单,然后就不想全局折腾参考大佬链接地址https://www.jb51.cc/......
  • 多路复用epoll
    epoll基本原理epoll相对于select与poll有较大的不同,主要是针对前面两种多路复用IO接口的不足与select/poll方案对比select方案使用数组存储文件描述符,最大支持1024select每次调用都需要将描述符集合拷贝到内核中,非常消耗资源poll方案解决文件描述符存储数量......
  • 在vue3中使用elementPlus的el-select时样式穿透问题
    下拉框的option样式只能在全局样式里改,千万不能用scope,否则不生效<el-selectclass="select":popper-append-to-body="false"v-model="selectValue"placeholder="请选择"popper-class="select-option"><......
  • 分布式部署 apollo
    一台服务器部署多环境的apollo以windows环境为例,linux环境类似部署方式前置条件java环境、MySQL、gitBash、eureka注册中心apollo官网https://www.apolloconfig.com/一、下载安装包、数据库sql下载地址:https://github.com/apolloconfig/apollo/releases1.1下载安装包三个安......
  • C# Lambda表达式select()和where()的区别
    1、where()用法:必须加条件,且返回对象结果。string[]arrays={"asd","abc","bbb","ccc"};varresults=arrays.Where(a=>a.Contains("b"));//必须加条件,返回对象2、select()用法:(1)(a=>a.Value=="22")加条件查询时,返回bool型结果;(2)(a=......