IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 系统运维 -> select、poll、epoll 区别在哪儿? -> 正文阅读

[系统运维]select、poll、epoll 区别在哪儿?

之前的《什么是 IO》中提到了 linux 的系统调用: select 、poll、epoll 都能实现 同步 IO 多路复用, 但是它们之间的区别是什么?哪一个方式性能更好? 带着疑问我们来开始今天的话题.

这些都是系统 Linux 操作系统的系统调用.所以学习的最好方式就是手册,首先看 select.

select

允许程序监视多个文件描述符, 直到一个或多个文件描述符变为某种 IO 操作的“ready”状态,比如读、写.

一个文件描述符是 ready 状态意味着可读可写, 可读是指数据准备好了,发起 read() 系统调用不会阻塞, 可写是指向该文件描述符发起 write() 系统调用时不会阻塞.

下面看 select 的传参:

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

nfds: 是指readfds、writefds、exceptfds 三个文件描述符集合的最大值 + 1

readfds: 被监控是否可读的文件描述符集合

writefds: 被监控是否可写的文件描述符集合

exceptfds: 被监控是否会出现异常的文件描述符集合

timeout: 指定了监控文件描述符是否 ready 的时间间隔.

可以看出每做一次 select 操作都要进行文件描述符从用户空间到内核空间的传递!

poll

 int poll(struct pollfd *fds, nfds_t nfds, int timeout);

pollfd 的结构体:

       struct pollfd {
               int   fd;         /* file descriptor,要监控的文件描述符 */
               short events;     /* requested events, 感兴趣的事件 */
               short revents;    /* returned events ,实际发生后由内核返回的事件*/
           };

nfds 指定 fds 数组的大小, timeout 指定 poll 阻塞的最小 ms .

可以看出每做一次 poll 操作都要进行文件描述符从用户空间到内核空间的传递!

epoll

和 select、poll 的本质区别是, epoll 是 IO 事件通知的基础设施. linux 2.6 内核后才支持.

epoll 实际包含三个基本调用:

// 创建 epoll 实例,并返回表示该实例的fd
epfd = epoll_create(int size)
// epoll 实例注册感兴趣的事件,注册成功返回0
int return = epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
// 返回 ready 的 fd 数, 阻塞调用线程
int nfds = epoll_wait(int epfd, struct epoll_event *events,
                      int maxevents, int timeout);

epoll 的两种模式:

1. edge-triggered(ET)

调用 epoll_wait 时, 只有在所监听的 fd 发生变化时,才返回事件,比如数据可读时会发出一个 EPOLLIN 事件, read 操作如果一次没有读完,再调用 epoll_wait 并不会返回事件;同样地, fd 可以写数据时,会翻出一个 EPOLLOUT 事件,但是如果数据一次性没有写完,再发生?epoll_wait调用并不会返回事件. 所以使用 ET 模式时, read 和 write 操作时,必须返回 EAGAIN 时再进行 epoll_wait 操作.

           #define MAX_EVENTS 10
           struct epoll_event ev, events[MAX_EVENTS];
           int listen_sock, conn_sock, nfds, epollfd;

           /* Set up listening socket, 'listen_sock' (socket(),
              bind(), listen()) */
           // 1. 创建 epoll 实例
           epollfd = epoll_create(10);
           if (epollfd == -1) {
               perror("epoll_create");
               exit(EXIT_FAILURE);
           }

           ev.events = EPOLLIN;
           ev.data.fd = listen_sock;
           // 2. 注册 EPOLLIN (accept, read)事件
           if (epoll_ctl(epollfd, EPOLL_CTL_ADD, listen_sock, &ev) == -1) {
               perror("epoll_ctl: listen_sock");
               exit(EXIT_FAILURE);
           }

           // 3. 等待事件
           for (;;) {
               nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1);
               if (nfds == -1) {
                   perror("epoll_pwait");
                   exit(EXIT_FAILURE);
               }

               for (n = 0; n < nfds; ++n) {
                   if (events[n].data.fd == listen_sock) {
                       conn_sock = accept(listen_sock,
                                       (struct sockaddr *) &local, &addrlen);

               for (n = 0; n < nfds; ++n) {
               for (n = 0; n < nfds; ++n) { 
                   // 如果是 listen 的端口对应的fd 的事件(也就是新客户端连接事件)
                   if (events[n].data.fd == listen_sock) {
                       conn_sock = accept(listen_sock,
                                       (struct sockaddr *) &local, &addrlen);
                       if (conn_sock == -1) {
                           perror("accept");
                           exit(EXIT_FAILURE);
                       conn_sock = accept(listen_sock,
                                       (struct sockaddr *) &local, &addrlen);
                       if (conn_sock == -1) {
                           perror("accept");
                           exit(EXIT_FAILURE);
                       }
                       setnonblocking(conn_sock);
                       ev.events = EPOLLIN | EPOLLET;
                       ev.data.fd = conn_sock;
                       if (epoll_ctl(epollfd, EPOLL_CTL_ADD, conn_sock,
                                   &ev) == -1) {
                           perror("epoll_ctl: conn_sock");
                           exit(EXIT_FAILURE);
                       }
                   } else {
                   // 读/写事件, 会从上次的位置继续,直到遇到 EAGAIN, 才进行下一次的事件监听  
                       do_use_fd(events[n].data.fd);
                   }
               }
           }

?Level-triggered(LT)

socket接收缓冲区不为空 ,有数据可读时,读事件一直触发, socket发送缓冲区不满, 可以继续写入数据,写事件一直触发.

LT 模式更符合编程习惯, epoll_wait 返回状态就代表 socket 文件描述符的状态,LT 模式下的 epoll 就是一个快速版本的 poll,使用起来更简单不容易出现bug.

nginx 的高并发就是基于 epoll ET 模式实现的.

总结

1. select、poll、epoll 都可以实现阻塞 IO 多路复用

2.?select、poll 都需要从用户空间到内核空间传递 fd 数组,因此如果fd 较多的话,肯定会影响性能.

3. epoll 是基于事件通知机制实现多路复用,不用反复传递 fd, 性能较好,能支持较大数量的 fd 监控.

  系统运维 最新文章
配置小型公司网络WLAN基本业务(AC通过三层
如何在交付运维过程中建立风险底线意识,提
快速传输大文件,怎么通过网络传大文件给对
从游戏服务端角度分析移动同步(状态同步)
MySQL使用MyCat实现分库分表
如何用DWDM射频光纤技术实现200公里外的站点
国内顺畅下载k8s.gcr.io的镜像
自动化测试appium
ctfshow ssrf
Linux操作系统学习之实用指令(Centos7/8均
上一篇文章      下一篇文章      查看所有文章
加:2022-03-15 23:07:58  更:2022-03-15 23:10:56 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/16 2:37:14-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码