网络_高级IO_epoll

本文深入探讨了epoll的工作原理及优势,详细介绍了epoll的系统调用,并提供了水平触发模式下的epoll服务器编程实例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

为处理大批量句柄而做了改进的poll

epoll解决了poll,select实现不了的问题

 

epoll相关的系统调用

1.创建一个epoll的句柄

size可以被忽略,用完之后必须调用close()关闭

 

2.epoll的事件注册函数

不同于select()是在监听事件时告诉内核要监听什么类型的事件,而是先注册要监听的事件类型

epfd:epoll_create的返回值(epoll的句柄)

op:表示动作,用3个宏来表示

fd:需要监听的文件描述符

event:告诉内核需要监听什么事

op的取值:EPOLL_CTL_ADD--注册新的fd到epfd中

                  EPOLL_CTL_MOD--修改已经注册的fd的监听事件

                  EPOLL_CTL_DEL--从epfd中删除一个fd

event可以是以下几个宏的集合:

     EPOLLIN:表示对应的文件描述符可以读 (包括对端SOCKET正常关闭);

     EPOLLOUT : 表⽰对应的⽂件描述符可以写; 

     EPOLLPRI : 表⽰对应的⽂件描述符有紧急的数据可读 (这⾥应该表⽰有带外数据到来); 

     EPOLLERR : 表⽰对应的⽂件描述符发⽣错误; 

     EPOLLHUP : 表⽰对应的⽂件描述符被挂断; 

     EPOLLET : 将EPOLL设为边缘触发(Edge Triggered)模式, 这是相对于⽔平触发(Level Triggered) 来说的.                                  EPOLLONESHOT:只监听⼀次事件, 当监听完这次事件之后, 如果还需要继续监听这个socket的话, 需要再次把

                            这个socket加入到EPOLL队列里

3.收集在epoll监控的事件中已经发送的事件   

     参数events是分配好的epoll_event结构体数组. 

     epoll将会把发⽣的事件赋值到events数组中 (events不可以是空指针,内核只负责把数据复制到这 个events数组中,不会          去去帮助我们在⽤户态中分配内存).

     maxevents告知内核这个events有多⼤,这个maxevents的值不能⼤于创建epoll_create()时的size. 

     参数timeout是超时时间 (毫秒,0会⽴即返回,-1是永久阻塞). 

     如果函数调⽤成功,返回对应I/O上已准备好的⽂件描述符数目,如返回0表⽰已超时, 返回⼩于0表示函数失败.

 

epoll的使用过程

1.调用epoll_create创建一个epoll句柄

2.调用epoll_ctl,将要监控的文件描述符进行注册

3.调用epoll_wait,等待文件描述符就绪

 

epoll的优点

1.文件描述符无上限

2.基于事件的就绪通知方式

3.维护就绪队列

4.内存映射机制

 

直到Linux2.6才出现了由内核直接支持的实现方法,那就是epoll,它几乎具备了之前所说的一切优点,被公认为Linux2.6下性能最好的多路I/O就绪通知方法。

epoll可以同时支持水平触发和边缘触发(Edge Triggered,只告诉进程哪些文件描述符刚刚变为就绪状态,它只说一遍,如果我们没有采取行动,那么它将不会再次告知,这种方式称为边缘触发),理论上边缘触发的性能要更高一些,但是代码实现相当复杂。

epoll同样只告知那些就绪的文件描述符,而且当我们调用epoll_wait()获得就绪文件描述符时,返回的不是实际的描述符,而是一个代表就绪描述符数量的值,你只需要去epoll指定的一个数组中依次取得相应数量的文件描述符即可,这里也使用了内存映射(mmap)技术,这样便彻底省掉了这些文件描述符在系统调用时复制的开销。

另一个本质的改进在于epoll采用基于事件的就绪通知方式。在select/poll中,进程只有在调用一定的方法后,内核才对所有监视的文件描述符进行扫描,而epoll事先通过epoll_ctl()来注册一个文件描述符,一旦基于某个文件描述符就绪时,内核会采用类似callback的回调机制,迅速激活这个文件描述符,当进程调用epoll_wait()时便得到通知。

 

epoll的两种工作方式(水平触发LT和边缘触发ET)

默认模式就是LT模式

当epoll检测到socket上事件就绪的时候,可以不立即处理或者只处理一部分,直到缓冲区上的所有数据被处理完,epoll_wait才不会立即返回。支持阻塞读写和非阻塞读写。

边缘触发ET模式

在第一步将socket添加到epoll描述符的时候使用EPOLLET标志,epoll进入ET工作模式。

1)当epoll检测到socket上事件就绪时,必须立刻处理

2)在ET模式下,文件描述符上的事件就绪后,只有一次处理机会

3)ET的性能比LT高

4)支持非阻塞的读写

 

select和poll都在LT模式下,epoll可以在LT模式下,也可在ET模式下

 

epoll的使用场景

在一定的场景下使用,如果在不适宜的场景下使用效果可能适得其反

对于多连接,且多连接只有一部分连接比较活跃时,比较适合用epoll

 

编程实例:epoll服务器

水平触发--LT模式

  1 #include <stdio.h>
  2 #include <stdlib.h>
  3 #include <unistd.h>
  4 #include <sys/socket.h>
  5 #include <sys/epoll.h>
  6 #include <netinet/in.h>
  7 #include <arpa/inet.h>
  8 
  9 int main(int argc,char * argv[])
 10 {
 11     if(argc!=3)
 12     {
 13         printf("Usage ./sever [ip] [port]\n");
 14         return 1;
 15     }
 16 
 17     struct sockaddr_in addr;
 18     addr.sin_family=AF_INET;
 19     addr.sin_addr.s_addr=inet_addr(argv[1]);
 20     addr.sin_port=htons(atoi(argv[2]));
 21 
 22     int sock=socket(AF_INET,SOCK_STREAM,0);
 23     
 24     printf("24 socket%d\n",sock);
 25     if(sock<0)
 26     {
 27         perror("socket");
 28         return 1;
 29     }
 30 
 31     int ret=bind(sock,(struct sockaddr*)&addr,sizeof(addr));
 32     if(ret<0){
 33         perror("bind");
 34         return 1;
 35     }
 36 
 37     ret=listen(sock,5);
 38     if(ret<0){
 39         perror("listen");
 40         return 1;
 41     }
 42     //创建epoll句柄
 43     int epoll_fd=epoll_create(10);
 44     if(epoll_fd<0)
 45     {
 46         perror("epoll_create");
 47         return 1;
 48     }
 49 
 50     struct epoll_event event;
 51     event.events=EPOLLIN;
 52     event.data.fd=sock;
 53 
 54     ret=epoll_ctl(epoll_fd,EPOLL_CTL_ADD,sock,&event);
 55     if(ret<0)
 56     {
 57         perror("epoll_ctl");
 58         return 1;
 59     }
 60 
 61     printf("59 socket%d\n",sock);
 62     while(1)
 63     {
 64         struct epoll_event events[10];
 65         //参数-1表示一直阻塞,直到状态改变
 66         ret=epoll_wait(epoll_fd,events,sizeof(events)/sizeof(events[0]),-1);
 67     printf("65 socket%d\n",sock);
 68         if(ret<0){
 69             perror("epoll_wait");
 70             continue;
 71         }
 72         if(ret==0){
 73             printf("time out!\n");
 74             continue;
 75         }
 76         int i=0;
 77         for(;i<ret;i++)
 78         {
 79             //当前的文件描述符不可读
 80             if(!(events[i].events&EPOLLIN))
 81             {
 82                 continue;
 83             }
 84             //socket就绪
 85             if(events[i].data.fd==sock)
 86             {
 87                 printf("enter socket ready\n");
 88                 struct sockaddr_in client;
 89                 socklen_t len=sizeof(client);
 90                 int connect_fd=accept(sock,(struct sockaddr*)&client,&len);
 91                 if(connect_fd<0)
 92                 {
 93                     perror("accept");
 94                     return;
 95                 }
 96                 printf("client %s:%d\n",inet_ntoa(client.sin_addr),ntohs(client.sin_port));
 97                 struct epoll_event env;
 98                 env.events=EPOLLIN;
 99                 env.data.fd=connect_fd;
100                 int res=epoll_ctl(epoll_fd,EPOLL_CTL_ADD,connect_fd,&env);
101                 if(res<0)
102                 {
103                     perror("epoll_ctl");
104                     return 1;
105                 }
106     printf("104 socket%d\n",sock);
107             }
108             else//处理connent
109             {
110                 printf("enter read ready!\n");
111                 char buf[1024]={0};
112                 printf("socket:%d\n",sock);
113                 ssize_t read_size=read(events[i].data.fd,buf,sizeof(buf)-1);
114                 if(read_size<0){
115                     perror("read");
116                     return 1;
117                 }
118                 if(read_size==0)
119                 {
120                     close(events[i].data.fd);
121                     epoll_ctl(epoll_fd,EPOLL_CTL_DEL,sock,NULL);
122                     printf("read done!\n");
123                     continue;
124                 }
125                 printf("client say:%s\n",buf);
126                 write(events[i].data.fd,buf,strlen(buf));
127             }
128         }
129     }
130     return 0;
131 }                       

 

电动汽车数据集:2025年3K+记录 真实电动汽车数据:特斯拉、宝马、日产车型,含2025年电池规格和销售数据 关于数据集 电动汽车数据集 这个合成数据集包含许多品牌和年份的电动汽车和插电式车型的记录,捕捉技术规格、性能、定价、制造来源、销售和安全相关属性。每一行代表由vehicle_ID标识的唯一车辆列表。 关键特性 覆盖范围:全球制造商和车型组合,包括纯电动汽车和插电式混合动力汽车。 范围:电池化学成分、容量、续航里程、充电标准和速度、价格、产地、自主水平、排放、安全等级、销售和保修。 时间跨度:模型跨度多年(包括传统和即将推出的)。 数据质量说明: 某些行可能缺少某些字段(空白)。 几个分类字段包含不同的、特定于供应商的值(例如,Charging_Type、Battery_Type)。 各列中的单位混合在一起;注意kWh、km、hr、USD、g/km和额定值。 列 列类型描述示例 Vehicle_ID整数每个车辆记录的唯一标识符。1 制造商分类汽车品牌或OEM。特斯拉 型号类别特定型号名称/变体。型号Y 与记录关联的年份整数模型。2024 电池_类型分类使用的电池化学/技术。磷酸铁锂 Battery_Capacity_kWh浮充电池标称容量,单位为千瓦时。75.0 Range_km整数表示充满电后的行驶里程(公里)。505 充电类型主要充电接口或功能。CCS、NACS、CHAdeMO、DCFC、V2G、V2H、V2L Charge_Time_hr浮动充电的大致时间(小时),上下文因充电方法而异。7.5 价格_USD浮动参考车辆价格(美元).85000.00 颜色类别主要外观颜色或饰面。午夜黑 制造国_制造类别车辆制造/组装的国家。美国 Autonomous_Level浮点自动化能力级别(例如0-5),可能包括子级别的小
内容概要:本文详细介绍了IEEE论文《Predefined-Time Sensorless Admittance Tracking Control for Teleoperation Systems With Error Constraint and Personalized Compliant Performance》的复现与分析。论文提出了一种预定义时间的无传感器导纳跟踪控制方案,适用于存在模型不确定性的遥操作系统。该方案通过具有可调刚度参数的导纳结构和预定义时间观测器(PTO),结合非奇异预定义时间终端滑模流形和预定义时间性能函数,实现了快速准确的导纳轨迹跟踪,并确保误差约束。文中详细展示了系统参数定义、EMG信号处理、预定义时间观测器、预定义时间控制器、可调刚度导纳模型及主仿真系统的代码实现。此外,还增加了动态刚度调节器、改进的广义动量观测器和安全约束模块,以增强系统的鲁棒性和安全性。 适合人群:具备一定自动化控制理论基础和编程能力的研究人员、工程师,尤其是从事机器人遥操作、人机交互等领域工作的专业人士。 使用场景及目标:①理解预定义时间控制理论及其在遥操作系统中的应用;②掌握无传感器力观测技术,减少系统复杂度;③学习如何利用肌电信号实现个性化顺应性能调整;④探索如何在保证误差约束的前提下提高系统的响应速度和精度。 阅读建议:本文内容涉及较多的数学推导和技术细节,建议读者先熟悉基本的控制理论和Python编程,重点理解各个模块的功能和相互关系。同时,可以通过运行提供的代码示例,加深对理论概念的理解,并根据自身需求调整参数进行实验验证。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值