博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
linux epoll用法
阅读量:4639 次
发布时间:2019-06-09

本文共 6296 字,大约阅读时间需要 20 分钟。

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

#include 
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 
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;#define CLIENTSIZE 5000#define BUFSIZE 4000int 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
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;}

  

转载于:https://www.cnblogs.com/zuofaqi/p/9638098.html

你可能感兴趣的文章
提高网站用户体验的4个方面
查看>>
内联函数和宏
查看>>
SpringMVC存取Session的两种方法
查看>>
Solution 27:跳台阶问题
查看>>
Docker-----常用命令
查看>>
PHP延迟静态绑定 static关键字
查看>>
tp5.0实战开发大型cms(2)模板分离
查看>>
cookie和session
查看>>
【Nginx+Tomcat】高性能负载均衡的Tomcat集群
查看>>
【翻译】22款HTML & CSS3 UI工具包免费下载
查看>>
gradle打包分编译环境
查看>>
[洛谷P3292][SCOI2016]幸运数字
查看>>
淘宝网质量分析
查看>>
B - Catch That Cow
查看>>
CSS Sprite、CSS雪碧图应用实例
查看>>
并发总结(博客转载)
查看>>
win 使用 tensorboard
查看>>
C#string类型总结
查看>>
shell下office、html、pdf文档互转方法
查看>>
浏览器事件window.onload、o…
查看>>