epoll模式简单介绍与使用

前言

epoll作为Linux特有的IO多路复用的模型,是select/poll的改进版,用于监控大量的文件描述符。另一个特点是,epoll全程为event poll,即该接口是基于事件触发的,它无须遍历整个被侦听的描述符集,只要遍历那些被内核IO事件异步唤醒而加入Ready队列的描述符集合就行了。

函数介绍

  1. 创建epoll监控树根节点句柄
/*创建一个epoll的句柄。size为epoll所支持的最大句柄数*/
int epoll_create(int size);
  1. 等待监听
/*描述:等待事件触发
epfd:创建的epoll句柄
events:储存所有的已触发读写事件 
maxevents:最大事件数量
timeout:超时,-1为永久等待*/
int epoll_wait(int epfd, struct epoll_event *events,
                      int maxevents, int timeout);
  1. 添加/修改/删除需要侦听的文件描述符及其事件
/*
epfd:创建的epoll句柄
op: 操作,包络增加,删除和修改
fd:监听的句柄
event:->events 事件触发类型,水平,边缘触发
->data 用户参数
*/
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *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 */
};

任何被侦听的文件符只要被关闭,那么它也会自动从被侦听的文件描述符集合中删除;要尽量少地调用epoll_ctl,防止其引来的开销

epoll event类型有如下三个:
EPOLL_CTL_ADD:添加
EPOLL_CTL_MOD:修改
EPOLL_CTL_DEL:删除

  1. epoll_ctl函数中的参数 struct epoll_event结构体介绍
struct epoll_event {
    uint32_t     events;      /* Epoll events */
    epoll_data_t data;        /* User data variable */
};

该参数表示要监控的fd的事件触发类型参数;参数1可以为如下:

enum EPOLL_EVENTS
  {
    EPOLLIN = 0x001,
#define EPOLLIN EPOLLIN
    EPOLLPRI = 0x002,
#define EPOLLPRI EPOLLPRI
    EPOLLOUT = 0x004,
#define EPOLLOUT EPOLLOUT
    EPOLLRDNORM = 0x040,
#define EPOLLRDNORM EPOLLRDNORM
    EPOLLRDBAND = 0x080,
#define EPOLLRDBAND EPOLLRDBAND
    EPOLLWRNORM = 0x100,
#define EPOLLWRNORM EPOLLWRNORM
    EPOLLWRBAND = 0x200,
#define EPOLLWRBAND EPOLLWRBAND
    EPOLLMSG = 0x400,
#define EPOLLMSG EPOLLMSG
    EPOLLERR = 0x008,
#define EPOLLERR EPOLLERR
    EPOLLHUP = 0x010,
#define EPOLLHUP EPOLLHUP
    EPOLLRDHUP = 0x2000,
#define EPOLLRDHUP EPOLLRDHUP
    EPOLLWAKEUP = 1u << 29,
#define EPOLLWAKEUP EPOLLWAKEUP
    EPOLLONESHOT = 1u << 30,
#define EPOLLONESHOT EPOLLONESHOT
    EPOLLET = 1u << 31
#define EPOLLET EPOLLET
  };

参数2的epoll_data_t data是一个联合体,定义如下

typedef union epoll_data {
  void        *ptr;
  int          fd;
  uint32_t     u32;
  uint64_t     u64;
} epoll_data_t;

一般用void* ptr来自定义参数。该参数在该文件句柄触发是返回给用户。所以可以用此参数来自定义返回值,例如如下定义

typedef int (*on_event)(int fd, void* argv);
struct _userdata{
	int fd; /*文件句柄*/
	on_event onevent; /*毁掉函数*/
	on_event onerr; /*错误返回*/
	void* argv; /*参数*/
	int status; /*当前fd状态*/
}

可以用于定义不同的fd使用不同的处理方法。

简单实例代码

/**
 * @file epoll_srv.c
 * @brief epoll测试
 * @details
 * @mainpage
 * @author Y Locki
 * @email chaodong_y@aliyun.com
 * @version V1.0.0.0
 * @date 2019-10-21日
 * @license Y Locki
 */

#include <stdio.h>
#include<stdlib.h>
#include <unistd.h>
#include<errno.h>

#include <fcntl.h>  
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/epoll.h>

#include "epoll_srv.h"

int setfd_nblk(int fd)
{
    int flag = fcntl(fd, F_GETFL);
    flag |= O_NONBLOCK;
    return fcntl(fd, F_SETFL, flag);
}

int main(int argc, char** argv)
{
    if (argc != 3) {
        printf("usage: epoll_srv ip port\n");
        return -1;
    }
    int ret = 0;
    int epollfd = 0;
    struct epoll_event ev = { 0 }; /**< epoll 节点事件 */
    struct epoll_event readyev[10] = { 0 }; /**< 就绪节点事件 */

    const char* ip = argv[1];
    unsigned short port = atoi(argv[2]);

    int fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);


    struct sockaddr_in addr = { 0 };
    inet_aton(ip, &addr.sin_addr);
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);

    if (0 != (ret = bind(fd, (struct sockaddr*) & addr, sizeof(addr)))) {
        goto SOCK_EXIT;
    }

    if (0 != (ret = listen(fd, MAX_CLIENT))) {
        goto SOCK_EXIT;
    }

    ret = setfd_nblk(fd);
    /*添加epoll事件*/
    epollfd = epoll_create(MAX_CLIENT);

    ev.events = EPOLLIN | EPOLLET; // 边沿触发
    // 添加私有地址
    ev.data.fd = fd;
    if (0 != (ret = epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev))) {
        printf("epoll_ctl failed errno is :%d", errno);
        close(epollfd);
        goto SOCK_EXIT;
    }

    while (1) {
        int readys = epoll_wait(epollfd, readyev, 10, -1);
        for (int i = 0; i < readys; i++) {
            if (readyev[i].data.fd == fd) {
                // 收到新链接
                struct sockaddr_in cli = { 0 };
                socklen_t len = sizeof(cli);
                int clifd = 0;
                if (-1 == (clifd = accept(fd, (struct sockaddr*)&cli, &len))) {
                    printf("accept failed, errno = %d\n", errno);
                    continue;
                }
                printf("new client ip = %s, fd=%d\n", inet_ntoa(cli.sin_addr), clifd);

                ret = setfd_nblk(clifd);
                struct epoll_event evc = { 0 };
                evc.events = EPOLLIN | EPOLLET;
                evc.data.fd = clifd;
                setfd_nblk(clifd);
                if (0 != (ret = epoll_ctl(epollfd, EPOLL_CTL_ADD, clifd, &evc))) {
                    printf("epoll_ctl fd:%d failed errno is :%d", clifd, errno);
                }
            }
            else {
                // 收到消息触发
                char recvbuf[2048] = { 0 };
                int recvlen = 0;
                while (1) {
                    ret = recv(readyev[i].data.fd, recvbuf + recvlen, sizeof(recvbuf) - recvlen, 0);
                    if (ret < 0) {

                        if (errno == EAGAIN) {
                            break;
                        }else {
                            printf("recv failed %d\n", errno);
                            break;
                        }
                    }else if (ret == 0)
                    {
                        printf("peer disconnected\n");
                        if (0 != (ret = epoll_ctl(epollfd, EPOLL_CTL_DEL, readyev[i].data.fd, NULL))) {
                            printf("epoll_ctl fd:%d failed errno is :%d", readyev[i].data.fd, errno);
                        }
                        break;
                    }else {
                        recvlen += ret;
                    }
                }
                printf("fd=%d recv buf:%s len:%d \n", readyev[i].data.fd, recvbuf, recvlen);
            }
        }
    }


SOCK_EXIT:
    close(fd);
    return 0;
}


/**
 * @file epoll_srv.h
 * @brief epoll 测试头文件
 * @details 
 * @mainpage 
 * @author Y Locki
 * @email chaodong_y@aliyun.com
 * @version V1.0.0.0
 * @date 2019-10-21日
 * @license Y Locki
 */

#define MAX_CLIENT 10000

typedef struct _epdata epdata_t;

struct _epdata {
    int fd;
    int flag;
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值