Server Develop (六) Linux epoll总结
Linux epoll
epoll是Kernel 2.6后新加入的事件机制,在高并发条件下,远优于select。epoll最大的好处在于它不会随着监听fd数目的增长而降低效率。因为在内核中的select实现中,它是采用轮询来处理的,轮询的fd数目越多,自然耗时越多。并且,在linux/posix_types.h头文件有这样的声明:
#define __FD_SETSIZE 1024 //select最多同时监听1024个fd当然,可以通过修改头文件再重编译内核来扩大这个数目,但这似乎并不治本。
所以在Nginx中采用了epoll来实现其高并发特性。
工作方式
LT(level triggered):水平触发,缺省方式,同时支持block和no-block socket,在这种做法中,内核告诉我们一个文件描述符是否被就绪了,如果就绪了,你就可以对这个就绪的fd进行IO操作。如果你不作任何操作,内核还是会继续通知你的,所以,这种模式编程出错的可能性较小。传统的select\poll都是这种模型的代表。
ET(edge-triggered):边沿触发,高速工作方式,只支持no-block socket。在这种模式下,当描述符从未就绪变为就绪状态时,内核通过epoll告诉你。然后它会假设你知道文件描述符已经就绪,并且不会再为那个描述符发送更多的就绪通知,直到你做了某些操作导致那个文件描述符不再为就绪状态了(比如:你在发送、接受或者接受请求,或者发送接受的数据少于一定量时导致了一个EWOULDBLOCK错误)。但是请注意,如果一直不对这个fs做IO操作(从而导致它再次变成未就绪状态),内核不会发送更多的通知。
区别:LT事件不会丢弃,而是只要读buffer里面有数据可以让用户读取,则不断的通知你。而ET则只在事件发生之时通知。
主要的数据结构
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 event */
epoll_data_t data; /* User data variable */
};
events表示感兴趣的事件和被触发的事件,可能的取值为:
EPOLLIN | 对应的文件描述符可以读 |
EPOLLOUT | 对应的文件描述符可以写 |
EPOLLPRI | 对应的文件描述符有紧急的数可读 |
EPOLLERR | 对应的文件描述符发生错误 |
EPOLLHUP | 对应的文件描述符被挂断 |
EPOLLET | 将EPOLL设为边缘触发(Edge Triggered)模式,这是相对于水平触发(Level Triggered)来说的 |
EPOLLONESHOT | 只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列里 |
操作函数
epoll的接口非常简单,用三个相关函数来创建epoll句柄、注册epoll事件以及等待事件的发生。
创建epoll句柄:
int epoll_create(int size);
//size表示内核需要监听的数目
//return : epoll文件描述符
需要注意的是,当创建好epoll句柄后,它就是会占用一个fd值(文件标识符),在linux下如果查看/proc/进程id/fd/,是能够看到这个fd的,所以在使用完epoll后,必须调用close()关闭,否则可能导致fd被耗尽。
epoll事件注册函数:
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event) //epfd是epoll_create()的返回值
//op表示动作
/* op可被表示为:
EPOLL_CTL_ADD:注册新的fd到epfd中;
EPOLL_CTL_MOD:修改已经注册的fd的监听事件;
EPOLL_CTL_DEL:从epfd中删除一个fd;
*/
//fd是需要监听的fd
//event是内核需要监听的事件
等待事件发生函数:
int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout)
//epfd是函数返回值
//events是内核监听事件的集合
//maxevents是epoll_wait可以处理的连接事件的最大限度值
//timeout是超时时间 //返回值:请求数
epoll工作流程
首先,需要调用epoll_create创建epoll,此后我们就可以进行socket/bind/listen,然后调用epoll_ctl进行注册。接下来,就可以通过一个while(1)循环调用epoll_wait来等待事件的发生,然后循环查看接收到的事件并进行处理。如果事件是sever的socketfd我们就要进行accept,并且把接收到client的socketfd加入到要监听的事件中。如果在监听过程中,需要修改操作方式(读/写),可以调用epoll_ctl来重新修改。如果监听到某一个客户端关闭,那么我就需要再次调用epoll_ctl把它从epoll监听事件中删除。
实例
#include <stdio.h>
#include <sys/time.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/epoll.h>
#include <netinet/in.h>
#include <fcntl.h>
#include <stdlib.h>
#include <errno.h> void setnonblocking(int sockfd)
{
int opts;
opts = fcntl(sockfd, F_GETFL);
if(opts < ){
perror("fcntl1 error!\n");
exit();
}
opts = opts | O_NONBLOCK;
if(fcntl(sockfd, F_SETFL, opts) < ){
perror("fcntl2 error!\n");
exit();
}
} int main()
{
int fd;
int on;
int rs;
int len;
int conn;
char buffer[];
int flag1, flag2;
struct sockaddr_in serv_addr, clt_addr;
struct timeval timeout; int i;
int nfds;
int epfd;
int newfd; struct epoll_event ev;
struct epoll_event events[]; fd = socket(AF_INET, SOCK_STREAM, ); on = ;
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
timeout.tv_sec = ;
timeout.tv_usec = ;
setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout)); bzero(&serv_addr, sizeof(struct sockaddr_in));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons();
serv_addr.sin_addr.s_addr = INADDR_ANY; rs = bind(fd, (struct sockaddr*)(&serv_addr), sizeof(struct sockaddr));
if(rs < ){
perror("");
close(fd);
return -;
} setnonblocking(fd); epfd = epoll_create(); ev.data.fd = fd;
ev.events = EPOLLIN|EPOLLET;
epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev); rs = listen(fd, );
if(rs < ){
perror("");
close(fd);
return -;
} len = sizeof(struct sockaddr); for(;;){
nfds = epoll_wait(epfd, events, , ); for(i = ; i < nfds; ++i){
if(events[i].data.fd == fd){
conn = accept(fd, (struct sockaddr*)(&clt_addr), (unsigned int*)(&len)); setnonblocking(conn); ev.data.fd = conn;
ev.events = EPOLLIN|EPOLLET;
epoll_ctl(epfd, EPOLL_CTL_ADD, conn, &ev);
}
else if(events[i].events & EPOLLIN){
if((newfd = events[i].data.fd) < )
continue;
bzero(buffer, sizeof(buffer));
flag1 = recv(newfd, buffer, , );
printf("recv: %s\n", buffer);
printf("recv return: %d\n", flag1); ev.data.fd = newfd;
ev.events = EPOLLOUT|EPOLLET;
epoll_ctl(epfd, EPOLL_CTL_MOD, newfd, &ev);
}
else if(events[i].events & EPOLLOUT){
if((newfd = events[i].data.fd) < )
continue; bzero(buffer, sizeof(buffer));
strcpy(buffer, "ACK");
flag2 = send(newfd, buffer, sizeof(buffer), );
printf("recv return: %d\n\n", flag2); ev.data.fd = newfd;
ev.events = EPOLLIN|EPOLLET;
epoll_ctl(epfd, EPOLL_CTL_MOD, newfd, &ev);
}
}
}
close(fd); return ;
}
参考
http://www.cnblogs.com/venow/archive/2012/11/30/2790031.html
http://hi.baidu.com/jingweiyoung/item/ae9fc81714be67dbbf9042b9
http://www.linuxidc.com/Linux/2011-04/35156p3.htm
http://www.cppblog.com/converse/archive/2008/04/29/48482.html
Server Develop (六) Linux epoll总结的更多相关文章
- Server Develop (七) Linux 守护进程
守护进程 守护进程,也就是通常说的Daemon进程,是Linux中的后台服务进程.它是一个生存期较长的进程,通常独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件.守护进程常常在系统引导装 ...
- (OK) Linux epoll模型—socket epoll server client chat
http://www.cnblogs.com/venow/archive/2012/11/30/2790031.html http://blog.csdn.net/denkensk/article/d ...
- Linux Epoll介绍和程序实例
Linux Epoll介绍和程序实例 1. Epoll是何方神圣? Epoll但是当前在Linux下开发大规模并发网络程序的热门人选,Epoll 在Linux2.6内核中正式引入,和select类似, ...
- c/c++ linux epoll系列2 利用epoll_wait查看是否可以送信
linux epoll系列2 利用epoll_wait查看是否可以送信 write函数本来是非阻塞函数,但是当缓存区被写满后,再往缓存区里写的时候,就必须等待缓存区再次变成可写,所以这是write就变 ...
- Java网络编程和NIO详解6:Linux epoll实现原理详解
Java网络编程和NIO详解6:Linux epoll实现原理详解 本系列文章首发于我的个人博客:https://h2pl.github.io/ 欢迎阅览我的CSDN专栏:Java网络编程和NIO h ...
- Enabling Active Directory Authentication for VMWare Server running on Linux《转载》
Enabling Active Directory Authentication for VMWare Server running on Linux Version 0.2 - Adam Breid ...
- Linux epoll总结
Linux epoll总结 Linux epoll epoll是Kernel 2.6后新加入的事件机制,在高并发条件下,远优于select.epoll最大的好处在于它不会随着监听fd数目的增长而降低 ...
- c/c++ linux epoll系列3 利用epoll_wait设置timeout时间长度
linux epoll系列3 利用epoll_wait设置timeout时间长度 epoll_wait函数的第四个参数可以设置,epoll_wait函数的等待时间(timeout时间长度). 例子1, ...
- c/c++ linux epoll系列1 创建epoll
linux epoll系列1 创建epoll 据说select和poll的弱点是,随着连接(socket)的增加,性能会直线下降. epoll不会随着连接(socket)的增加,性能直线下降. 知识点 ...
随机推荐
- 《深入理解Nginx》阅读与实践(四):简单的HTTP过滤模块
一.Nginx的HTTP过滤模块特征 一个请求可以被任意个HTTP模块处理: 在普通HTTP模块处理请求完毕并调用ngx_http_send_header()发送HTTP头部或调用ngx_http_o ...
- 使用timer控件控制进度条
using System;using System.Collections.Generic;using System.ComponentModel;using System.Data;using Sy ...
- linq 延迟执行带来的困扰
有这样一个案例: var filteredResult = from f in orgFileList select f; ; i < WorkStatusFilters.ListWorkSta ...
- apache 泛域名配置
需求: 在apache上配置两个项目,分别是项目a和项目b,a.baiye5.com访问项目a,其余的除了a前缀名之外的例如b2b.baiye5.com .*.baiye5.com等都访问项目b. ...
- 超链接的那些事(二): 属性href
a标签的属性之一 href 1. 定义 href 属性用于指定超链接目标的 URL. 2. 用法 ①. 锚点 同一页面添加锚点 (1)<a href="#test"& ...
- C++和C#混合编程
最近需要利用C++和C#混合编程,然后就写了一个C#调用C++生成的DLL的DEMO.困扰我好久的就是C#中string类型在C++里面怎么表达,现在把C++生成DLL供C#调用的流程写出来. 源码: ...
- media type的类型汇总
用的比较多的是screen和print:区分打印和屏幕显示(Android,iPhone都不是手持设备handheld,都是screen设备)
- Linux命令(1)- grep
1.grep 功能:查找文件里符合条件的字符串. 语法:grep [-abcEFGhHilLnqrsvVwxy][-A<显示列数>][-B<显示列数>][-C<显示列数& ...
- 尽量少用if else
Michael Feathers是Object Mentor International公司的技术顾问.他的工作不仅是技术开发,他还参与对世界各地技术团队进行培训.指导等工作.他曾开发了将JUnit迁 ...
- ICMP
(一)ICMP IP是一个尽力的不可靠的协议,IP不能提供差错控制(如果数据在传播过程中出现错误了),这个时候ICMP就起作用了. ICMP提供两个功能:差错的报告,查询. ICMP的ICMP包分为两 ...