写tcp并发服务器的时候,并发的实现方式:
第一种: 阻塞等待
来了一个客户端就创建一个进程或线程去服务器,但是创建的线程或进程大多数时间都是处于休眠状态,所以这种并发的方式比较浪费资源
缺点: 浪费资源(内存)
第二种:非阻塞忙轮询
accept和read都不带阻塞,进程在不停的轮询,如果客户端没有发来消息,也没有新的客户端请求连接,这个时候程序在做无用功,浪费cpu的资源
第三种: 多路IO转接(多路IO复用)
epoll是多路IO转接技术的一种,可以解决select的缺点
epoll没有文件描述符的限制
epoll每次重新监听不需要将文件描述符从用户态拷贝至内核态
epoll返回的是已经变化的文件描述符,不需要在对这颗树遍历了
大量并发少了活跃效率高
epoll的工作流程3步:
1创建树
#include <sys/epoll.h>
int epoll_create(int size);
参数: size > 0即可(树上的 节点最大值,>0自动适配)
返回值: 树的句柄
2将lfd上树
#include <sys/epoll.h>
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
功能: 操作树上的节点 -节点上树 节点下树 修改节点
参数:
epfd: 树的句柄
op:
EPOLL_CTL_ADD: 上树
EPOLL_CTL_DEL : 下树
EPOLL_CTL_MOD: 修改
fd: 修改的文件描述符
event: 节点的地址
返回值:成功返回0 失败返回-1
typedef union epoll_data {
void *ptr;
int fd;
uint32_t u32;
uint64_t u64;
} epoll_data_t;
struct epoll_event {
uint32_t events; /* 读事件 EPOLLIN 写事件 EPOLLOUT */
epoll_data_t data; /* User data variable */
};
3监听
#include <sys/epoll.h>
int epoll_wait(int epfd, struct epoll_event *events,
int maxevents, int timeout);
功能: 循环监听树上的节点变化
参数:
epfd: 树的句柄
events: struct epoll_event数组的首元素地址,数组用来接收epoll_wait返回的节点
maxevents:events指向数组的元素个数
timeout:
-1 永久等待
0 : 不等待
0 : 限时等待
返回值: 返回变化的文件描述符个数