概述
简介
epoll是Linux内核为处理大批量句柄而作了改进的poll,是Linux下多路复用IO接口select/poll的增强版本,它能显著减少程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率。
两种工作模式
epoll默认的工作模式是level triggered,简称LT即水平触发模式。是一种缺省的工作方式,支持block和no-block socket。
在这种工作模式中,内核会通知你一个文件描述符是否就绪了,如果就绪了,你可以对其进行IO操作,但是如果你不做任何操作的话,内核会继续通知你。
epoll另一种工模式是edge triggered,简称ET即边缘触发模式。是一种高速的工作方式,支持no-block socket.
在这种工作模式中,当描述符从未就绪状态变为已就绪状态时,内核会通知你。
但是需要注意的是如果描述符一直没有进行IO操作(使其在次变为未就绪状态),那么内核就不会在发送更多通知了。
创建
通过epoll_create1来进行创建,函数定义如下:
#include <sys/epoll.h>
int epoll_create(int size);
int epoll_create1(int flags);
这两个函数都可以创建epoll,第一个函数参数指的是要监听的数目一共有多少个,这个函数在linux2.6.8之后,被忽略了,建议用epoll_create1(0)这种方法,而且epoll_create1(EPOLLCLOEXEC)创建的epoll可以在执行后关闭。
成功时,返回创建好的epoll句柄,失败时返回-1,错误信息可以通过errno获得。
记得用close()关闭创建出来的epoll句柄,,否则可能导致系统fd被耗尽。
事件注册
使用函数epoll_ctl,函数定义如下:
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
参数epfd:epoll_create()函数返回的epoll句柄
参数op:操作选项
op可选值有以下3个:
EPOLL_CTL_ADD:注册新的fd到epfd中
EPOLL_CTL_MOD:修改已经注册的fd的监听事件
EPOLL_CTL_DEL:从epfd中删除一个fd
参数fd:要进行操作的目标文件描述符
参数event:struct epoll_event结构指针,将fd和要进行的操作关联起来
返回值:0成功,-1失败,错误信息可以通过errno获得。
再来看一下epoll_event结构体的定义:
typedef union epoll_data {
void *ptr;
int fd;
uint32_t u32;
uint64_t u64;
} epoll_data_t;
struct epoll_event {
uint32_t events; /* Epoll events */
epoll_data_t data; /* User data variable */
};
evets可选值有以下几个:
EPOLLIN :表示对应的文件描述符可以读
EPOLLOUT:表示对应的文件描述符可以写
EPOLLPRI:表示对应的文件描述符有紧急的数据可读
EPOLLERR:表示对应的文件描述符发生错误
EPOLLHUP:表示对应的文件描述符被挂断
EPOLLET: 将EPOLL设为边缘触发模式,这是相对于水平触发来说的
EPOLLONESHOT:只监听一次事件,当监听事件完成后,就不在监听
data是用户数据变量,可以传一些数据,在事件监听的时候拿到。
事件监听
使用函数epoll_wait,函数定义如下:
int epoll_wait(int epfd, struct epoll_event *events,int maxevents, int timeout);
参数epfd:epoll_create()函数返回的epoll句柄
参数events:struct epoll_event结构指针,事件被触发后返回的事件集合
参数 maxevents:最大监听的事件数目
参数 timeout: 等待时的超时时间,以毫秒为单位。
返回值:成功时,返回需要处理的事件数目。调用失败时,返回0,表示等待超时,返回-1,出现错误
一个例子
这个代码的功能在子线程中每隔两秒写入数据,主线程中使用epoll_wait监听写入事件,事件触发后读取数据输出。
但是我运行一直有问题,报错“Bad file descriptor”,不知道什么原因,请大神给看一下。
#include<stdio.h>
#include<pthread.h>
#include<sys/epoll.h>
#include<sys/eventfd.h>
#include <unistd.h>
#include<pthread.h>
//文件描述符
int fd = -1;
//线程执行函数
void* callback(void* data){
uint64_t write_data = 30;
int ret = -1;
while(1){
ret = eventfd_write(fd,write_data);
if(ret == -1){
printf("evenfd write failedn");
break;
}
printf("fd is %d write data %ldn",fd,write_data);
sleep(2); //睡眠两秒
}
}
int main(int argc,char* argv[]){
fd = eventfd(0,0);
if(fd == -1){
printf("eventfd create failedn");
return 0;
}
printf("fd %dn",fd);
int handle = epoll_create1(0);
if(handle == -1){
printf("epoll create failedn");
return 0;
}
struct epoll_event ev;
struct epoll_event event;
memset(&ev,0,sizeof(struct epoll_event));
ev.data.fd = fd;
ev.events = EPOLLIN|EPOLLET;
int ret = epoll_ctl(handle,EPOLL_CTL_ADD,fd,&ev);
if(ret == -1){
printf("epoll ctl failedn");
return 0;
}
//开启线程
pid_t pid;
pthread_create(&pid, NULL,callback,NULL);
uint64_t value;
while(1){
ret = epoll_wait(handle,&event,1,-1);
if(ret > 0){
ret = eventfd_read(fd,&value);
if(ret == 0){
printf("value is %ldn",value);
}
}else{
printf("fd %dn",event.data.fd); //返回的描述符,和我在注册事件时传入的描述符不一样了,为负的值
perror("epoll_wait");//输出:epoll_wait: Bad file descriptor
break;
}
}
close(fd);
close(handle);
return 0;
}
最后
以上就是斯文红酒为你收集整理的Linux中epoll简单使用简介两种工作模式创建事件注册事件监听一个例子的全部内容,希望文章能够帮你解决Linux中epoll简单使用简介两种工作模式创建事件注册事件监听一个例子所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复