epoll学习
一、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学习的更多相关文章
- 【UNIX】select、poll、epoll学习
三者都是UNIX下多路复用的内核接口,select是跨平台的接口,poll是systemV标准,epoll是linux专有的接口,基于poll改造而成. select 函数原型: int select ...
- I/O复用模型之epoll学习
简介: epoll是linux下多路复用I/O接口select/poll的增强版,它能够显著提高程序在大量并发连接中只有少量活跃的情况下的系统cpu利用率,原因是它会复用文件描述符集合来传递结果而不用 ...
- linux epoll 学习
一.epoll介绍 epoll是linux内核为处理大批量句柄而作的改进的poll,是linux下IO多路复用select.poll的增强版,它能显著减少程序在大量并发连接中只有少量活跃的情况下的系统 ...
- linux epoll学习
#include <sys/time.h> /* For portability */ #include <sys/select.h> int select(int nfds, ...
- epoll学习(二)
首先看程序一,这个程序想要实现的功能是当用户从控制台有任何输入操作时,输出”hello world!”. l 程序一 #include <unistd.h> #include <io ...
- epoll相关
1) 能不能给一个使用epoll相关API进行IO监控的示例?在<<epoll学习笔记>>中有一个简单的示例说明epoll相关API的使用, 但是这个示例是非常简单的, 它仅仅 ...
- epoll 系列函数简介、与select、poll 的区别
一.epoll 系列函数简介 #include <sys/epoll.h> int epoll_create(int size); int epoll_create1(int flags) ...
- python开发学习-day10(select/poll/epoll回顾、redis、rabbitmq-pika)
s12-20160319-day10 *:first-child { margin-top: 0 !important; } body>*:last-child { margin-bottom: ...
- 网络编程基础——学习阻塞,非阻塞(select和epoll)
<h3 class="xyn" helvetica="" neue',="" helvetica,="" aria ...
随机推荐
- idea将本地项目推送到git远程库
如何将本地项目推送到github远程仓库? 1. 在github上创建一个仓库,取名mybatis 2. 在idea中将项目交由git管理 注意,文件名会变红了, 说明这些文件在git工作区,但还没规 ...
- LDD3 第13章 USB驱动程序
通用串行总线(USB)是主机和外围设备之间的一种连接.最新USB规范修订增加了理论上高达480Mbps的高速连接. 从拓扑上看,USB子系统并不是以总线的方式来布置的,它是一颗由几个点对点的连接构建而 ...
- 【CF741D】Arpa’s letter-marked tree and Mehrdad’s Dokhtar-kosh paths(dsu on tree)
题意:我们称一个字符串为周驿东串当且仅当重排它的字符可以组成一个回文串. 给出一个n个点的有根树,根为1,每条边上有一个从a到v的字符,求每个点的子树中所有简单路径可以组成的周驿东串中的最长长度. n ...
- HBASE(分布式海量NOSQL数据库)
HBase建表高级属性,hbase应用案例看行键设计,HBase和mapreduce结合,从Hbase中读取数据.分析,写入hdfs,从hdfs中读取数据写入Hbase,协处理器和二级索引 1. Hb ...
- CLLocationManager在多线程下使用
如果是子线程中创建CLLocationManager,那么startUpdatingLocation后是无法定位的,任何代理函数都不会被调用,而且表面上还会有提示是否定位的MessageBox,一切看 ...
- python中将'12345'转换为12345,不要使用int
#将'12345'转换为12345,不要使用int s = " #定义一个数字变量 ret = 0 for x in s : ret = ret*10 +( ord(x)-ord(" ...
- Linux随笔 - linux 多个会话同时执行命令后history记录不全的解决方案【转载】
基本认识linux默认配置是当打开一个shell终端后,执行的所有命令均不会写入到~/.bash_history文件中,只有当前用户退出后才会写入,这期间发生的所有命令其它终端是感知不到的. 问题场景 ...
- 用 Flask 来写个轻博客 (25) — 使用 Flask-Principal 实现角色权限功能
目录 目录 前文列表 扩展阅读 Flask-Principal 使用 Flask-Principal 来实现角色权限功能 添加 Role Model 在 Manager shell 中手动的添加角色 ...
- 聚合函数:sum,count,max,avg
聚合函数:sum,count,max,avg等,一般作用于多条记录上.通过group by可以将数据对属于一组的数据起作用. SELECT region, SUM(population), SUM(a ...
- javascript: 禁用右键、文本选择功能、复制按键
<script type="text/javascript"> //禁用右键.文本选择功能.复制按键 //http://www.jinyuanbao.cn $(docu ...