博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
epoll服务器小结
阅读量:5908 次
发布时间:2019-06-19

本文共 7656 字,大约阅读时间需要 25 分钟。

###epoll简介:

  epoll是Linux下多路复用IO接口select/poll的增强版本,它能显著提高程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率,因为它会复用文件描述符集合来传递结果而不用迫使开发者每次等待事件之前都必须重新准备要被侦听的文件描述符集合,另一点原因就是获取事件的时候,它无须遍历整个被侦听的描述符集,只要遍历那些被内核IO事件异步唤醒而加入Ready队列的描述符集合就行了。
  目前epell是linux大规模并发网络程序中的热门首选模型。
  epoll除了提供select/poll那种IO事件的电平触发(Level Triggered)外,还提供了边沿触发(Edge Triggered),这就使得用户空间程序有可能缓存IO状态,减少epoll_wait/epoll_pwait的调用,提高应用程序效率。
可以使用cat命令查看一个进程可以打开的socket描述符上限。

cat /proc/sys/fs/file-max

如有需要,可以通过修改配置文件的方式修改该上限值。

sudo vi /etc/security/limits.conf	在文件尾部写入以下配置,soft软限制,hard硬限制。如下图所示。	* soft nofile 65536	* hard nofile 100000

###基础API

  • epoll_create

    创建一个epoll句柄,参数size用来告诉内核监听的文件描述符的个数,跟内存大小有关。	#include 
    int epoll_create(int size) size:监听数目调用之后内核创建一颗红黑树模型
  • epoll_ctl

    控制某个epoll监控的文件描述符上的事件:注册、修改、删除。在系统内核生成的红黑树上进行修改	#include 
    int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event) epfd: 为epoll_creat的句柄 op: 表示动作,用3个宏来表示: EPOLL_CTL_ADD (注册新的fd到epfd), EPOLL_CTL_MOD (修改已经注册的fd的监听事件), EPOLL_CTL_DEL (从epfd删除一个fd); event: 告诉内核需要监听的事件 struct epoll_event {
    _uint32_t events; /* Epoll events */ epoll_data_t data; /* User data variable */ }; typedef union epoll_data {
    void *ptr; int fd; uint32_t u32; uint64_t u64; } epoll_data_t; EPOLLIN : 表示对应的文件描述符可以读(包括对端SOCKET正常关闭) EPOLLOUT: 表示对应的文件描述符可以写 EPOLLPRI: 表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来) EPOLLERR: 表示对应的文件描述符发生错误 EPOLLHUP: 表示对应的文件描述符被挂断; EPOLLET: 将EPOLL设为边缘触发(Edge Triggered)模式,这是相对于水平触发(Level Triggered)而言的 EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列里
  • epoll_wait

等待所监控文件描述符上有事件的产生,类似于select()调用。	#include 
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout) events: 用来存内核得到事件的集合, maxevents: 告之内核这个events有多大,这个maxevents的值不能大于创建epoll_create()时的size, timeout:是超时时间 -1: 阻塞 0: 立即返回,非阻塞 >0: 指定毫秒 返回值: 成功返回有多少文件描述符就绪,时间到时返回0,出错返回-1

###样例:

为了使代码更加简洁,将常用的函数进行了二次封装,加入了出错处理,见wrap.c.

/*************************************************************************	> File Name: server.c	> Author: sunxingying	> Mail: 1159015605@qq.com 	> Created Time: 2017年06月29日 星期四 03时36分35秒 ************************************************************************/#include
#include
#include
#include
#include
#include
#include
#include
#include
#include"wrap.h"#define _SIZE_ 128#define OPEN_MAX 256typedef struct epoll_msg{ int fd; char buf[_SIZE_];}epoll_t,*epoll_p,**epoll_pp;static void *allocator(int fd){ epoll_p buf=(epoll_p)malloc(sizeof(epoll_t)); if(NULL==buf) { perror("malloc"); exit(6); } buf->fd=fd; return buf;}void delalloc(void *ptr){ if(NULL!=ptr) { free(ptr); }}int startup(char *ip,int port){ int sock=Socket(AF_INET,SOCK_STREAM,0); int opt=1; if(setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt))<0) { perror("setsockopt"); exit(1); } struct sockaddr_in local; local.sin_family=AF_INET; local.sin_port=htons(port); local.sin_addr.s_addr=inet_addr(ip); Bind(sock,(struct sockaddr*)&local,sizeof(local)); Listen(sock,10); return sock;}int main(int argc,char *argv[]){ if(argc!=3) { printf("请输入ip地址和端口号\n"); return 1; } int lis_sock=startup(argv[1],atoi(argv[2])); //创建epoll模型,epfd指向红黑数跟节点 int epfd=epoll_create(OPEN_MAX); if(epfd<0) { perror("epoll_create"); } struct epoll_event envs; envs.events=EPOLLIN|EPOLLET; envs.data.ptr=allocator(lis_sock); //将lis_sock及对应的结构体设置到树上,epfd可找到该树 epoll_ctl(epfd,EPOLL_CTL_ADD,lis_sock,&envs); while(1) { int num=0; int timeout=-1; struct epoll_event evs[32]; int max=32; switch((num=epoll_wait(epfd,evs,max,timeout))) { case 0: printf("time out..."); break; case -1: perror("epoll _wait"); break; default: { int i=0; for(;i
fd; if(fd==lis_sock&&evs[i].events&EPOLLIN) { struct sockaddr_in peer; socklen_t len=sizeof(peer); int connfd=Accept(lis_sock,(struct sockaddr*)&peer,&len); envs.events=EPOLLIN; envs.data.ptr=allocator(connfd); epoll_ctl(epfd,EPOLL_CTL_ADD,connfd,&envs); } else if(fd!=lis_sock&&evs[i].events&EPOLLIN) { int s=read(fd,((epoll_p)(evs[i].data.ptr))->buf,_SIZE_-1); if(s>0) { char *buf=((epoll_p)(evs[i].data.ptr))->buf; buf[s]=0; printf("client # %s\n",buf); evs[i].events=EPOLLOUT; epoll_ctl(epfd,EPOLL_CTL_MOD,fd,&evs[i]); } else if(s==0) { epoll_ctl(epfd,EPOLL_CTL_DEL,fd,NULL); delalloc(evs[i].data.ptr); close(fd); } else { perror("read"); } } else if(fd!=lis_sock&&evs[i].events&EPOLLOUT) { char *msg="http/1.0 200 ok\r\n\r\n
孙兴颖的epoll服务器

欢迎来到孙兴颖的epoll服务器

当前时间是:

"; write(fd,msg,strlen(msg)); delalloc(evs[i].data.ptr); epoll_ctl(epfd,EPOLL_CTL_DEL,fd,&evs[i]); close(fd); } } } } } return 0;}

wrap.c

#include 
#include
#include
#include
#include
void perr_exit(const char *s){ perror(s); exit(-1);}int Accept(int fd, struct sockaddr *sa, socklen_t *salenptr){ int n;again: //accept是慢速系统调用,有可能在阻塞期间被信号中断 //被信号中断时errno 被设置为ECONNABORTED或者EINTR if ((n = accept(fd, sa, salenptr)) < 0) { if ((errno == ECONNABORTED) || (errno == EINTR)) goto again; else perr_exit("accept error"); } return n;}int Bind(int fd, const struct sockaddr *sa, socklen_t salen){ int n; if ((n = bind(fd, sa, salen)) < 0) perr_exit("bind error"); return n;}int Connect(int fd, const struct sockaddr *sa, socklen_t salen){ int n; if ((n = connect(fd, sa, salen)) < 0) perr_exit("connect error"); return n;}int Listen(int fd, int backlog){ int n; if ((n = listen(fd, backlog)) < 0) perr_exit("listen error"); return n;}int Socket(int family, int type, int protocol){ int n; if ((n = socket(family, type, protocol)) < 0) perr_exit("socket error"); return n;}ssize_t Read(int fd, void *ptr, size_t nbytes){ ssize_t n;//read也是慢速系统调用//read返回值:/* * 1. >0实际读到的字节数 buf=1024 1.==buf 1024 2.
0) { if ((nread = read(fd, ptr, nleft)) < 0) { if (errno == EINTR) nread = 0; else return -1; } else if (nread == 0) break; nleft -= nread; ptr += nread; } return n - nleft;}/*writen同readn*/ssize_t Writen(int fd, const void *vptr, size_t n){ size_t nleft; ssize_t nwritten; const char *ptr; ptr = vptr; nleft = n; while (nleft > 0) { if ( (nwritten = write(fd, ptr, nleft)) <= 0) { if (nwritten < 0 && errno == EINTR) nwritten = 0; else return -1; } nleft -= nwritten; ptr += nwritten; } return n;}static ssize_t my_read(int fd, char *ptr){ static int read_cnt; static char *read_ptr; static char read_buf[100]; if (read_cnt <= 0) {again: if ( (read_cnt = read(fd, read_buf, sizeof(read_buf))) < 0) { if (errno == EINTR) goto again; return -1; } else if (read_cnt == 0) return 0; read_ptr = read_buf; } read_cnt--; *ptr = *read_ptr++; return 1;}/*readline ---fgets*///传出参数vptrssize_t Readline(int fd, void *vptr, size_t maxlen){ ssize_t n, rc; char c, *ptr; ptr = vptr; for (n = 1; n < maxlen; n++) { if ( (rc = my_read(fd, &c)) == 1) { *ptr++ = c; if (c == '\n') break; } else if (rc == 0) { *ptr = 0; return n - 1; } else return -1; } *ptr = 0; return n;}

wrap.h

#ifndef __WRAP_H_#define __WRAP_H_void perr_exit(const char *s);int Accept(int fd, struct sockaddr *sa, socklen_t *salenptr);int Bind(int fd, const struct sockaddr *sa, socklen_t salen);int Connect(int fd, const struct sockaddr *sa, socklen_t salen);int Listen(int fd, int backlog);int Socket(int family, int type, int protocol);ssize_t Read(int fd, void *ptr, size_t nbytes);ssize_t Write(int fd, const void *ptr, size_t nbytes);int Close(int fd);ssize_t Readn(int fd, void *vptr, size_t n);ssize_t Writen(int fd, const void *vptr, size_t n);ssize_t my_read(int fd, char *ptr);ssize_t Readline(int fd, void *vptr, size_t maxlen);#endif

###效果展示:

这里写图片描述

转载于:https://www.cnblogs.com/readlearn/p/10806449.html

你可能感兴趣的文章
KbmMW 4.50.00 测试版发布
查看>>
《驱蚊神器v1.0》android应用 赶走那些烦人的臭蚊子
查看>>
JAVA中,数组的操作与排序
查看>>
Linux 小知识翻译 - 「协议(protocol)」
查看>>
数据仓库与数据挖掘的一些基本概念
查看>>
tungsten
查看>>
Linux系统攻略 用UUID在Fstab中挂载分区
查看>>
如何将经纬度利用Google Map API显示C# VS2005 Sample Code
查看>>
Offer是否具有法律效力?
查看>>
EditText 限制输入,自定义样式,监听输入的字符,自动换行
查看>>
常用算法计算方式
查看>>
【LAMP】在Debian系linux下安装LAMP
查看>>
Xamarin.Android多界面
查看>>
[MySQL 5.6] MySQL 5.6 group commit 性能测试及内部实现流程
查看>>
[Android Pro] Android应用性能测试之CPU和内存占用(转载)
查看>>
Linux Shell 小脚本经典收藏
查看>>
Log4Net的使用
查看>>
金刚经的40句名言
查看>>
【BZOJ】1084: [SCOI2005]最大子矩阵(DP)
查看>>
一套后台管理html模版
查看>>