文章目录
epoll描述
epoll是Linux下多路复用IO接口select/poll的增强版本,它能显著提高程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率,因为它会复用文件描述符集合来传递结果而不用迫使开发者每次等待事件之前都必须重新准备要被侦听的文件描述符集合,另一点原因就是获取事件的时候,它无须遍历整个被侦听的描述符集,只要遍历那些被内核IO事件异步唤醒而加入Ready队列的描述符集合就行了。
epoll突破单进程文件描述符1024的限制
对于描述符上限限制其实不由代码控制,通过修改配置完成
查看socket描述符允许的上限,这由硬件设备的配置决定
cat /proc/sys/fs/file-max
可以通陪修改配置文件
sudo vi /etc/security/limits.conf
在文件尾部写入以下配置,soft软限制,hard硬限制。如下图所示。
* soft nofile 65536
* hard nofile 100000
通过命令ulimit -a可以查看软限制参数。
epoll常用基础API接口
epoll的实质是将所有的监听文件描述符放到一颗红黑树上,监听事件只返回触发事件的文件描述符来进行处理
头文件为
#include <sys/epoll.h>
int epoll_create(int size)
其返回值的是一个红黑树的根节点句柄,size为监听的文件描述符个数,可以进行扩容。
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)
控制某个epoll监控的文件描述符上的事件:注册、修改、删除。
epfd: 为epoll_create返回的句柄。
op: 表示为执行的动作,用3个宏表述
EPOLL_CTL_ADD(注册新的fd到epfd,即加到红黑树上),
EPOLL_CTL_MOD(修改已经注册的fd的监听事件)
EPOLL_CTL_DEL(从epfd删除一个fd,即从红黑树上摘取下来)
fd: 即为要控制的文件描述符
struct epoll_event{
__uint32_t events; /*表示为监听事件的类型*/
epoll_data_t data; /使用者数据变量,是一个union/
};
1. 关于events常用的有以下几种宏定义:
EPOLLIN:表示监听对应描述符的读事件
EPOLLOUT:表示监听对应描述符的写事件
EPOLLERR:表示监听对应描述符的错误事件
EPOLLET:将EPOLL设为边缘触发(Edge Triggered)模式,这是相对于水平触发(Level Triggered)而言的,在高效并发中常常会用到。
/*以上四个是最常用的监听事件类型,后面几个了解下*/
EPOLLPRI:表示对于的文件描述符有紧急的数据可读(表示有带外来数据到来)
EPOLLHUP:表示对应的文件描述符被挂断
EPOLLONESHOT:只监听一次事件,当监听完成这次事件之后,如果还要继续监听这个socket话,需要重新吧这个socket放入树中
2. 关于epoll_data_t data重点了解下前两个;
typedef union epoll_data {
void *ptr; //用于epoll反应堆,之后再详细描述
int fd; //对应的描述符
uint32_t u32;
uint64_t u64;
} epoll_data_t;
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout)
等待所监控文件描述符上有事件的产生,类似于select()调用。
epfd: 为epoll_create返回的句柄。
events:用来存内核得到的事件的数组,由用户自己定义,规定大小
maxevents:告之内核这个events有多大,这个maxevents的值不能大于创建epoll_create()时的size
timeout: 是超时时间
-1: 阻塞
0: 立即返回,非阻塞
>0: 指定毫秒
返回值: 成功返回有多少文件描述符就绪,时间到时返回0,出错返回-1
利用epoll完成的简单server端
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#include <errno.h>
#include <ctype.h>
# define MAXLINE 8192
# define SERV_PORT 8000
# define OPEN_MAX 5000
#define INET_ADDRSTRLEN 16
int main(int argc,char *argv[])
{
int listenfd; //服务器的监听描述符
int i,connfd,sokcfd; //用于充当临时变量
int n;
int num = 0;/客服端连接个数/
ssize_t nready,efd,res;
char buf[MAXLINE],str[INET_ADDRSTRLEN];
socklen_t clilen;
struct sockaddr_in cliaddr,servaddr;
struct epoll_event tep; //单个描述符绑定的属性,用于epoll_ctl参数
struct epoll_event ep[OPEN_MAX]; //用于传出参数,为所有触发事件的描述符的集合,用于epoll_wait参数
listenfd = Socket(AF_INET, SOCK_STREAM, 0);
int opt = 1;
setsockopt(listenfd,SQL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt)); //端口复用
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(SERV_PORT);
bind(listenfd, (struct sockaddr *) &servaddr, sizeof(servaddr));
listen(listenfd, 20);
efd = epoll_create(OPEN_MAX); //创建epoll模型, efd指向红黑树根节点
if (efd == -1){
perr_exit