epoll 是 linux 特有的 I/O 复用函数。它是把用户关心的文件描述符事件放在内核的一个事件列表中,故而,无须像select和poll一样每次调用都重复传入文件描述符或事件集。但是, epoll 需要一个额外的文件描述符,来唯一标识内核中的这个事件表。这个文件描述符由 epoll_create 函数来创建:

       #include <sys/epoll.h>

       int epoll_create(int size);

  size 参数现在是被忽略的,但是,为了兼容性,需要传入一个大于0的数。

epoll_ctl 函数来操作epoll的内核事件表:

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

  epfd是epoll_create返回的文件描述符,op指定操作类型,有如下三种:

/* Valid opcodes ( "op" parameter ) to issue to epoll_ctl().  */
#define EPOLL_CTL_ADD 1 /* Add a file descriptor to the interface. */
#define EPOLL_CTL_DEL 2 /* Remove a file descriptor from the interface. */
#define EPOLL_CTL_MOD 3 /* Change file descriptor epoll_event structure. */

  event 参数指定事件,它是 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 是成员描述符事件类型。事件类型也定义在 sys/epoll.h 文件中

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
EPOLLEXCLUSIVE = 1u << 28,
#define EPOLLEXCLUSIVE EPOLLEXCLUSIVE
EPOLLWAKEUP = 1u << 29,
#define EPOLLWAKEUP EPOLLWAKEUP
EPOLLONESHOT = 1u << 30,
#define EPOLLONESHOT EPOLLONESHOT
EPOLLET = 1u << 31
#define EPOLLET EPOLLET
};

  data 是 epoll_data_t 联合体类型。可以用fd 表示文件描述符,或者用ptr指针指向更多的用户数据。

epoll 系列系统调用的主要接口是epoll_wait函数,它在一段超时时间内等待一组文件描述符上的事件,定义:

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

  成功时,返回就绪文件描述符的个数,失败返回-1,并设置errno

其中,timeout指定超时时间,单位毫秒。-1表示永远等待直到有文件描述符就绪。

maxevents 指定最多监听多少个事件,它必须大于0

epoll_wait 函数如果检测到时间,就将事件从内核事件表中复制到第二个参数events指向的数组中。这个数组只输出epoll_wait检测到的就绪事件。

epoll 对文件描述符的操作有两种模式:LT(level trigger 电平触发)和 ET(edge trigger 边沿触发)。LT是默认的工作模式。在这种模式下,文件描述符会一直被检测到直到应用程序处理它。ET模式下,文件描述符就绪,被通知给应用程序,之后,就不再通知该同一事件了。ET模式降低了同一个epoll时间被重复触发的次数,因此效率较高。

EPOLLONESHOT事件

对于注册了EPOLLONESHOT事件的文件描述符,操作系统最多触发其上注册的一个可读、可写、异常事件,且只触发一次,除非我们使用 epoll_ctl 函数重置该文件描述符上注册的 EPOLLONESHOT 事件。这样就不会用多并发的问题。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/epoll.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <fcntl.h>
#include <signal.h> #include <map>
#include <string> using namespace std; #define CLIENTSIZE 5000
#define BUFSIZE 4000 int createSocket()
{
struct sockaddr_in servaddr;
int listenfd = -1; if (-1 == (listenfd = socket(PF_INET, SOCK_STREAM, 0)))
{
fprintf(stderr, "socket: %d, %s\n", errno, strerror(errno));
exit(1);
} int reuseaddr = 1;
if (-1 == setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof(reuseaddr)))
{
fprintf(stderr, "setsockopt: %d, %s\n", errno, strerror(errno));
exit(1);
} memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = PF_INET;
servaddr.sin_port = htons(8008);
inet_pton(PF_INET, "0.0.0.0", &servaddr.sin_addr); if (-1 == bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)))
{
fprintf(stderr, "bind: %d, %s\n", errno, strerror(errno));
exit(1);
} if (-1 == listen(listenfd, 5))
{
fprintf(stderr, "listen: %d, %s\n", errno, strerror(errno));
exit(1);
} return listenfd;
} int setnoblock(int fd)
{
int oldopt = fcntl(fd, F_GETFL);
int newopt = oldopt | O_NONBLOCK;
fcntl(fd, F_SETFL, newopt);
return oldopt;
} void ErrExit(const char* reason)
{
fprintf(stderr, "%s: %d, %s\n", reason, errno, strerror(errno));
exit(1);
} void addsig(int sig, void (*handler)(int))
{
int olderrno = errno;
struct sigaction ac; memset(&ac, 0, sizeof(ac));
ac.sa_handler = handler;
ac.sa_flags |= SA_RESTART;
sigfillset(&ac.sa_mask);
if (-1 == sigaction(sig, &ac, NULL))
{
ErrExit("sigaction");
}
errno = olderrno;
} void addfd(int epfd, int fd)
{
struct epoll_event ev; ev.events = EPOLLIN | EPOLLET | EPOLLERR;
ev.data.fd = fd;
if (-1 == epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev))
{
ErrExit("epoll_ctl");
}
setnoblock(fd);
} void delfd(int epfd, int fd)
{
struct epoll_event ev; ev.data.fd = fd;
if (-1 == epoll_ctl(epfd, EPOLL_CTL_DEL, fd, &ev))
{
ErrExit("epoll_ctl");
}
} int main(int argc, char const *argv[])
{
int listenfd = createSocket();
int epfd = -1;
map<int, string> mapdata; if (-1 == (epfd = epoll_create(CLIENTSIZE)))
{
ErrExit("epoll_create");
} struct epoll_event evs[CLIENTSIZE];
addfd(epfd, listenfd); while (1)
{
int connnum = epoll_wait(epfd, evs, CLIENTSIZE, -1);
for (int i = 0; i < connnum; ++i)
{
if (evs[i].events & EPOLLERR)
{
printf("%d exit\n", evs[i].data.fd);
delfd(epfd, evs[i].data.fd);
close(evs[i].data.fd);
mapdata.erase(evs[i].data.fd);
}
else if (evs[i].data.fd == listenfd && (evs[i].events & EPOLLIN))
{
struct sockaddr_in client;
socklen_t len = sizeof(client);
int cfd = accept(listenfd, (struct sockaddr*)&client, &len);
if (cfd == -1)
{
ErrExit("accept");
}
printf("get connection: %d\n", cfd);
addfd(epfd, cfd);
}
else if (evs[i].events & EPOLLIN)
{
char buf[BUFSIZE] = {0};
int len = recv(evs[i].data.fd, buf, BUFSIZE-1, 0);
if (len > 0)
{
mapdata[evs[i].data.fd] = buf;
evs[i].events &= (~EPOLLIN);
evs[i].events |= EPOLLOUT;
if (-1 == epoll_ctl(epfd, EPOLL_CTL_MOD, evs[i].data.fd, &evs[i]))
{
ErrExit("epoll_ctl");
}
}
else if (len == 0)
{
printf("%d exit\n", evs[i].data.fd);
delfd(epfd, evs[i].data.fd);
close(evs[i].data.fd);
mapdata.erase(evs[i].data.fd);
}
else
{
ErrExit("recv");
}
}
else if (evs[i].events & EPOLLOUT)
{
if (send(evs[i].data.fd, mapdata[evs[i].data.fd].c_str(), mapdata[evs[i].data.fd].size(), 0) < 0)
{
if (errno == 104)
{
continue;
}
ErrExit("send");
}
evs[i].events &= (~EPOLLOUT);
evs[i].events |= EPOLLIN;
if (-1 == epoll_ctl(epfd, EPOLL_CTL_MOD, evs[i].data.fd, &evs[i]))
{
ErrExit("epoll_ctl");
}
}
}
} close(listenfd);
close(epfd); return 0;
}

  

linux epoll用法的更多相关文章

  1. 网络通信 --> epoll用法

    epoll用法 在linux的网络编程中,很长的时间都在使用select来做事件触发.在linux新的内核中,有了一种替换它的机制,就是epoll. epoll函数 1. 创建epoll的句柄 siz ...

  2. Windows完成端口与Linux epoll技术简介

    收藏自:http://www.cnblogs.com/cr0-3/archive/2011/09/09/2172280.html WINDOWS完成端口编程1.基本概念2.WINDOWS完成端口的特点 ...

  3. Java网络编程和NIO详解6:Linux epoll实现原理详解

    Java网络编程和NIO详解6:Linux epoll实现原理详解 本系列文章首发于我的个人博客:https://h2pl.github.io/ 欢迎阅览我的CSDN专栏:Java网络编程和NIO h ...

  4. select,poll,epoll用法

    http://blog.csdn.net/sunboy_2050/article/details/6126712 select用法 #include <sys/time.h>       ...

  5. Windows完成端口与Linux epoll技术简介(能看懂)

    WINDOWS完成端口编程1.基本概念2.WINDOWS完成端口的特点3.完成端口(Completion Ports )相关数据结构和创建4.完成端口线程的工作原理5.Windows完成端口的实例代码 ...

  6. Server Develop (六) Linux epoll总结

    Linux  epoll epoll是Kernel 2.6后新加入的事件机制,在高并发条件下,远优于select.epoll最大的好处在于它不会随着监听fd数目的增长而降低效率.因为在内核中的sele ...

  7. linux curl用法详解

    linux ‍‍curl用法详解 ‍‍curl的应用方式,一是可以直接通过命令行工具,另一种是利用libcurl库做上层的开发.本篇主要总结一下命令行工具的http相关的应用, 尤其是http下载方面 ...

  8. Linux Epoll介绍和程序实例

    Linux Epoll介绍和程序实例 1. Epoll是何方神圣? Epoll但是当前在Linux下开发大规模并发网络程序的热门人选,Epoll 在Linux2.6内核中正式引入,和select类似, ...

  9. Linux epoll总结

    Linux epoll总结 Linux  epoll epoll是Kernel 2.6后新加入的事件机制,在高并发条件下,远优于select.epoll最大的好处在于它不会随着监听fd数目的增长而降低 ...

随机推荐

  1. mapreduce总结

    一.mapreduce简介 MapReduce是一种分布式计算模型,是hadoop的核心组件之一,是Google提出的,主要用于搜索领域,解决海量数据的计算问题. MR有两个阶段组成:Map和Redu ...

  2. Spring课程 Spring入门篇 4-2 Spring bean装配(下)之Autowired注解说明1

    课程链接: 1 解析 2 代码演练 1 解析 1.1 @Required注解 该注解适用于bean属性的set方法 1.2 @Autowired 作用: 是为了把依赖的对象,自动的注入到bean里 使 ...

  3. (11)JavaScript之[DOM HTML][DOM CSS]

    DOM HTML //改变HTML输出流 document.write(Date()); //改变HTML的内容 document.getElementById('box').innerHTML = ...

  4. Java Knowledge series 3

    JVM & Bytecode Abstract & Object Object in Java (1) 所有东西都是对象object.可将对象想象成一种新型变量:它保存着数据,但可要求 ...

  5. Android Studio Git 分支实践

    新公司有些项目是用的 Git,以前公司都是 svn,为了练手 Git,我个人 APP 用到了,但是仅简单的 git pull/push 的使用,并未用到 Git 精髓,只有当项目中用到,才会紧迫去全面 ...

  6. Gameplay Classes

    每个虚幻游戏类都是一个.h和一个.cpp组成. 类在虚幻中有便准的命名模式. 前缀: A继承于可量产的游戏性类.他们都是Actor,可以直接在游戏中生成. U继承于所有游戏性对象.不能在游戏中直接生成 ...

  7. linux lnmp搭建

    1.安装nginx: yum install gcc -y yum install -y pcre pcre-devel yum install -y zlib zlib-devel yum inst ...

  8. MSD_radix_sort

    一.这次是在上一次尝试基础上进行的,预期是达到上次性能的9倍. MSD的基本手法就是不断切片.每一步都是把整体数据切割成256片,如上图所示,实际情况切片未必均匀,有的slice内可能一个元素也没有. ...

  9. mysql5.6之前需要账号的安全加固

    mysql5.6之前需要账号的安全加固 从5.7开始就不需要了. delete from mysql.user where user!='root' or host='localhost'; flus ...

  10. 2018.9.6 Java常考知识点总结

    一 Java中的值传递和引用传递(非常重要) 首先要明确的是:"对象传递(数组.类.接口)是引用传递,原始类型数据(整型.浮点型.字符型.布尔型)传递是值传递." 那么什么是值传递 ...