- 浏览: 203058 次
- 性别:
- 来自: 重庆
文章分类
最新评论
epoll是Linux下多路复用IO接口select/poll的增强版本,它能显著减少程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率。
一、epoll的优点
支持一个进程打开大数目的socket描述符。
IO效率不随FD数目增加而线性下降。
二、epoll的使用
epoll有2种工作方式:LT和ET。
LT(level triggered,水平触发)是缺省的工作方式,并且同时支持block和no-block socket.在这种做法中,
内核告诉你一个文件描述符是否就绪了,然后你可以对这个就绪的fd进行IO操作。如果你不作任操作,
内核还是会继续通知你的,所以,这种模式编程出错误可能性要小一点。传统的select/poll都是这种模型
的代表。
ET (edge-triggered,边缘触发)是高速工作方式,只支持no-block socket。在这种模式下,当描述符从未
就绪变为就绪时,内核通过epoll告诉你。然后它会假设你知道文件描述符已经就绪,并且不会再为那个文
件描述符发送更多的就绪通知,直到你做了某些操作导致那个文件描述符不再为就绪状态了(比如,你在
发送,接收或者接收请求,或者发送接收的数据少于一定量时导致了一个EWOULDBLOCK 错误)。但是
请注意,如果一直不对这个fd作IO操作(从而导致它再次变成未就绪),内核不会发送更多的通知
(only once)。
epoll相关的系统调用有3个:epoll_create, epoll_ctl和epoll_wait。在头文件<sys/epoll.h>
1. int epoll_create(int size);
创建一个epoll句柄,即图中的epfd, 用来监听事件, size用来告诉内核这个监听的数目一共有多大。
这个参数不同于select()中的第一个参数,给出最大监听的fd+1的值。需要注意的是,当创建好epoll句柄后,它就是会占用一个fd值,所以在使用完epoll后,必须调用close()关闭,否则可能导致fd被耗尽。
2. int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
参数op是操作类型, 使用这个方法完成3种操作:
EPOLL_CTL_ADD:注册新的fd到epfd中;
EPOLL_CTL_MOD:修改已经注册的fd的监听事件;
EPOLL_CTL_DEL:从epfd中删除一个fd;
(1) 注册新事件
- struct epoll_event ev;
- ev.data.fd = fd;
- ev.events = EPOLLIN;
- epoll_cntl(epfd, EPOOL_CTL_ADD, fd, &ev);
(2) 修改监听事件
- struct epoll_event *a_event = get_a_event()
- struct epoll_event ev;
- ev.data.fd = a_event->data.fd;
- ev.events = a_event->events | EPOLLOUT;
- epoll_cntl(epfd, EPOOL_CTL_MOD, a_event->data.fd, &ev);
(3) 删除事件
- struct epoll_event *a_event = get_a_event()
- struct epoll_event ev;
- ev.data.fd = a_event->data.fd;
- epoll_cntl(epfd, EPOOL_CTL_DEL, a_event->data.fd, &ev);
3种操作都使用了一个ev变量, 这个变量用来关联fd和它的监听事件, 是临时的, 可以反复使用.
ev是一个struct 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)来说的。
注意多个socket可以设置不同的触发模式
EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列里
3. int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
等待事件的产生, 把产生的事件存放到events数组里, 如图中所示, 调用epoll_wait后,
fd 1和 fd 3和fd k产生了事件, 把它们分别存放到events[0], events[1], events[2]
参数epfd:epoll_create()函数返回的epoll句柄。
参数events:struct epoll_event结构指针,用来从内核得到事件的集合。
参数 maxevents:告诉内核这个events有多大
参数 timeout: 等待时的超时时间,以毫秒为单位。
返回值:成功时,返回需要处理的事件数目。调用失败时,返回0,表示等待超时。
三 epoll实例 -- 模拟HTTP服务器
- #include <sys/socket.h>
- #include <sys/wait.h>
- #include <netinet/in.h>
- #include <netinet/tcp.h>
- #include <sys/epoll.h>
- #include <sys/sendfile.h>
- #include <sys/stat.h>
- #include <unistd.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <strings.h>
- #include <fcntl.h>
- #include <errno.h>
- #define MAX_EVENTS 10
- #define PORT 8080
- //设置socket连接为非阻塞模式
- void setnonblocking(int sockfd) {
- int opts;
- opts = fcntl(sockfd, F_GETFL);
- if(opts < 0) {
- perror("fcntl(F_GETFL)\n");
- exit(1);
- }
- opts = (opts | O_NONBLOCK);
- if(fcntl(sockfd, F_SETFL, opts) < 0) {
- perror("fcntl(F_SETFL)\n");
- exit(1);
- }
- }
- int main(){
- struct epoll_event ev, events[MAX_EVENTS];
- int addrlen, listenfd, conn_sock, nfds, epfd, fd, i, nread, n;
- struct sockaddr_in local, remote;
- char buf[BUFSIZ];
- //创建listen socket
- if( (listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
- perror("sockfd\n");
- exit(1);
- }
- bzero(&local, sizeof(local));
- local.sin_family = AF_INET;
- local.sin_addr.s_addr = htonl(INADDR_ANY);;
- local.sin_port = htons(PORT);
- if( bind(listenfd, (struct sockaddr *) &local, sizeof(local)) < 0) {
- perror("bind\n");
- exit(1);
- }
- listen(listenfd, 20);
- epfd = epoll_create(MAX_EVENTS);
- if (epfd == -1) {
- perror("epoll_create");
- exit(EXIT_FAILURE);
- }
- ev.events = EPOLLIN;
- ev.data.fd = listenfd;
- if (epoll_ctl(epfd, EPOLL_CTL_ADD, listenfd, &ev) == -1) {
- perror("epoll_ctl: listen_sock");
- exit(EXIT_FAILURE);
- }
- for (;;) {
- nfds = epoll_wait(epfd, events, MAX_EVENTS, -1);
- if (nfds == -1) {
- perror("epoll_pwait");
- exit(EXIT_FAILURE);
- }
- for (i = 0; i < nfds; ++i) {
- fd = events[i].data.fd;
- if (fd == listenfd) {
- conn_sock = accept(listenfd,
- (struct sockaddr *) &remote, &addrlen);
- if (conn_sock == -1) {
- perror("accept");
- exit(EXIT_FAILURE);
- }
- setnonblocking(conn_sock);
- ev.events = EPOLLIN | EPOLLET;
- ev.data.fd = conn_sock;
- if (epoll_ctl(epfd, EPOLL_CTL_ADD, conn_sock,
- &ev) == -1) {
- perror("epoll_ctl: add");
- exit(EXIT_FAILURE);
- }
- continue;
- }
- if (events[i].events & EPOLLIN) {
- n = 0;
- while ((nread = read(fd, buf + n, BUFSIZ-1)) > 0) {
- n += nread;
- }
- buf[n] = '\0';
- ev.data.fd = fd;
- ev.events = events[i].events | EPOLLOUT;
- if (epoll_ctl(epfd, EPOLL_CTL_MOD, fd, &ev) == -1) {
- perror("epoll_ctl: mod");
- }
- }
- if (events[i].events & EPOLLOUT) {
- sprintf(buf, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\n\r\nHello World", 11);
- n = strlen(buf);
- if (write(fd, buf, n) < n) {
- perror("write");
- }
- close(fd);
- }
- }
- }
- return 0;
- }
运行程序后, 打开浏览器:
发表评论
-
tcp socket的发送与接收缓冲区
2012-09-17 14:32 6463tcp socket的发送缓冲 ... -
浅谈TCP/IP网络编程中socket的行为
2012-09-18 11:11 1158我认为,想要熟练掌握Linux下的TCP/IP网络编程, ... -
再谈应用环境下的TIME_WAIT和CLOSE_WAIT
2012-09-13 15:15 1050昨天解决了一个HttpClient调用错误导致的服务器异 ... -
epoll在LT和ET模式下的读写方式
2012-09-13 14:43 2412ET模型的逻辑:内核的读buffer有内核态主动变化时, ... -
分片重组与原始套接字
2012-09-13 13:42 2133winpcap是对链路层的封装,而链路层是不对IP分片进 ... -
Linux中的EAGAIN含义
2012-09-13 11:06 897在Linux环境下开发经常会碰到很多错误(设置errno),其 ... -
listen和accept的套接字描述符有什么用
2012-09-10 16:32 1798在阅读创建socketpair时发现不太理解socket中li ... -
linux下socket connect 阻塞方式 阻塞时间控制(转)
2012-09-10 15:34 2136同事今天问我,如何在linux下的c代码里面控 ...
相关推荐
epoll 内核 代码 学习,不完整,须改进,epoll 内核 代码 学习epoll 内核 代码 学习epoll 内核 代码 学习epoll 内核 代码 学习epoll 内核 代码 学习
linux下的epoll模拟IOCP异步的demo,希望对学习epoll的朋友有所帮助。
使用Python做的epoll高效率服务器。使用平台:Linux,windows不支持;带测试客户端,适合新手学习
epoll学习实例,epoll是Linux内核为处理大批量文件描述符而作了改进的poll,是Linux下多路复用IO接口select/poll的增强版本,它能显著提高程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率。另一点原因就是...
linux下的Tcp服务器,采用的是Epoll模式,linux下用epoll实现的udp服务器例子
epoll 编写的多线程服务器 可以学习使用
本示例演示了使用epoll函数处理多路IO复用。本示例在ubuntu + Qt下编译通过的,仅提供学习使用。
自己学习并改写的epoll代码,在Linux测试没问题,分别实现了epoll的LT模式和ET模式,初学者可以学一学
想学习epoll模型的朋友们,这个是个不错的例子哦~包您满意
linux 网络编程 epoll 简单示例 客户端 源码 只为学习epoll用 有不妥之处欢迎指正
用C实现的epoll服务器的实例,带一个简单的测试客户端,可用于初学者学习epoll编程
linux socket epoll 模型,linux网络编程学习实例。
个人学习过程中写的基于epoll的简单socket server
unbound + libevent + epoll学习.txt
这是最新epoll编码学习笔记,它能告诉你如何在linux系统下构建超级服务器。
一个epoll的实例,非常实用的例子,学习tcp、ip很好的例子,学习网络编程很好的例子,了解linux下面的io多路复用
它主要涉及到TCP/UDP协议以及select/poll/epoll等多路复用技术。 TCP/UDP协议是网络通信的基础,其中TCP协议提供面向连接的可靠数据传输,而UDP协议则提供无连接的不可靠数据传输。在Linux网络编程中,开发者需要...
C++ epoll方式 client与server stocket连接通信例子, 供大家学习参考
Epoll Epoll学习
linux 下用epoll写tcp服务器的完整例子,还有通用makefile的例子。是一个学习linux服务器编程的好例子。