一、epoll_create

#include <sys/epoll.h>

int epoll_create(int size);
int epoll_create1(int flags);
返回:成功非负文件描述符,-1出错
size:内核监听数目一共多大

创建一个epoll接口,size参数和select不同,不是fd+1?

需要注意的是:当创建好epoll后,它就会占用一个fd值,在linux /proc/id/fd/能看到这个fd的,所以使用完epoll后,必须close()关闭,否则可能导致耗尽fd。

二、epoll_ctl

#include <sys/epoll.h>

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
返回:0成功,-1失败
epfd:由epoll_create生成的epoll专用的文件描述符
op:要进行的操作,可能取EPOLL_CTL_ADD注册、EPOLL_CTL_MOD修改、EPOLL_CTL_DEL删除
fd:关联的文件描述符
event:指向epoll_event指针

epoll的相关的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

#include <sys/epoll.h>

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);
返回:发生事件数
epfd:由epoll_create生成的epoll专用的文件描述符
epoll_event:用于回传代处理事件的数组
maxevents:每次能处理的事件数
timeout:等待I/O事件发生的超时值

在linux中的man手册中有epoll模型:

            #define MAX_EVENTS 10
struct epoll_event ev, events[MAX_EVENTS];
int listen_sock, conn_sock, nfds, epollfd; /* Set up listening socket, 'listen_sock' (socket(),
bind(), listen()) */ epollfd = epoll_create();
if (epollfd == -) {
perror("epoll_create");
exit(EXIT_FAILURE);
} ev.events = EPOLLIN;
ev.data.fd = listen_sock;
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, listen_sock, &ev) == -) {
perror("epoll_ctl: listen_sock");
exit(EXIT_FAILURE);
} for (;;) {
nfds = epoll_wait(epollfd, events, MAX_EVENTS, -); //等待IO事件
if (nfds == -) {
perror("epoll_pwait");
exit(EXIT_FAILURE);
} for (n = ; n < nfds; ++n) {
//如果是主socket事件,则表示有新连接进入,需要进行新连接的处理
if (events[n].data.fd == listen_sock) {
conn_sock = accept(listen_sock,
(struct sockaddr *) &local, &addrlen);
if (conn_sock == -) {
perror("accept");
exit(EXIT_FAILURE);
}
//将新连接置于非阻塞模式
setnonblocking(conn_sock);
ev.events = EPOLLIN | EPOLLET;
//注意这里的参数EPOLLIN|EPOLLET并没有设置对写socket的监听
//如果有写操作的话,这个时候epoll是不会返回事件的
//如果要对写操作也监听的话,应该是EPOLLIN|EPOLLOUT|EPOLLET
//并且将新连接也加入EPOLL的监听队列
ev.data.fd = conn_sock;
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, conn_sock,
&ev) == -) {
//加入到epoll的监听队列里,这里用EPOLL_CTL_ADD
//来加一个新的epoll事件,可以通过EPOLL_CTL_DEL减少
//一个epoll事件,通过EPOLL_CTL_MOD来改变一个i额事件的监听方式
perror("epoll_ctl: conn_sock");
exit(EXIT_FAILURE);
}
} else {
//如果不是主socket的事件的话,则代表这是一个用户的socket事件
//则用来处理这个用户的socket的事情是,比如说read(fd, xxx)之类,或者一些其他的处理
do_use_fd(events[n].data.fd);
}
}
}

epoll模型

下面是个epoll的使用例子:

 #include <iostream>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <strings.h> using namespace std; #define MAXLINE 5
#define OPEN_MAX 100
#define LISTENQ 20
#define SERV_PORT 5000
#define INFTIM 1000 void setnonblocking(int sock)
{
int opts;
opts = fcntl(sock, F_GETFL);
if(opts < ) {
perror("fcntl(sock, GETFL)");
exit();
}
opts = opts | O_NONBLOCK;
if(fcntl(sock, F_SETFL, opts) < ) {
perror("fcntl(sock, SETFL, opts)");
exit();
}
} int main()
{
int i, maxi, listenfd, connfd, sockfd, epfd, nfds;
ssize_t n;
char line[MAXLINE];
socklen_t clilen; struct epoll_event ev, events[];
epfd = epoll_create();
struct sockaddr_in clientaddr;
struct sockaddr_in serveraddr;
listenfd = socket(AF_INET, SOCK_STREAM, ); setnonblocking(listenfd);
ev.data.fd = listenfd;
ev.events = EPOLLIN | EPOLLET;
epoll_ctl(epfd, EPOLL_CTL_ADD, listenfd, &ev);
bzero(&serveraddr, sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
char *local_addr = "192.168.1.63";
inet_aton(local_addr, &(serveraddr.sin_addr));
serveraddr.sin_port = htons(SERV_PORT);
bind(listenfd, (sockaddr *)&serveraddr, sizeof(serveraddr));
listen(listenfd, LISTENQ); maxi = ;
for( ; ; ) {
nfds = epoll_wait(epfd, events, , );
for(i=;i<nfds;i++) {
if(events[i].data.fd == listenfd) {
connfd = accept(listenfd, (sockaddr *)&clientaddr, &clilen);
if(connfd < ) {
perror("connfd < 0");
exit();
}
setnonblocking(connfd);
char *str = inet_ntoa(clientaddr.sin_addr);
std::cout<<"connect from"<<str<<std::endl;
ev.data.fd = connfd;
ev.events = EPOLLIN | EPOLLET;
epoll_ctl(epfd, EPOLL_CTL_ADD, connfd, &ev);
} else if(events[i].events & EPOLLIN) {
if((sockfd = events[i].data.fd) < ) {
continue;
}
if((n = read(sockfd, line, MAXLINE)) < ) {
if(errno == ECONNRESET) {
close(sockfd);
events[i].data.fd = -;
} else {
std::cout<<"readline error"<<std::endl;
}
} else if(n == ) {
close(sockfd);
events[i].data.fd = -;
}
ev.data.fd = sockfd;
ev.events = EPOLLOUT | EPOLLET;
epoll_ctl(epfd, EPOLL_CTL_MOD, sockfd, &ev);
} else if(events[i].events & EPOLLOUT) {
sockfd = events[i].data.fd;
write(sockfd, line, n);
ev.data.fd = sockfd;
ev.events = EPOLLIN | EPOLLET;
epoll_ctl(epfd, EPOLL_CTL_MOD, sockfd, &ev);
}
}
}
}

epoll例子

epoll学习的更多相关文章

  1. 【UNIX】select、poll、epoll学习

    三者都是UNIX下多路复用的内核接口,select是跨平台的接口,poll是systemV标准,epoll是linux专有的接口,基于poll改造而成. select 函数原型: int select ...

  2. I/O复用模型之epoll学习

    简介: epoll是linux下多路复用I/O接口select/poll的增强版,它能够显著提高程序在大量并发连接中只有少量活跃的情况下的系统cpu利用率,原因是它会复用文件描述符集合来传递结果而不用 ...

  3. linux epoll 学习

    一.epoll介绍 epoll是linux内核为处理大批量句柄而作的改进的poll,是linux下IO多路复用select.poll的增强版,它能显著减少程序在大量并发连接中只有少量活跃的情况下的系统 ...

  4. linux epoll学习

    #include <sys/time.h> /* For portability */ #include <sys/select.h> int select(int nfds, ...

  5. epoll学习(二)

    首先看程序一,这个程序想要实现的功能是当用户从控制台有任何输入操作时,输出”hello world!”. l 程序一 #include <unistd.h> #include <io ...

  6. epoll相关

    1) 能不能给一个使用epoll相关API进行IO监控的示例?在<<epoll学习笔记>>中有一个简单的示例说明epoll相关API的使用, 但是这个示例是非常简单的, 它仅仅 ...

  7. epoll 系列函数简介、与select、poll 的区别

    一.epoll 系列函数简介 #include <sys/epoll.h> int epoll_create(int size); int epoll_create1(int flags) ...

  8. python开发学习-day10(select/poll/epoll回顾、redis、rabbitmq-pika)

    s12-20160319-day10 *:first-child { margin-top: 0 !important; } body>*:last-child { margin-bottom: ...

  9. 网络编程基础——学习阻塞,非阻塞(select和epoll)

    <h3 class="xyn" helvetica="" neue',="" helvetica,="" aria ...

随机推荐

  1. Android USB驱动源码分析(-)

    Android USB驱动中,上层应用协议里最重要的一个文件是android/kernel/drivers/usb/gadget/android.c.这个文件实现USB的上层应用协议. 首先包含了一些 ...

  2. .Net Core 使用Redis进行数据缓存

    1.运行环境 开发工具:Visual Studio 2017 JDK版本:.NET Core 2.0 项目管理工具:nuget 2.GITHUB地址 https://github.com/nbfujx ...

  3. shell脚本学习 (9) 提取开头或结尾的几行

    1 提取开头的n行 用head awk或者 sed实现 do.txt sed 1q do.txt awk 'FNR <= 1' do.txt do.txt文件 2 显示行尾的几行 用tail - ...

  4. Android开发新手常见的10个误区

    在过去十年中最流行的移动应用开发开发平台中,我们认为,Android平台是一个新开发的最方便的平台.一个廉价的工具,友好的开发者社区,众所周知的编程语言(Java),使得开发Android应用程序从未 ...

  5. 简单js表单验证

     简单js表单验证demo <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org ...

  6. Maven之搭建本地私服(nexus)仓库

    摘要:现在越来越多的项目都在使用Maven管理项目,尤其是在大型的项目团队中使用Maven能带来更加多的好处,私服的好处我相信大家都明白,在这里我就不多说了,它最重要的作用就是可以让项目团队成员更加方 ...

  7. https中引用http

    https里引用http不安全,会报错 解决方案 1.可以部署在http中,http中引用https不会存在安全问题报错 2.https中引用https,把需要引用的http部署成https

  8. python 装饰器 第九步:使用类来作为装饰器

    #第九步:使用类来作为装饰器 class kuozhan: #接收装饰器的参数(函数outer) def __init__(self,arg): print(self,arg)#arg就是la sel ...

  9. 怒转一波,此人整理的Flink特别好

    Apache Flink:特性.概念.组件栈.架构及原理分析 Apache Flink是一个面向分布式数据流处理和批量数据处理的开源计算平台,它能够基于同一个Flink运行时(Flink Runtim ...

  10. Workbox使用策略

    1.什么是Workbox Strategies? 当service workers 首次被引入时,可以设定一组常见的缓存策略. 缓存策略是一种模式,用于确定service workers 在收到fet ...