Linux中epoll简单使用

简介

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  failed\n");
            break;
        }
        printf("fd is %d write data %ld\n",fd,write_data);
        sleep(2);  //睡眠两秒
    }

}

int main(int argc,char* argv[]){
    fd = eventfd(0,0);
    if(fd == -1){
        printf("eventfd create failed\n");
        return 0;
    }

    printf("fd %d\n",fd);

    int handle = epoll_create1(0);
    if(handle == -1){
        printf("epoll create failed\n");
        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 failed\n");
        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 %ld\n",value);
            }
        }else{
            printf("fd %d\n",event.data.fd);  //返回的描述符,和我在注册事件时传入的描述符不一样了,为负的值
             perror("epoll_wait");//输出:epoll_wait: Bad file descriptor
             break;
        }
    }
 

    close(fd);
    close(handle);
    return 0;
}

 

 

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值