Epoll 机制

描述

epoll 是poll系统调用的升级版。可以用做单边沿(level-triggered)和双边沿(edge-triggered)的两种工作模式,同样也可以用于检测多个文件描述符。

API

  • epoll_create(int size)

用于创建一个epoll的实例对象。参数size代表可以一次性检测的文件对象的个数。返回值是epoll 实例对象的文件描述符,次描述符用于后续的epoll_ctl和epoll_wait函数中,当没有对象检测的时候,需要使用close系统调用关系该文件描述符,因为epoll实际上也会占用用一个fd的。

  • epoll_ctl(int epfd, int op, int fd, struct epoll_event* event)

用于注册要检测的对象以及检测的事件。 epfd代表的是epoll的实例对象,也就是epoll_create的返回值。op代表的几种操作,如下:

EPOLL_CTL_ADD:     注册一个new的fd对象到epoll实例中。

EPOLL_CTL_MOD:    修改已经注册的fd的事件。

EPOLL_CTL_DEL:      从epoll检测的列表中remove掉fd。

event就是代表具体要检测的事件类型,详细信息如下:

struct 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成员,代表具体检测的事件类型:

EPOLLIN :表示对应的文件描述符可以读(包括对端SOCKET正常关闭);
EPOLLOUT:表示对应的文件描述符可以写;
EPOLLPRI:表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来);
EPOLLERR:表示对应的文件描述符发生错误;
EPOLLHUP:表示对应的文件描述符被挂断;
EPOLLET: 将EPOLL设为边缘触发(Edge Triggered)模式,这是相对于水平触发(Level Triggered)来说的。
EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列里
  • epoll_wait(int epfd,  struct epoll_event* events,  int maxevents,  int timeout)

该系统调用用于等待检测对象的事件发生。 events具体返回具体的发生的事件内容,maxevents用于告知允许最大的事件数量,timout当然也就是超时时间,-1代表一直等待不超时。返回值代表发生事件需要处理的数据,返回值为0代表timeout。

实例分析

#include <sys/epoll.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>


#define EPOLL_INSTANCE_SIZE (8)
#define EPOLL_EVENTS_SIZE   (8)


int main(int argc, char** argv)
{

    int epfd;
    int i = 0;
    int events;
    struct epoll_event eventItem[EPOLL_EVENTS_SIZE];
    char buf[512];

    /*usage*/
    if(argc < 2 )
    {
        printf("Usage: %s <file1> <file2> ...\n", argv[0]);
        return -1;
    }

    /*创建epoll实例对象*/
    epfd = epoll_create(EPOLL_INSTANCE_SIZE);
    if(epfd == -1)
    {
        printf("epoll_create error!\n");
        return -1;
    }
    
    /*添加检测实例对象*/
    for(i = 1; i < argc; i++)
    {
        int fd;
        struct epoll_event event;

        fd = open(argv[i], O_RDWR);                
        event.events = EPOLLIN;
        event.data.fd = fd;
        epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &event);
    }
 
    while(1)
    { 
        /*等待事件发生*/
        events = epoll_wait(epfd, eventItem, EPOLL_EVENTS_SIZE, -1);
        for(i = 0; i < events; i++)
        {
            int len = read(eventItem[i].data.fd, buf, 512);
            buf[len] = '\0';
            printf("read buf is %s\n",buf);
        }
    }
    
    return 0;
}


测试结果:

1.   编译代码

gcc epoll.c -o epoll

2.   在tmp创建下创建3个fifo文件

mkfifo tmp/1 tmp/2 tmp3

3.  使用epoll在后台检测

./epoll tmp/1 tmp/2 tmp/3 &
可以从/proc/epoll进程的pid/fd中看到详细信息

4.  echo 数据到 tmp/1中查看结果

test$ echo 111 > tmp/1
read buf is 111


发布了162 篇原创文章 · 获赞 93 · 访问量 35万+

猜你喜欢

转载自blog.csdn.net/longwang155069/article/details/54019845