Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >什么是select/poll/epoll模型?

什么是select/poll/epoll模型?

作者头像
友儿
发布于 2023-03-11 09:38:59
发布于 2023-03-11 09:38:59
32300
代码可运行
举报
文章被收录于专栏:友儿友儿
运行总次数:0
代码可运行

模型图

单线程处理

  • 我们自己会怎样写单线程处理多个客户端连接呢?我们知道在linux里面中每个网络连接在内核中都是文件描述符(Fd)的形式存在,为了使大家看得明白,我们使用一段伪代码来编写一个单线程网络服务器,以下伪代码中我们需要用程序判断当前Fdx是否有数据,这个其实过程还是有些慢的,下面让我们看一下select/poll/epoll的原代码是怎么写的。
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
while (1){
    for(Fdx in (Fd1~Fd5)) { # Fdx 为当前遍历到的文件;Fd1~Fd5为这5个网络连接在内核中的文件描述符
        if (Fdx) { # 是否有数据
            # 读取Fdx并处理数据
        }
    }
}

select 函数源码

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
# 准备文件描述符的数组 Fds

# 创建socket服务端
sockfd = socket(AF_INET, SOCK_STREAM, 0);
memset(&addr, 0, sizeof (addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(2000);
addr.sin_addr.s_addr = INADDR_ANY;
bind(sockfd,(struct sockaddr*)&addr ,sizeof(addr));
listen (sockfd, 5); 

# 创建了5个文件描述符
for (i=0;i<5;i++) 
  {
    memset(&client, 0, sizeof (client));
    addrlen = sizeof(client);
    fds[i] = accept(sockfd,(struct sockaddr*)&client, &addrlen);
    # 计算出一个最大的文件描述符
    if(fds[i] > max)
        max = fds[i]; 
  }

  # 通过以上程序我们准备好了一个文件描述符的集合假如是1 3 6 7 9 和 一个最大的文件描述符 9

  while(1){
    FD_ZERO(&rset);
      for (i = 0; i< 5; i++ ) {
          FD_SET(fds[i],&rset);
      }

       puts("round again");
  # select参数依次含义:最大的文件描述符 + 1 (用于内核截取文件描述符); 读文件描述符集合; 写文件描述符集合;异常文件描述符集合; 超时时间;  
                 
  # rset参数接受的并不是Fd集合,而是接受了FD_SETFD_SET的类型是一个bitmap
  # 假如我们文件描述符集合是 1 3 6 7 9,那么我们bitmap里面存的值就是0101001101000...  (102401的位置坑位)。
  
  # 无数据时select阻塞
    select(max+1, &rset, NULL, NULL, NULL);
  # 有数据时select会把Fd置位并返回向下执行。
    
    for(i=0;i<5;i++) {
                # 循环判断哪个Fd被置位了,然后读取数据并且处理
        if (FD_ISSET(fds[i], &rset)){
            memset(buffer,0,MAXBUF);
            read(fds[i], buffer, MAXBUF);
            puts(buffer);
        }
    }    
  }
  • select 函数执行过程过程中会把用户态空间rset的bitmap类型的数据复制到内核态一份,由内核判断Fd是否有数据。我们知道用户态程序判断效率一定是低于内核去判断的,因为用户态判断也是在询问内核,它是有一个用户态到内核态的切换的,判断每一个Fd都需要用户态到内核态的切换。如果没数据的话,select函数是一个阻塞函数,会一直阻塞在select所在的行。如果有数据的话,内核会把有数据的Fd置位(可以理解为标识成有数据)并且返回,不在阻塞。
select 缺点
  • rset 的 bitmap 类型有大小限制1024位,即FD_SET的大小限制为1024位
  • FD_SET是不可重用,rset 的数据会被内核置位修改,每次while循环的时候都需要重新赋值
  • 虽然是比我们在用户态判断的消耗好些,但是 rset 数据从用户态复制到内核态仍然是有很大的开销的
  • select 函数返回的时候说明至少有一个Fd有数据(被置位了),但是我们并不知道是哪一个Fd或者那些Fd被置位了,需要我们再去遍历一边哪个被置位了,需要o(n)的复杂度的。

poll 函数源码

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
# 声明结构图pollfd

struct pollfd {
      int fd;
      short events; 
      short revents;
};
  # 构造pollfd数据
  for (i=0;i<5;i++) 
  {
    memset(&client, 0, sizeof (client));
    addrlen = sizeof(client);
    pollfds[i].fd = accept(sockfd,(struct sockaddr*)&client, &addrlen);
    # POLLIN 读事件
    pollfds[i].events = POLLIN;
  }
  sleep(1);

  while(1){
      puts("round again");
        # 传入pollfds 结构体
        # 5 传入的数量
        # 50000 超时时间
        # 也是阻塞函数
    poll(pollfds, 5, 50000);
         
    for(i=0;i<5;i++) {
        if (pollfds[i].revents & POLLIN){
            pollfds[i].revents = 0;
            memset(buffer,0,MAXBUF);
            read(pollfds[i].fd, buffer, MAXBUF);
            puts(buffer);
        }
    }
  }
  • 同select一样,poll函数也阻塞函数,只不过poll传入的是一个数组类型的结构体,poll函数也会把用户态数据复制到内核态置位,但是select置位的是bitmap(导致不可从重用),而poll置位的是结构体中的revents(初始化为0,有数据置位为POLLIN)字段。同理,无数据时poll堵塞,有数据时执行读操作并处理数据,并把revents设置为0,所以pollfd数据并没有发生改变可以复用。
poll函数解决了select的哪些问题
  • pollfd这种结构体,解决了bitmap 1024位的限制
  • 结构体的revents字段在有数据是被置位(POLLIN),我们遍历读取的时候重新设置为0,所以pollfds可以重用,解决了select中 FD_SET 不可重用的弊端。

epoll 函数源码

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
  # 创建一个epfd,在执行epoll_wait时需要,epfd 就相当于一种空的数据格式
  struct epoll_event events[5];
  int epfd = epoll_create(10);
  ...
  ...
  for (i=0;i<5;i++) 
  {
    static struct epoll_event ev;
    memset(&client, 0, sizeof (client));
    addrlen = sizeof(client);
    ev.data.fd = accept(sockfd,(struct sockaddr*)&client, &addrlen);
    ev.events = EPOLLIN;
    # 对 epfd配置,相当于poll声明的结构体,定义一种数据格式,epoll_ctl相当于构造epfd这种数据格式,大概是一个文件描述符对应的一个events事件 (fd-events)
    epoll_ctl(epfd, EPOLL_CTL_ADD, ev.data.fd, &ev); 
  }
  # 准备好epfd执行下面程序
  while(1){
      puts("round again");
      nfds = epoll_wait(epfd, events, 5, 10000);
    
    for(i=0;i<nfds;i++) {
            memset(buffer,0,MAXBUF);
            read(events[i].data.fd, buffer, MAXBUF);
            puts(buffer);
    }
  }
  • epoll 也是阻塞函数,无数据时阻塞;有数据时,它也是置位,不过它的置位其实是一种变相的位,会把有数据的fd重拍到最前面位置,之前的select和poll都没有返回值,而epoll是有返回值的,它的返回值是有数据的个数。这样的话我们后续的遍历读取处理也简单了,遍历复杂度由 o(n)次简化为 o(1)次了。

select/poll/epoll 源码参考文章

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
详解I/O多路转接模型:select & poll & epoll
多路转接是IO模型的一种,这种IO模型通过select、poll或者epoll进行IO等待,可以同时等待多个文件描述符,当某个文件描述符的事件就绪,便会通知上层处理对应的事件。
二肥是只大懒蓝猫
2023/10/13
7030
详解I/O多路转接模型:select & poll & epoll
I/O 多路复用, select, poll, epoll
I/O 是应用程序必然逃不掉的一个话题。大家在计算机基础学习中,学过计组,操作系统和计网,而想要把 I/O 研究深入肯定要将对这三个计算机基础方面有所深入。
ge3m0r
2024/05/16
1150
Netty如何做到单机百万并发?
今天给大家分享一篇万字长文《微言 Netty:百万并发基石上的 epoll 之剑》。
肉眼品世界
2021/06/08
9590
I/O多路复用select/poll/epoll
早期操作系统通常将进程中可创建的线程数限制在一个较低的阈值,大约几百个。因此, 操作系统会提供一些高效的方法来实现多路IO,例如Unix的select和poll。现代操作系统中,线程数已经得到了极大的提升,如NPTL线程软件包可支持数十万的线程。
WindSun
2019/09/09
1.3K0
I/O多路复用select/poll/epoll
单线程的Redis为什么辣么快?
你之所以问这样的问题。是因为你认为只有多线程分别接收connection才可以更快,就像过去的tomcat那样,同时开多个线程来响应。
ImportSource
2018/08/14
4440
单线程的Redis为什么辣么快?
Linux内核编程--常见IO模型与select/poll/epoll编程
套接字上的数据传输分两步执行:第一步,等待网络中的数据送达,将送达后的数据复制到内核中的缓冲区。第二步,把数据从内核中的缓冲区拷贝到应用进程的缓冲区。整个过程的运行空间是从应用进程空间切换到内核进程空间然后再切换回应用进程空间。
Coder-ZZ
2022/06/23
1.5K0
Linux内核编程--常见IO模型与select/poll/epoll编程
【Linux网络】多路转接:select、poll、epoll
在Linux中,常见的多路转接/复用有 select、poll 和 epoll 。
_小羊_
2025/03/11
2520
【Linux网络】多路转接:select、poll、epoll
select,poll,epoll区别
select的本质是采用32个整数的32位,即32*32= 1024来标识,fd值为1-1024。当fd的值超过1024限制时,就必须修改FD_SETSIZE的大小。这个时候就可以标识32*max值范围的fd。
阳光岛主
2019/02/19
1.4K0
select、poll、epoll
I/O多路复用就是通过一种机制,可以同时监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。
_咯噔_
2022/03/15
1.3K0
select/poll/epoll 对比分析
select函数监控3类文件描述符,调用select函数后会阻塞,直到描述符fd准备就绪(有数据可读、可写、异常)或者超时,函数便返回。 当select函数返回后,可通过遍历描述符集合,找到就绪的描述符。
Yif
2019/12/26
1.2K0
关于I/O模型,和select、poll、epoll的区别
I/O模型主要包括:阻塞IO、非阻塞IO、I/O 多路复用、异步I/O和信号I/O;
呱牛笔记
2023/05/02
4210
关于I/O模型,和select、poll、epoll的区别
【Linux】从零开始使用多路转接IO --- poll
我们对比一下select,select需要传入三个事件集,输入输出性参数,每次都会发生改变!所以才需要每次调用都要进行初始化。而poll使用一个结构体,对于这个文件描述符有两种事件:requested events 与 returned events!输入输出并不互相干扰!那么就解决了select需要不断初始化的问题。
叫我龙翔
2024/11/04
1230
【Linux】从零开始使用多路转接IO --- poll
彻底理解 IO多路复用
https://github.com/caijinlin/learning-pratice/tree/master/linux/io
范蠡
2020/08/18
1.5K0
IO复用 知识点梳理
readfds、writefds、exceptfds这三个数组既是输入参数,也是输出参数。 它们也用于内核空间想用户空间传递就绪的文件描述符。
windealli
2019/11/01
7970
linux 网络编程 I/O复用 select,poll ,epoll
http://blog.csdn.net/zs634134578/article/details/19929449
bear_fish
2018/09/20
2.7K0
select的限制以及poll的使用
1.先说select在多路IO中的限制: 1)linux中每个程序能够打开的最多文件描述符是有限制的。默认是1024. 可以通过ulimit -n进行查看和修改:
xcywt
2022/05/09
1K0
select的限制以及poll的使用
【Linux】I/O多路复用-SELECT/POLL/EPOLL
I/O多路复用 前言 文本相关参考资料及部分内容来源 《Linux高性能服务器编程》 《TCP/IP网络编程》 《Linux/UNIX系统编程手册》 ---- I/O多路复用核心思想为,使用一个线程,来处理多个客户端的请求。 或者说,使用一个特殊的fd,监视多个fd。 使得程序能同时监听多个文件描述符,这对提高程序的性能至关重要。 通常,网络程序在下列情况下需要使用I/O多路复用技术。 客户端程序需要同时处理多个socket。 客户端程序要同时处理用户输入和网络连接。 TCP服务器要同
半生瓜的blog
2023/05/13
1.1K0
【Linux】I/O多路复用-SELECT/POLL/EPOLL
多路IO—POll函数,epoll服务器开发流程
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
莫浅子
2023/11/01
3320
多路IO—POll函数,epoll服务器开发流程
【计算机网络】select/poll
多路转接属于 IO 复用方式的一种。系统提供 select() 函数来实现多路复用输入/输出模型。select 系统调用是用来让我们的程序监视多个文件描述符的状态变化的。程序会停在 select 这里等待,直到被监视的文件描述符有一个或多个发生了状态改变。
YoungMLet
2024/04/09
1460
【计算机网络】select/poll
IO多路复用API总结
I/O 多路复用技术是为了解决进程或线程阻塞到某个 I/O 系统调用而出现的技术,使进程不阻塞于某个特定的 I/O 系统调用。
DeROy
2021/12/20
1.2K0
IO多路复用API总结
相关推荐
详解I/O多路转接模型:select & poll & epoll
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验