epoll

epoll

epoll - I/O event notification facility

epoll is a variant of poll(2) that can be used either as an edge-triggered or a level-triggered interface and scales well to large numbers of watched file descriptors.

  • epoll_create

#include <sys/epoll.h>

int epoll_create(int size);

size不用,0即可。

int epoll_create1(int flags);

  • epoll_ctl

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

op: EPOLL_CTL_ADD, EPOLL_CTL_DEL, EPOLL_CTL_MOD;

typedef union epoll_data{

void *ptr;

int fd;

uint32_t u32;

uint64_t u64;

}epoll_data_t;

typedef epoll_event {

uint32_t events; /*Epoll events*/

epoll_data_t data;

};

events:EPOLLIN:可读;EPOLLOUT:可写;EPOLLET:边沿触发;EPOLLLT:水平触发(默认)。

The suggested way to use epoll as an edge-triggered (EPOLLET) interface is as follows:

          i   with nonblocking file descriptors; and
 
 
          ii  by waiting for an event only after  read(2) or write(2) return EAGAIN.
 
 
  • epoll_wait

int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);

int epoll_pwait(int epfd, struct epoll_event *events, int maxevents, int timeout, const sigset_t *sigmask);

timeout: -1死等;0,不等;>0,有限等待时间。

成功:事件个数,失败-1。

示例代码:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/epoll.h>

#define MAX_EVENTS 10

int
main(void)
{
    struct epoll_event ev, events[MAX_EVENTS];
    int listen_sock, connect_sock, nfds, epollfd;

    epollfd = epoll_create(MAX_EVENTS);
    if(epollfd == -1){
        perror("epoll_create");
        exit(EXIT_FAILURE);
    }

    ev.events = EPOLLIN;
    ev.data.fd = listen_sock;
    if(epoll_ctl(epollfd, EPOLL_CTL_ADD, listen_sock, &ev) == -1)
    {
        perror("epoll_ctl:listen_sock");
        exit(EXIT_FAILURE);
    }

    while(1)
    {
        nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1);
        if(nfds == -1)
        {
            perror("epoll_wait");
            exit(EXIT_FAILURE);
        }

        for(n = 0; n <nfds; n++)
        {
            if(events[n].data.fd == listen_sock){
                connect_sock = accept(listen_sock, (struct sockaddr *)&local, &addrlen);
                if(connect_sock == -1)
                {
                    perror("accept");
                    exit(EXIT_FAILURE);
                }
                //setnonblocking(connect_sock);
                ev.events = EPOLLIN | EPOLLET;
                ev.data.fd = connect_sock;
                if(epoll_ctl(epollfd, EPOLL_CTL_ADD, connect_sock, \
                    &ev) == -1)
                {
                    perror("epoll_ctl:connect_sock");
                    exit(EXIT_FAILURE);
                }
            } else {
                //do_use_fd(events[n].data.fd);
            }
        }
    }

    return 0;
}

 

参考:

1.处理大并发之二 对epoll的理解,epoll客户端服务端代码

 

EPOLLRDHUP处理 

在对系统问题进行排查时,我发现了一个奇怪的现象:明明是对方断开请求,系统却报告一个查询失败的错误,但从用户角度来看请求的结果正常返回,没有任何问题。

对这个现象深入分析后发现,这是一个基于 epoll 的连接池实现上的问题,或者说是特性 。

首先解释一下导致这个现象的原因。

在使用 epoll 时,对端正常断开连接(调用 close()),在服务器端会触发一个 epoll 事件。在低于 2.6.17 版本的内核中,这个 epoll 事件一般是 EPOLLIN,即 0x1,代表连接可读。

连接池检测到某个连接发生 EPOLLIN 事件且没有错误后,会认为有请求到来,将连接交给上层进行处理。这样一来,上层尝试在对端已经 close() 的连接上读取请求,只能读到 EOF,会认为发生异常,报告一个错误。

因此在使用 2.6.17 之前版本内核的系统中,我们无法依赖封装 epoll 的底层连接库来实现对对端关闭连接事件的检测,只能通过上层读取数据时进行区分处理。

不过,2.6.17 版本内核中增加了 EPOLLRDHUP 事件,代表对端断开连接,关于添加这个事件的理由可以参见 “[RFC] epoll and half closed TCP connections”。

在使用 2.6.17 之后版本内核的服务器系统中,对端连接断开触发的 epoll 事件会包含 EPOLLIN | EPOLLRDHUP,即 0x2001。有了这个事件,对端断开连接的异常就可以在底层进行处理了,不用再移交到上层。

重现这个现象的方法很简单,首先 telnet 到 server,然后什么都不做直接退出,查看在不同系统中触发的事件码。

注意,在使用 2.6.17 之前版本内核的系统中,sys/epoll.h 的 EPOLL_EVENTS 枚举类型中是没有 EPOLLRDHUP 事件的,所以带 EPOLLRDHUP 的程序无法编译通过。

参考:http://www.linuxidc.com/Linux/2016-04/129819.htm

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值