【网络编程】I/O复用

文章目录

  • 一、select系统调用
    • 1.1、文件描述符就绪条件(socket可读条件)
    • 1.2、处理带外数据
  • 二、poll系统调用
  • 三、epoll系列系统调用
    • 3.1、内核事件表
    • 3.2、epoll_wait函数
    • 3.3、LT和ET模式
    • 3.4、EPOLLONESHOT事件
  • 四、select、poll、epoll三组I/O复用函数比较


一、select系统调用

  select系统调用的用途是:在一段指定时间以内,监听用户感兴趣的文件描述符上的可读、可写和异常事件。

#include <sys/select.h>
int select(int nfds,fd_set* readfds, fd_set* writefds, fd_set* exceptfds, struct timeval* timeout);

  nfds设置为被监听文件描述符的总数,即所有文件描述符中最大值加一。
  readfds,writefds,exceptfds分别表示可读,可写,异常等事件的文件描述符集合,其类型fd_set仅包含一个整数数组,每个元素的每一位标记一个文件描述符,select中包含了以下宏来对fd_set进行操作。

#include<sys/select.h>
FD_ZERO(fd_set* fdset);                //清除fdset的所有位
FD_SET(int fd, fd_set* fdset);         //设置fdset的位fd
FD_CLR(int fd, fd_set* fdset);         //清除fdset的位fd
int FD_ISSET(int fd, fd_set* fdset);   //测试fdset的位fd是否被设置

  timeout用来设置select函数的超时时间。其中用到了timeval结构体

struct timeval{long tv_sec;   //秒数long tv_usec;  //微秒数
}

  select成功时返回就绪文件的文件描述符,如果超时返回0,失败时返回-1并设置error。如果在select等待期间,程序收到信号,select立即返回-1,并设置errno为EINTR。

1.1、文件描述符就绪条件(socket可读条件)

《Linux高性能服务器编程》P148
注:网络编程中select能处理的异常情况只有一种:socket上接收到带外数据。

1.2、处理带外数据

  下面的代码描述了select是如何处理socket处于两种不同的就绪状态,即接收到普通数据处于可读状态和接收到带外数据处于异常状态。

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <stdlib.h>int main(int argc, char* argv[]){if(argc <= 2){printf("usage:%s ip_address port_number/n", basename(argv[0]));return 1;}const char* ip = argv[1];int port = atoi(argv[2]);int ret = 0;struct sockaddr_in address;        //新建一个ipv4的socket结构体bzero(&address, sizeof(address));address.sin_family = AF_INET;      //设置ipv4地址族inet_pton(AF_INET, ip, &address.sin_addr); //将传进来的点分十进制转化为网络字节序表示的ip地址address.sin_port = htons(port);  //将传进来的主机字节序转化为网络字节序int listenfd = socket(PF_INET, SOCK_STREAM, 0);   //创建一个使用ipv4协议的TCP连接socketassert(listenfd >= 0);  //其值为假,则终止运行并弹出错误信息ret = bind(listenfd, (struct sockaddr*)&address, sizeof(address));//将address所指向的地址分配给文件描述符listenfdassert(ret != -1);ret = listen(listenfd, 5);//开始监听socket,监听队列长度为5assert(ret != -1);struct sockaddr_in client_address;//创建一个客户端的socket连接用来存储客户端的地址信息socklen_t client_addrlength = sizeof(client_address);int connfd = accept(listenfd, (struct sockaddr*)&client_address, &client_addrlength);//接受listenfd和client_address的socket连接,并将相关信息保存下来//accept是从监听队列中取出连接,并且将连接的客户地址存在client_address。if(connfd < 0){printf("errno is: %d\n", errno);close(listenfd);}char buf[1024];fd_set read_fds;fd_set exception_fds;FD_ZERO(&read_fds);FD_ZERO(&exception_fds);while(1){memset(buf, '\0', sizeof(buf));FD_SET(connfd, &read_fds);FD_SET(connfd, &exception_fds);ret = select(connfd + 1, &read_fds, NULL, &exception_fds, NULL);if(ret < 0){printf("selection failure\n");break;}if(FD_ISSET(connfd, &read_fds)){  //read_fds中是否设置了connfd,是的话说明监听到了connfd上有可读事件ret = recv(connfd, buf, sizeof(buf) - 1, 0);if(ret <= 0) break;printf("get %d bytes of normal data: %s\n", ret, buf);}else if(FD_ISSET(connfd, &exception_fds)){//exception_fds中是否设置了connfd,是的话说明监听到了connfd上有异常事件ret = recv(connfd, buf, sizeof(buf) - 1, MSG_OOB);if(ret <= 0) break;printf("get %d bytes of oob data: %s\n", ret, buf);}close(connfd);close(listenfd);return 0;}
}

二、poll系统调用

  poll系统调用是在指定时间内轮询一定数量的文件描述符,以测试其中是否有就绪者。

#include <poll.h>
int poll(struct pollfd* fds, nfds_t nfds, int timeout);

  fds是一个pollfd类型的数组。它的定义如下:

struct pollfd{int fd;         //文件描述符short events;   //注册的事件short revents;  //实际发生的事件,由内核填充
}

  nfds参数指定了被监听事件集合fds的大小。
  timeout参数指定了poll的超时值。当timeout为-1时,poll调用永远堵塞,直到某个事件发生,当timeout为0时,timeout立即返回。

三、epoll系列系统调用

3.1、内核事件表

  epolllinux特有的I/O复用函数,它与select,poll的差异在于:

  • epoll使用一组函数来完成任务而不是单个函数。
  • epoll把用户关心的文件描述符上的事件放在内核的一个事件表中,无须像selectpoll一样每次调用都要传入文件描述符或者事件集。所以epoll需要一个文件描述符来唯一表示该内核事件表。

  下面的函数用来创建内核事件表的文件描述符。

#include <sys/epoll.h>
int epoll_create(int size);  //创建内核事件表的文件描述符
//size指定了内核事件表有多大

  下面的函数用来操作epoll的内核事件表。

#include <sys/epoll.h>
int epoll_ctl(int epfd, int op, int fd, struct epoll_event* event);
//成功返回0,失败返回-1并设置errno

  epfd指的是某个内核事件表的文件描述符
  fd指的是要操作的文件描述符
  op指定操作类型,有以下三种:EPOLL_CTL_ADD,往事件表中注册fd上的事件。EPOLL_CTL_MOD,修改fd上的注册事件。EPOLL_CTL_DEL,删除fd上的注册事件。
  event指定事件,其定义为:

struct epoll_event{__uint32_t events;  //epoll事件epoll_data_t data;  //用户数据
}
typedef union epoll_data{void* ptr;int fd;uint32_t u32;uint64_t u63;
}epoll_data_t;

3.2、epoll_wait函数

  这是epoll系统调用的主要接口,作用是在一段时间内等待一组文件描述符上的事件。

#include <sys/epoll.h>
int epoll_wait(int epfd, struct epoll_event* events, int maxevents, int timeout);

  timeout的含义与polltimeout含义相同。
  maxevents指定最多监听多少个事件,必须大于0 。
  epoll_wait如果检测到事件,就将所有的就绪事件从内核事件表中取出并复制到第二个参数events指向的数组中。它不像selectpoll的数组参数既用于传入用户注册的事件,又用于输出内核检测到的事件,这就极大的提高了应用程序索引就绪文件描述符的效率。

3.3、LT和ET模式

  epoll对文件描述符的操作有两种模式:LT(Level Trigger,电平触发)和ET(Edge Trigger,边沿触发)两种模式。LT模式是默认的工作模式,相当于一个效率较高的pollET模式是epoll的高效工作模式。
  对于采用LT模式的文件描述符,当epoll_wait检测到其上有事件发生并将此事件通知应用程序后,应用程序可以不立即处理该事件。这样,当应用程序下一次调用epoll_wait时,epoll_wait还会再次向应用程序通告此事件,直到该事件被处理。
  对于采用ET模式的文件描述符,当epoll_wait检测到其上有事件发生并将此事件通知应用程序后, 应用程序必须立即处理该事件,因为后续的epoll_wait调用将不再向应用程序通知这一事件。
  由此可见,ET模式相较于LT模式,减少了同一个epoll事件被重复触发的次数,因此效率要比LT模式高。

  • 注:每个使用ET模式的文件描述符都应该是非阻塞的。如果文件描述符时阻塞的,那么读或写操作将会因为没有后续的事件而一直处于阻塞状态。

3.4、EPOLLONESHOT事件

  EPOLLONESHOT事件可以实现一个socket连接在任一时刻都只被一个线程处理。对于注册了EPOLLONESHOT事件的文件描述符,操作系统最多触发其上注册的一个可读、可写或者异常事件,且只触发一次,除非使用epoll_ctl函数重置该文件描述符上注册的EPOLLONESHOT事件。
  所以,一旦注册了EPOLLONESHOT事件的socket被某个线程处理完毕,该线程就应该立即重置这个socket上的EPOLLONESHOT事件,以确保这个socket下次可读时,其EPOLLIN事件能被触发,从而让其他工作线程有机会继续处理这个socket。

四、select、poll、epoll三组I/O复用函数比较

系统调用selectpollepoll
事件集合用户通过三个参数分别传入感兴趣的可读、可写以及异常等事件,内核通过对这些参数的在线修改来反馈其中的就绪事件。这使得用户每次调用select都要重复这三个参数统一处理所有事件类型,因此只需一个事件集参数,用户通过pollfd,events传入感兴趣的事件,内核通过修改pollfd,revents反馈其中的就绪事件内核通过一个事件表直接管理用户感兴趣的所有事件。因此每次调用epoll,wait时,无须反复传入用户感兴趣的事件,epoll_wait系统调用的参数events仅用来反馈就绪的事件
应用程序索引就绪事件的事件复杂度O(n)O(n)O(1)
最大支持文件描述符数量一般有最大值限制6553565535
工作模式LTLT支持ET高效率模式
内核实现和工作效率采用轮询的方式来检测就绪事件,算法的时间复杂度为O(n)采用轮询的方式来检测就绪事件,算法的时间复杂度为O(n)采用回调的方式来检测就绪事件,算法的时间复杂度为O(1)

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

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

相关文章

路由的配置

1、在router中设置路由导航跳转函数,在index.js文件中写这句话&#xff1a; 1.1 只要发生跳转, 就会调用这个函数&#xff1a; 1.2 导航的声明函数 2、访问系统访问控制系统如何形成 3、来一个导航守卫的案例&#xff1a;看看导航守卫的案例&#xff0c;写一个Main.Vue 和login…

超简单的fastapi链接websocket用例

main.py from typing import Listfrom fastapi import FastAPI, WebSocket, WebSocketDisconnectapp FastAPI()class ConnectionManager:def __init__(self):# 存放激活的ws连接对象self.active_connections: List[WebSocket] []async def connect(self, ws: WebSocket):# 等…

sky-notes-02

11、HttpClient HttpClient作用&#xff1a; 发送HTTP请求接收响应数据 HttpClient的maven坐标&#xff1a; <dependency><groupId>org.apache.httpcomponents</groupId><artifactId>httpclient</artifactId><version>4.5.13</vers…

springmvc登录拦截器

Ioc和Aop是spring的两大重要思想&#xff0c;前者指的是控制反转&#xff08;Invers of control&#xff09;后者指的是面向切面编程&#xff08;Aspect oriented programing&#xff09;。aop的一大作用就是能将很多重复的功能点抽取出来&#xff0c;而用注解或者配置的方式统…

docker启动容器报错

报错信息 [rootDream soft]# docker run -it -d -p 8080:8080 tomcat eec9fab6b9ca06d2bbf1467aef05d8020ee60448978e10ac20c38888934f0a0b docker: Error response from daemon: driver failed programming external connectivity on endpoint hungry_euclid (163242f0079e72…

关于c++中虚函数和虚函数表的创建时机问题

以这段代码为例。 #include <iostream>using namespace std;class Parent { public:Parent(){}virtual void func1() {};virtual void func2() {}; };class Child :public Parent { public:Child():n(0),Parent(){cout << "Child()" << endl;}vir…

如何建立ftp server?快解析内网穿透实现外网直接访问

serveru是一款由Rob Beckers开发的获奖的ftp服务器软件&#xff0c;全称为&#xff1a;serv-u ftp server&#xff0c;它功能强大又易于使用。ftp服务器用户通过它用ftp协议能在internet上共享文件。serv-u不仅100%遵从通用ftp标准&#xff0c;也包括众多的独特功能可为每个用户…

Android getevent用法详解

TP驱动调试分享——基于Qualcomm SDM710平台Android9.0&#xff0c;TP 采用I2C方式和CPU进行通信_高通tp驱动_永恒小青青的博客-CSDN博客 手机触摸屏扫描信号实测波形_触摸屏报点率_AirCity123的博客-CSDN博客 如何查看TP报点率&#xff1f;触摸TP查看详细信息 adb shell ge…

音视频——视频流H264编码格式

1 H264介绍 我们了解了什么是宏快&#xff0c;宏快作为压缩视频的最小的一部分&#xff0c;需要被组织&#xff0c;然后在网络之间做相互传输。 H264更深层次 —》宏块 太浅了 ​ 如果单纯的用宏快来发送数据是杂乱无章的&#xff0c;就好像在没有集装箱 出现之前&#xff0c;…

【Rust教程 | 基础系列 | Rust初相识】Rust简介与环境配置

教程目录 前言一&#xff0c;Rust简介1&#xff0c;Rust的历史2&#xff0c;Rust的特性3&#xff0c;为什么选择Rust 二&#xff0c; Rust环境配置1&#xff0c;windows11安装2&#xff0c;Linux安装 三&#xff0c;安装IDE 前言 Rust是一种系统编程语言&#xff0c;专注于速度…

U盘安装CentOS7.9出错:进入 dracut问题和解决方法

U盘安装CentOS7.9出错&#xff1a;进入 dracut问题和解决方法 原因&#xff1a;U盘名称未识别&#xff0c; 解决&#xff1a;进入启动界面&#xff0c;按e进入编辑界面 修改&#xff1a; vmlinuz initrdinitrd.img inst.stage2hd:LABELCentOS\x207\x20x86_64.check quiet 为 …

eslint-webpack-plugin

说明&#xff1a;现在eslint已经弃用了eslint-loader,如果要安装来使用的话&#xff0c;会报错&#xff0c;烦死人 大概的报错信息如下&#xff1a; ERROR in ./src/index.js Module build failed (from ./node_modules/eslint-loader/dist/cjs.js): TypeError: Cannot read …

驱动开发 day4 (led灯组分块驱动)

//编译驱动(注意Makefile的编译到移植到开发板的内核) make archarm //清除编译生成文件 make clean //安装驱动 insmod mycdev.ko //卸载驱动 rmmod mycdev //编译fun.c 函数(用到交叉工具编译) arm-linux-gnueabihf-gcc fun.c head.h #ifndef __HEAD_H__ #define __HEAD_H__…

Linux 之 systemctl

systemctl 可以控制软件&#xff08;一般指服务&#xff09;的启动、关闭、开机自启动 能被systemctl 管理的软件&#xff0c;一般也称 服务 系统内置服务均可被 systemctl 控制第三方软件&#xff0c;如果 自动注册了 可被systemctl 控制第三方软件&#xff0c;如果没有自动…

【业务功能篇60】Springboot + Spring Security 权限管理 【终篇】

4.4.7 权限校验扩展 4.4.7.1 PreAuthorize注解中的其他方法 hasAuthority&#xff1a;检查调用者是否具有指定的权限&#xff1b; RequestMapping("/hello")PreAuthorize("hasAuthority(system:user:list)")public String hello(){return "hello Sp…

【AutoGluon_03】保存模型并调用模型

在训练好autogluon模型之后&#xff0c;可以将模型进行保存。之后当有新的数据需要使用autogluon进行预测的时候&#xff0c;就可以直接加载原来训练好的模型进行训练。 import pandas as pd from sklearn.model_selection import train_test_split from autogluon.tabular im…

SpringSecurity的实现

SpringSecurity的实现 1.依赖 security起步依赖 redis起步依赖 fastjson jjwt生成token mybatis-plus起步依赖 mysql连接 web起步 test起步 <!-- security启动器 --><dependency><groupId>org.springframework.boot</groupId><arti…

【Unity细节】关于NotImplementedException: The method or operation is not implemented

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! 本文由 秩沅 原创 收录于专栏&#xff1a;unity细节和bug ⭐关于NotImplementedException: The method or operation is not implemented.⭐…

ReID网络:MGN网络(1) - 概述

Start MGN 1. 序言 现代基于感知的信息中&#xff0c;视觉信息占了80~85%。基于视觉信息的处理和分析被应用到诸如安防、电力、汽车等领域。 以安防市场为例&#xff0c;早在2017年&#xff0c;行业咨询公司IHS Market&#xff0c;我国在公共和私人领域安装有摄像头约1.76亿…

QGraphicsView实现简易地图1『加载离线瓦片地图』

最简单粗暴的加载方式&#xff0c;将每一层级的所有瓦片地图全部加载 注&#xff1a;该方式仅能够在瓦片地图层级较低时使用&#xff0c;否则卡顿&#xff01;&#xff01;&#xff01; 瓦片地图数据来源&#xff1a;水经注-高德地图-卫星地图 瓦片地图瓦片大小&#xff1a;25…