简介:

epoll是linux下多路复用I/O接口select/poll的增强版,它能够显著提高程序在大量并发连接中只有少量活跃的情况下的系统cpu利用率,原因是它会复用文件描述符集合来传递结果而不用迫使开发者每次等待事件之前都必须重新准备要被侦听的描述符,另一个原因就是获取事件的时候,它不必遍历整个被监听的描述符集,只要遍历那些被内核IO事件异步唤醒而加入ready队列的描述符集合就行了。

优点:

1、支持一个进程打开大数目的socket描述符:select最不能忍受的是一个进程所打开的FD是有一定限制的,由FD_SETSIZE设置,默认值是1024,。对于那些需要支持上万连接数目的IM服务器来说实在是太少了。

2、IO效率不随FD数目的增加而线性下降:传统的select/poll的一个缺陷就是当你拥有一个很大的socket集合,由于网络延时,任意时间只有一部分的socket是“活跃"的,但是select/poll每次调用都会线性扫描全部的集合,导致效率呈现线性的下降。但是epoll不存在这个问题,它只会对“活跃”的socket进行操作,原因是在内核实现中epoll是根据每个fd上面的callback函数实现的,其他idle状态socket则不会。但是,如果所有的socket基本上都是活跃的,比如说一个高速的LAN网络,epoll并不比select/poll有什么效率,相反,如果过多食用epoll_ctl,效率还会稍微下降。

3、select从内核到用户空间传递文件描述符上的发送的信息都是使用内存复制的方式进行的,而epoll是采用共享内存的方式。

epoll中的三个主要的函数:

1、int epoll_create(int size):

函数功能:生成一个epoll专用的文件描述符。

函数参数:size表示用来告诉内核这个监听的最大数目。

函数返回值:生成的文件描述符。

需要注意的地方:当创建好一个epoll句柄后,他就会占用一个fd值,所以在使用完epoll后,必须调用close()函数关闭,否则可能导致fd被耗尽。

2、int epoll_ctl(int epfd, int op, int fd, struct epoll_event * event):

函数功能:控制某个epoll文件描述符上的事件,可以注册事件,修改时间,删除事件。

函数参数:

epfd:由epoll_create生成的epoll专用的文件描述符。

op:EPOLL_CTL_ADD注册、EPOLL_CTL_MOD修改、EPOLL_CTL_DEL删除。

fd:关联的文件描述符。

event:指向epoll_event的指针。

函数返回值:0表示成功,-1表示失败。

3、int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout):

函数功能:轮询I/O时间的发生,类似于selelct()。

函数参数:

epfd:由epoll_create生成的epoll专用的文件描述符。

events:用于回传待处理事件的数组。

maxevents:每次能处理的事件数。

timeout:等待I/O事件发生的超时值,-1相当于阻塞,0相当于非阻塞。

函数返回值:>= 0表示返回的事件数,-1表示错误。

epoll中的主要用到的数据结构:

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:文件描述符可以读。

EPOLLOUT:文件描述符可以写。

EPOLLPRI:文件描述符有紧急的数据可读。

EPOLLERR:文件描述符发生错误。

EPOLLHUP:文件描述符被挂断。

EPOLLET:文件描述符有事件发生。

epoll的两种工作方式:

1、ET:即Edge Triggered,边缘触发。仅当状态发生变化时才会通知,需要细致的处理每个请求,否则容易发生丢失事件的情况。只支持非阻塞的socket。

2、LT:Level Triggered,水平触发(默认工作方式)。只要还有没有处理的事件就会一直通知,因此不用担心事件丢失的情况。效率会低于ET触发,尤其在大并发,大流量的情况下。支持阻塞和非阻塞的socket。

服务器端:

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/epoll.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h> #define SERV_PORT 1234 /*服务器监听的端口号*/
#define MAX_CLINE 100 /*最大同时连接请求数*/
#define MAX_EVENTS 1000
#define BUF_SIZE 1024
#define EPOLL_RUN_TIME_OUT -1 void setnonblocking(int sock)
{
int opts = fcntl(sock, F_GETFL);
if (opts < 0) {
perror("fcntl error");
exit(1);
} opts |= O_NONBLOCK; if (fcntl(sock, F_GETFL, opts) < 0) {
perror("fcntl error");
exit(1);
}
} void do_use_fd(int client_fd)
{
char message[BUF_SIZE];
int res = recv(client_fd, message, BUF_SIZE, 0);
if (res < 0) {
perror("recv error");
exit(1);
} else if (res == 0) {
close(client_fd);
} else {
const char str[] = "hehe...\n";
if (send(client_fd, str, sizeof(str), 0) == -1) {
perror("send error...");
exit(1);
}
}
} int main()
{
int sock_fd;
struct sockaddr_in server_addr, remote_addr; if ((sock_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
perror("can not create socket");
exit(1);
} server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(SERV_PORT);
server_addr.sin_addr.s_addr = INADDR_ANY;
bzero(&(server_addr.sin_zero), 8); setnonblocking(sock_fd); //设置监听socket为不阻塞
if (bind(sock_fd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) == -1) {
perror("bind error");
exit(1);
} if (listen(sock_fd, MAX_CLINE) == -1) {
perror("listen error");
exit(1);
} socklen_t sin_size = sizeof(struct sockaddr_in); struct epoll_event ev, events[MAX_EVENTS];
int conn_sock, nfds, epollfd; epollfd = epoll_create(MAX_EVENTS); //创建一个epoll描述符,并将监听socket加入epoll if (epollfd == -1) {
perror("epoll_erate error");
exit(1);
} puts("epoll_erate Ok..."); ev.events = EPOLLIN | EPOLLET; //可读,边沿触发
ev.data.fd = sock_fd; if (epoll_ctl(epollfd, EPOLL_CTL_ADD, sock_fd, &ev) == -1) {
perror("epoll_ctl error");
exit(1);
} puts("epoll_ctl Ok..."); while (true) { puts("epoll_wait start..."); //类似于select()调用,函数返回的是事件数
nfds = epoll_wait(epollfd, events, MAX_EVENTS, EPOLL_RUN_TIME_OUT);
if (nfds == -1) {
perror("epoll_wait error");
exit(1);
} printf("epoll_wait returns, nfds = %d\n", nfds); for (int i = 0; i < nfds; ++i) {
//新的连接到来,将连接添加到epoll中,并发送"welcome"消息
if (events[i].data.fd == sock_fd) {
conn_sock = accept(sock_fd, (struct sockaddr *)&remote_addr, &sin_size);
if (conn_sock == -1) {
perror("accept error");
exit(1);
} setnonblocking(conn_sock); //设为非阻塞的
ev.events = EPOLLIN | EPOLLET; //可读,边缘触发
ev.data.fd = conn_sock; if (epoll_ctl(epollfd, EPOLL_CTL_ADD, conn_sock, &ev) == -1) {
perror("eppol_ctl error");
exit(1);
} char message[BUF_SIZE];
bzero(message, BUF_SIZE);
int res = sprintf(message, "Welcome client %d", conn_sock);
if ((res = send(conn_sock, message, BUF_SIZE, 0)) < 0) {
perror("send error");
exit(1);
} else if (res == 0) {
close(conn_sock);
} } else {
printf("%d.....\n", i);
do_use_fd(events[i].data.fd);
}
} } close(sock_fd);
close(epollfd); return 0;
}

客户端:

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/epoll.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h> #define SERV_PORT 1234 /*服务器监听的端口号*/
#define MAX_EVENTS 1000
#define BUF_SIZE 100 int main()
{
int client_fd, res;
struct sockaddr_in addr; addr.sin_family = AF_INET;
addr.sin_port = htons(SERV_PORT);
addr.sin_addr.s_addr = htonl(0x7f000001); for (int i = 0; i < MAX_EVENTS; ++i) {
if((client_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
perror("can not create socket");
exit(1);
} if (connect(client_fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
perror("connect error");
exit(1);
} char message1[] = "I'm client..\n";
char message2[BUF_SIZE];
bzero(message2, BUF_SIZE); if ((res = send(client_fd, message1, BUF_SIZE, 0)) < 0) {
perror("send error");
exit(1);
} if ((res = recv(client_fd, message2, BUF_SIZE, 0)) < 0) {
perror("recv error");
exit(1);
} else if (res == 0) {
close(client_fd);
} else
printf("%s\n", message2); }
return 0;
}

I/O复用模型之epoll学习的更多相关文章

  1. I/O复用模型之select学习

    linux下的I/O模型可以分为5种: 1.阻塞式I/O模型 2.非阻塞式I/O模型 3.I/O复用模型 4.信号驱动I/O模型 5.异步I/O模型 简单解释: 阻塞和非阻塞:就是说需要做一件事的时候 ...

  2. 多路IO复用模型--select, poll, epoll

    select 1.select能监听的文件描述符个数受限于FD_SETSIZE,一般为1024,单纯改变进程打开的文件描述符个数并不能改变select监听文件个数 2.解决1024以下客户端时使用se ...

  3. 一个I/O线程可以并发处理N个客户端连接和读写操作 I/O复用模型 基于Buf操作NIO可以读取任意位置的数据 Channel中读取数据到Buffer中或将数据 Buffer 中写入到 Channel 事件驱动消息通知观察者模式

    Tomcat那些事儿 https://mp.weixin.qq.com/s?__biz=MzI3MTEwODc5Ng==&mid=2650860016&idx=2&sn=549 ...

  4. 基本I/O模型与Epoll简介

    5种基本的I/O模型:1)阻塞I/O ;2)非阻塞I/O; 3)I/O复用(select和poll);4)信号驱动I/O(SIGIO);5)异步I/O(POSIX.1的aio_系列函数). 操作系统中 ...

  5. 强化学习(十七) 基于模型的强化学习与Dyna算法框架

    在前面我们讨论了基于价值的强化学习(Value Based RL)和基于策略的强化学习模型(Policy Based RL),本篇我们讨论最后一种强化学习流派,基于模型的强化学习(Model Base ...

  6. Linux非阻塞IO(二)网络编程中非阻塞IO与IO复用模型结合

    上文描述了最简易的非阻塞IO,采用的是轮询的方式,这节我们使用IO复用模型.   阻塞IO   过去我们使用IO复用与阻塞IO结合的时候,IO复用模型起到的作用是并发监听多个fd. 以简单的回射服务器 ...

  7. COMET探索系列二【Ajax轮询复用模型】

    写在前面:Ajax轮询相信大家都信手拈来在用,可是有这么一个问题,如果一个网站中同时有好多个地方需要用到这种轮询呢?就拿我们网站来说,有一个未读消息数提醒.还有一个时实时加载最新说说.昨天又加了一个全 ...

  8. swoole(3)网络服务模型(单进程阻塞、预派生子进程、单进程阻塞复用模型)

    一:单进程阻塞 设计流程: 创建一个socket,绑定端口bind,监听端口listen 进入while循环,阻塞在accept操作上,等待客户端连接进入,进入睡眠状态,直到有新的客户发起connet ...

  9. 人工智能中小样本问题相关的系列模型演变及学习笔记(二):生成对抗网络 GAN

    [说在前面]本人博客新手一枚,象牙塔的老白,职业场的小白.以下内容仅为个人见解,欢迎批评指正,不喜勿喷![握手][握手] [再啰嗦一下]本文衔接上一个随笔:人工智能中小样本问题相关的系列模型演变及学习 ...

随机推荐

  1. FIDO 标准简介

    FIDO 联盟(Fast IDentity Online Alliance)简介 网站:http://fidoalliance.org FIDO Alliance,成立于2012年7月. FIDO的目 ...

  2. 常见web错误码 404 500

    404表示文件或资源未找到java WEB常见的错误代码1.1xx-信息提示:这些状态代码表示临时的响应.客户端在收到常规响应之前,应准备接收一个或多个1xx响应.100-继续.101-切换协议.2. ...

  3. ajax与后台交互传输数据的工具类

    public class Result<T> implements Serializable { private static final long serialVersionUID = ...

  4. Divide and conquer:Telephone Lines(POJ 3662)

    电话线 题目大意:一堆电话线要你接,现在有N个接口,总线已经在1端,要你想办法接到N端去,电话公司发好心免费送你几段不用拉网线,剩下的费用等于剩余最长电话线的长度,要你求出最小的费用. 这一看又是一个 ...

  5. September 11th 2016 Week 38th Sunday

    Nothing happens unless first a dream. 一切始于梦想. When everything seems to be going against you, remembe ...

  6. August 16th 2016 Week 34th Tuesday

    The worst solitude is to be destitute of sincere friendship. 最大的孤独莫过于没有真诚的友谊. Sometimes we pay the m ...

  7. VAssistX的VA Snippet Editor的类注释和函数注释

    title:类注释shortcut:=== /******************************************************** [DateTime]:$YEAR$.$M ...

  8. 三、jQuery--jQuery基础--jQuery基础课程--第12章 jQuery在线聊天室

    在线聊天室案例 一.功能简介: 1.用户需要登录后才能进入聊天室交流 2.已无刷新的方式,动态展示交流后的内容和在线人员的基本信息 3.登录后的用户可以提交文字和表情图标 技术重点:利用ajax的无刷 ...

  9. iOS开发-正则表达式的使用方法

    前言:在表单验证中,我们经常会使用到正则,因为我们需要用它来判断用户输入的字符是否为合法的,如果是不合法的,那么应该提示用户输入错误,并不让提交至服务器.我们也可以通过正则表达式,从用户输入的字符串中 ...

  10. 数据结构和算法 – 5.模式匹配和文本处理

    了使用正则表达式,需要把 RegEx 类引入程序.大家可以在 System.Text.RegularExpression 名字域中找到这种类.一旦把这种类导入了程序,就需要决定想要用 RegEx 类来 ...