I/O复用函数的使用——epoll
1.epoll的接口介绍
epoll 是 Linux 特有的 I/O 复用函数。它在实现和使用上与 select、poll 有很大差异。首先,epoll 使用一组函数来完成任务,而不是单个函数。其次,epoll 把用户关心的文件描述符上的事件放在内核里的一个事件表中,从而无需像 select 和 poll 那样每次调用都要重复传入文件描述符或事件集。但 epoll 需要使用一个额外的文件描述符,来唯一标识内核中的这个事件表。epoll 相关的函数如下:
◼ epoll_create()用于创建内核事件表
◼ epoll_ctl()用于操作内核事件表
◼ epoll_wait()用于在一段超时时间内等待一组文件描述符上的事件其各自的原型如下:
#include <sys/epoll.h>int epoll_create( int size);
/*
epoll_create()成功返回内核事件表的文件描述符,失败返回-1
size 参数现在并不起作用,只是给内核一个提示,告诉它事件表需要多大。
*/int epoll_ctl( int epfd, int op, int fd, struct epoll_event *event);
/*
epoll_ctl()成功返回 0,失败返回-1
epfd 参数指定要操作的内核事件表的文件描述符
fd 参数指定要操作的文件描述符
op 参数指定操作类型:
EPOLL_CTL_ADD 往内核事件表中注册 fd 上的事件
EPOLL_CTL_MOD 修改 fd 上的注册事件
EPOLL_CTL_DEL 删除 fd 上的注册事件
event 参数指定事件,它是 epoll_event 结构指针类型,
epoll_event 的定义如下:
struct epoll_event
{_uint32_t events; // epoll 事件epoll_data_t data; // 用户数据
};
其中,events 成员描述事件类型,epoll 支持的事件类型与 poll 基本相同,表示epoll 事件的宏是在 poll 对应的宏前加上‘E’,比如 epoll 的数据可读事件是EPOLLIN。但是 epoll 有两个额外的事件类型--EPOLLET 和 EPOLLONESHOT。
data 成员用于存储用户数据,是一个联合体,其定义如下:
typedef union epoll_data
{void *ptr;int fd;uint32_t u32;uint64_t u64;
}epoll_data_t;
其中 fd 成员使用的最多,它指定事件所从属的目标文件描述符。
*/int epoll_wait( int epfd, struct epoll_event *events, int maxevents, int timeout);
/*
epoll_wait()成功返回就绪的文件描述符的个数,失败返回-1,超时返回 0
epfd 参数指定要操作的内核事件表的文件描述符
events 参数是一个用户数组,这个数组仅仅在 epoll_wait 返回时保存内核检测到的所有就绪事件,而不像 select 和 poll 的数组参数那样既用于传入用户注册的事件,又用于输出内核检测到的就绪事件。这就极大地提高了应用程序索引就绪文件描述符的效率。
maxevents 参数指定用户数组的大小,即指定最多监听多少个事件,它必须大于0
timeout 参数指定超时时间,单位为毫秒,如果 timeout 为 0,则 epoll_wait 会立即返回,如果 timeout 为-1,则 epoll_wait 会一直阻塞,直到有事件就绪。
*/
2.epoll支持的事件类型
3.epoll 的示例代码
服务器代码:
#define _GUN_SOURCE#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<assert.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<sys/epoll.h>#define EPOLLSIZE 10
#define MAX_FD 128
#define DATALEN 128//初始化套接字
int InitSocket()
{int sockfd = socket(AF_INET,SOCK_STREAM,0);if(sockfd == -1){return -1;}struct sockaddr_in saddr;saddr.sin_family = AF_INET;saddr.sin_port = htons(6000);saddr.sin_addr.s_addr = inet_addr("127.0.0.1");int res = bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));if(res == -1){return -1;}res = listen(sockfd,5);if(res == -1){return -1;}return sockfd;
}//处理客户端连接
void GetClientLink(int sockfd,int epfd)
{struct sockaddr_in caddr;int len = sizeof(caddr);int c = accept(sockfd,(struct sockaddr*)&caddr,&len);if(c < 0){printf("Client Link error\n");return;}else{struct epoll_event ev;ev.events = EPOLLIN | EPOLLRDHUP;ev.data.fd = c;if(epoll_ctl(epfd,EPOLL_CTL_ADD,c,&ev) == -1)//将携带客户端信息的结构体ev添加到{printf("epoll_ctl add error\n");}else{printf("Client %d Link Sucess\n",c);}}}//关闭客户端
void CloseClient(int epfd,int clifd)
{struct epoll_event ev;if(epoll_ctl(epfd,EPOLL_CTL_DEL,clifd,NULL) == -1)//从内核事件表中移除clifd信息和事件{printf("epoll_ctl del error\n");}close(clifd);//服务器关闭客户端连接,注意必须要从内核事件表中注销客户端事件后才能关闭//否则的话,关闭客户端连接后,描述符就没用了,内核事件表查不到,就没办法注销事件printf("Client %d over\n",clifd);
}//处理客户端可读信息
void DealClientData(int epfd,int clifd)
{char buff[DATALEN] = {0};int n = recv(clifd,buff,DATALEN-1,0);if(n <= 0){CloseClient(epfd,clifd);return;}else{printf("%d:%s\n",clifd,buff);send(clifd,"Ok",2,0);}}void DealReadyEvent(struct epoll_event* events,int n,int sockfd,int epfd)
{for(int i = 0; i < n; i++)//events携带就绪事件信息,总共n个{if(events[i].data.fd == sockfd)//说明有客户端连接{GetClientLink(sockfd,epfd);}else if(events[i].events & EPOLLIN)//说明客户端有事件,而且是可读事件{DealClientData(epfd,events[i].data.fd);}else if(events[i].events & EPOLLOUT)//说明客户端断开连接{CloseClient(epfd,events[i].data.fd);}else{printf("DelReady error\n");}}
}int main()
{int sockfd = InitSocket();assert(sockfd != -1);//创建epoll实例,epfd为句柄,就像打开一个文件后返回文件描述符一样int epfd = epoll_create(EPOLLSIZE);assert(epfd != -1);//从内核事件表删除注册事件都需要借用struct epoll_event结构体类型来当作描述符和事件的载体struct epoll_event ev;ev.data.fd = sockfd;ev.events = EPOLLIN;if(epoll_ctl(epfd,EPOLL_CTL_ADD,sockfd,&ev) == -1)//向内核事件表中注册sockfd的事件{printf("epoll_ctl add error\n");exit(0);}while(1){//就像需要向内核事件表注册事件一样,这里需要struct epoll_event结构体类型获取保存就绪事件信息struct epoll_event events[MAX_FD];int n = epoll_wait(epfd,events,MAX_FD,5000);//从内核时间表中获取就绪事件,返回值为就绪事件个数if(n < 0)//n < 0 epoll_wait失败{printf("epoll_wait error\n");continue;}else if(n == 0)//超过等待时间{printf("time out\n");continue;}else//有就绪事件{DealReadyEvent(events,n,sockfd,epfd);}}exit(0);
}
客户端代码在讲解I/O复用函数的使用——poll博客中有,这里就不进行粘贴了。
下面是运行结果:
I/O复用函数的使用——epoll相关推荐
- Linux内核剖析-----IO复用函数epoll内核源码剖析
本文参考董浩博客 http://donghao.org/uii/ epoll内核实现 (1)内核为epoll做准备工作 这个模块在内核初始化时(操作系统启动)注册了一个新的文件系统,叫" ...
- scala tail recursive优化,复用函数栈
在scala中如果一个函数在最后一步调用自己(必须完全调用自己,不能加其他额外运算子),那么在scala中会复用函数栈,这样递归调用就转化成了线性的调用,效率大大的提高.If a function c ...
- Linux网络编程——I/O复用函数之epoll
https://blog.csdn.net/lianghe_work/article/details/46544567 一.epoll概述 epoll 是在 2.6 内核中提出的,是之前的 selec ...
- poll函数_I/O复用 - 三组I/O复用函数的比较
在之前的文章中 I/O复用 - epoll 和 I/O复用 - select&poll 中我们讨论了三组I/O复用的系统调用,这3组系统调用都能同时监听多个文件描述符.它们将等待由timeou ...
- I/O复用函数的比较
select poll 和 epoll 三组I/O复用系统调用都能够同时监听多个文件描述符.它们将等到由timeout参数指定的超时时间,直到一个或者多个文件描述符上有时间发生时返回,返回值就是就绪的 ...
- I/O复用函数的使用——select
I/O 复用使得程序能同时监听多个文件描述符,这对于提高程序的性能至关重要.通常,网络程序在下列情况下需要使用 I/O 复用技术: ◼ TCP 服务器同时要处理监听套接字和连接套接字. ◼ 服务器要同 ...
- Linux使用I/O复用函数的超时机制的定时器
I/O复用超时机制 利用socket的timeout的参数,进行超时设定,这期间也可以处理其他事情.在主循环中,一定要每次都更新超时参数!!! 这是个代码实例: #include <stdio. ...
- epoll用到的epoll_create,epoll_ctl, epoll_wait三个函数,以及epoll的工作模式
首先附上B站关于epoll原理讲解视频连接:https://www.bilibili.com/video/BV1qJ411w7du?from=search&seid=1476914145148 ...
- bigdecimal判断等于0_vue2.0源码用到的工具函数,12个简易的复用函数,看看有多简单...
戎马:https://segmentfault.com/a/1190000019679638 1. 创建一个被冻结的空对象 export const emptyObject = Object.free ...
最新文章
- adc0808温度换算公式_adc0808模数转换电路图及程序
- 【C 语言】文件操作 ( 按照单个字符的方式读写文件 | fgetc 函数 | fputc 函数 )
- 消息机制学习笔记(四)—— 内核回调机制
- The import android cannot be resolved”错误解决方法
- C的安装编译Error
- ubuntu安装百度网盘
- html5怎么设置drop,HTML5 拖放(Drag 和 Drop)
- mysql connector c编程_MySQL数据库之MySQL Connector 编程
- 数据库MySQL基础---约束、表关系、聚合函数、连接查询、分组查询和子查询
- 修改docx表格_实例29_在Word表格中将上下行相同内容的单元格自动合并
- python创建docx文件
- Redis客户端Redisson+SpringBoot实现的分布式锁案例
- onload事件_图像onLoad事件+ Internet ExplorerJavaScript问题
- 论文阅读_ICD编码_BERT
- git提交代码步骤和idea中不同颜色代表意义
- 江苏小高考计算机难吗,江苏小高考成绩
- Users用户类默认值及各属性可能的值
- 使用融资的心得和教训
- 关于java爬虫手机壁纸图片网站
- char int word long的大小
热门文章
- SwiftUI之深入解析高级动画的路径Paths
- 198. House Robber
- python3利用smtplib通过qq邮箱发送邮件
- 数据库开发——MySQL——约束条件与表关系
- 排序算法 —— 插入排序
- 【Linux】一步一步学Linux——head命令(41)
- fpga在线升级 linux_仅5000行Verilog代码、可在FPGA上跑轻量级Linux系统的RISC-V内核
- keepalived(9)——sorry_server
- 深度学习中常用的激活函数详解及对比分析(sigmoid)
- java做日历怎么对齐日期_如何使用Java日历从日期中减去X天?