背景
1.使用背景与整体架构
2.epoll如何使用
3.认识reactor(反应堆)模式
4.epoll+reactor联合
1.1 为什么会出现IO多路复用
相比较于传统简单的tcp服务器是基于线程做用户连接的,也就是新连接一个客户端(new fd)服务器端就会分配一个线程来服务该连接,但是系统资源是有限的(也就是线程资源是有限)导致客户数量连接是有限制的也就是所谓的C10k问题,那么这样的服务器是一中残次服务器。因此随着技术的发展引入了IO复用(EPOLL/SELECT/POLL
)。
1.2 IO多路复用
IO指的是网络IO,多路指的是多个TCP连接,复用指的是复用一个或者多个线程。IO多路复用是一种同步IO模型(image_1)也就是实现一个线程可以监视多个文件句柄。从下图中可以看出一旦数据准备好(也就是某个文件句柄就绪后),就能够通知应用程序进行相应的读写操作;没有文件就绪时就会阻塞应用程序,此时就会交出CPU。
2.1 EPOLL相关函数
int epoll_create(int size);
功能:创建一个epoll文件描述符(一个监听红黑树)
参数:size-创建红黑树的监听节点的数量(仅供内核参考)
返回值:成功返回新创建红黑树的根节点fd;失败返回-1,同时errno被设置。
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
功能:对红黑树进行添加、修改、删除操作。即对客户进行控制操作。
参数:
- int epfd
epoll_create函数的返回值(监听红黑树根节点的文件描述符) - int op
对该监听红黑树所做的操作:
EPOLL_CTL_ADD
添加fd到监听红黑树
EPOLL_CTL_MOD
修改fd在监听红黑树上的监听事件
EPOLL_CTL_DEL
将一个fd从监听红黑树上摘下【取消监听/删除一个监听时间】 - int fd
待监听的文件描述符 - struct epoll_event *event
event本质是一个struct epoll_event结构体指针
struct epoll_event {
uint32_t events; /* Epoll events /
epoll_data_t data; / 用户数据 */
};
这个结构体里的变量:
- uint32_t events:EPOLLIN/EPOLLOUT/EPOLLERR
- epoll_data_t data: 联合体epoll_data【这意味着,如果使用了ptr,就不能使用data!使用了data,就不能使用ptr】
typedef union epoll_data {
void *ptr;
int fd;
uint32_t u32;
uint64_t u64;
} epoll_data_t;
- void *ptr :利用这个指针,可以在epoll反应堆模型中回调函数【当使用了ptr这个指针后,fd参数就不管用了!因为是联合体!!】
- int fd 对应监听事件的fd
- uint32_t u32; 一般不用
- uint64_t u64; 一般不用
返回值
成功返回0,失败返回-1,并且设置errno
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
功能:等待间提供的所有的fd相应事件的产生。
参数:
- int epfd: epoll_create()函数返回的epoll实例的fd。
- struct epoll_event * events: 接口的返回参数,epoll把发生的事件的集合从内核复制到 events数组中。events数组是一个用户分配好大小的数组,数组长度大于等于maxevents。(events不可以是空指针,内核只负责把数据复制到这个 events数组中,不会去帮助我们在用户态中分配内存)。
- int maxevents: 表示本次可以返回的最大事件数目,通常maxevents参数与预分配的events数组的大小是相等的。
- int timeout: 表示在没有检测到事件发生时最多等待的时间,超时时间(>=0),单位是毫秒ms,-1表示阻塞,0表示不阻塞。
返回值: - 返回>0 表示满足监听的总个数,可用作循环上限。
- 返回=0表示没有 满足的监听事件
- 返回<0失败,并且设置errno