• I/O多路转接-epoll


    By francis_hao    Aug 5,2017

     

    APUE讲多路转接的章节介绍了select、pselect和poll函数。而epoll是linux内核在2.5.44引入的。在glibc 2.3.2添加了支持。

     

    epoll_create – 打开一个epoll文件描述符

    epoll_ctl – 控制epoll文件描述符接口

    epoll_wait – 在epoll文件描述符上等待一个I/O事件

    概述

    #include <sys/epoll.h>
    int epoll_create(int size);
    int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
    int epoll_wait(int epfd, struct epoll_event *events,int maxevents,
                int timeout);

     

    描述

    epoll_create()

    创建一个epoll(7)实例,返回指向这个实例的文件描述符,这个文件描述符会被之后的epoll函数使用,当该文件描述符不再需要的时候,应当使用close(2)关闭。当所有指向同一个epoll实例的文件描述符都被关闭后,内核会销毁该实例,释放分配的资源以复用。

    参数size原本用来告诉内核要添加到epoll实例中文件描述符的数量,内核用这个参数作为一个大概的分配空间大小的指示,以存放描述事件的结构体数据。但是从内核2.6.8之后,这个参数就不再需要了,内核会动态的分配需要的空间大小。但是参数size必须依然是大于0的数,以确保新的epoll函数运行在旧的内核上时能向后兼容。

    函数执行成功返回非负的文件描述符,若有错误则返回-1,而且errno被置为相应的值以指示该错误。

    epoll_ctl()

    这个系统调用对由文件描述符epfd指定的epoll实例执行控制操作,同时需要指定目标文件描述符fd,和对其的操作op。有效的op参数如下

    op

    说明

    EPOLL_CTL_ADD

    注册目标文件描述符fd到epoll实例,并关联事件event

    EPOLL_CTL_MOD

    改变关联到目标文件描述符fd的事件event

    EPOLL_CTL_DEL

    从epoll实例移除目标文件描述符,event被忽略,可以是NULL

    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 */
    };

    成员events可以是以下常量的按位或集:

    events

    说明

    EPOLLIN

    关联的文件可以进行read(2)操作

    EPOLLOUT

    关联的文件可以进行write(2)操作

    EPOLLRDHUP

    (since Linux 2.6.17)

    对方关闭了流套接字连接,or shut down writing half of connection。

    EPOLLPRI

    有紧急数据可以进行read(2)操作

    EPOLLERR

    在关联的文件描述上发生了错误,epoll_wait(2)总会等待这一事件,因此无需对此位置位

    EPOLLHUP

    关联的文件描述符挂起(Hang up)了,poll_wait(2)总会等待这一事件,因此无需对此位置位

    EPOLLET

    为关联的文件描述符设置边沿触发,默认的行为是电平触发(Level Triggered)

    EPOLLONESHOT

    (since Linux 2.6.2)

    为关联的文件描述符设置单次行为,这意味着该文件描述符一旦由epoll_wait(2)返回一次事件,就会被内部失能,再不会有其他事件被报告,用户程序必须调用epoll_ctl()函数,使用EPOLL_CTL_MOD命令重新关联event

    函数执行成功返回0,错误返回-1,而且errno被置为相应的值。

    epoll_wait()

    该系统调用等待由文件描述符epfd指定的epoll实例上的事件,由events指向的内存区域包含了准备好的事件。至多有maxevents个事件由epoll_wait()返回,该值必须大于0。timeout参数指定epoll_wait()将最多阻塞多长时间(ms)。指定-1使epoll_wait()一直等待,直到有准备好的事件,而指定0使epoll_wait()立即返回,即使没有事件准备好。

    函数执行成功返回准备好的文件描述符的数量,如果在timeout时间内仍然没有文件描述符准备好则返回0,错误返回-1,而且errno被置为相应的值。

     

    两点要注意:

    1. epoll_ctl(int epfd, int op, int fd, struct epoll_event *event),fd和event的fd必须同时赋值
    2. close不能使文件描述符从epoll中移除,必须使用epoll_ctl的EPOLL_CTL_DEL(此情况是已经有数据在缓存里,还未调用epoll_wait,此时先close再调用epoll_wait的情况)

     

    实例

    一个简单的用法:

    #include <sys/epoll.h>
    #include <stdio.h>
    #include <string.h>
    int main(void)
    {
        int epoll_fd;
        int fds_num;
        char str[50]={0};
        struct epoll_event ev_in,ev_out;
        fds_num = 0;

        epoll_fd = epoll_create(2); /*this 'size' argument is ignored*/
        if (-1 == epoll_fd){
            perror("epoll_create");
            return -1;
        }
        memset(&ev_in, 0, sizeof (ev_in));
        memset(&ev_out, 0, sizeof (ev_out));
        ev_in.events = EPOLLIN;
        if (-1 == epoll_ctl(epoll_fd, EPOLL_CTL_ADD, 0, &ev_in)){/*Register the target file descriptor 0,that is stdin*/
            perror("epoll_ctl");
            return -1;
        }

    #define TIMEOUT 20
        while(1){
            fds_num = epoll_wait(epoll_fd, &ev_out, 5, TIMEOUT*1000);
            //fds_num = epoll_wait(epoll_fd, &ev_out, 5, -1);
            if (-1 == fds_num){
                perror("epoll_wait");
                return -1;
            }else if (0 == fds_num){
                printf("none data within %u s ",TIMEOUT);
                return 0;
            }
            if ( 0 == ev_out.data.fd){
                if ((ev_out.events & EPOLLIN) != 0){
                    //printf("events is %x ",ev_out.events);
                    if (-1 == read(0, str, 50)){
                        perror("read");
                        return -1;
                    }
                    printf("%s",str);
                    memset(str, 0 ,sizeof (str));
                }
            }else{
                printf("%d ",ev_out.data.fd);
            }
        }
        return 0;
    }

    执行结果:

    程序源码在github上,可以直接clone编译测试: epoll

     

     


    本文由
    刘英皓 创作,采用 知识共享 署名-非商业性使用-相同方式共享 3.0 中国大陆 许可协议进行许可。欢迎转载,请注明出处:
    转载自:http://www.cnblogs.com/yinghao1991/p/7291912.html

     

     

    参考

    【1】man 7 epoll

    【2】man epoll_create

    【3】man epoll_ctl

    【4】man epoll_wait

     

  • 相关阅读:
    C#中发送邮件,包含Html代码 CDO.Message
    CodeSmith生成SQL Server视图的实体类脚本/对应的生成模板
    分享到微信朋友圈
    获取验证码效果和后台代码(js+html+cs)
    弹出遮罩层
    WebAPI上传文件
    zoj1665 dij变形
    hdu1535 SPFA
    hdu1217 floyd
    poj1703 并查集
  • 原文地址:https://www.cnblogs.com/yinghao-liu/p/7291912.html
Copyright © 2020-2023  润新知