19.epoll反应堆模型(libevet核心思想实现)

版权声明:guojawee https://blog.csdn.net/weixin_36750623/article/details/83511784

一. epoll的struct epoll_event结构体

自定义结构体

struct epoll_event{
     __unit32_t events;    // epoll事件类型:EPOLLET / EPOLLONESHOT
     epoll_data_t data;    // 存储用户数据
};
其中,epoll_data_t定义:
	typedef union epoll_data{
	    void* ptr;  //自定义的结构体(最常用)
	    int fd;     // 一般
	    uint32_t u32;
	    uint64_t u64;
	} epoll_data_t;

还记得每一个在红黑树上的文件描述符所对应的结构体epoll_event吗?
1.一般在epoll_event结构体中的联合体data上传入的是文件描述符fd本身
2.但是在epoll模型中,传入联合体的是一个自定义结构体指针,该结构体的基本结构至少包括:

struct my_events {  
    int        m_fd;                             //监听的文件描述符
    void       *m_arg;                           //泛型参数
    void       (*call_back)(void *arg);          //回调函数
    /*
     *  你可以在此处封装更多的数据内容
     *  例如用户缓冲区、节点状态、节点上树时间等等
     */
};
/*
 * 注意:用户需要自行开辟空间存放my_events类型的数组,并在每次上树前用epoll_data_t里的  
 *      ptr指向一个my_events元素。
 */

根据该模型,在程序中可以让所有的事件都拥有自己的回调函数,只需要使用ptr传入即可。示例代码:

while(1) {
   int n_ready = epoll_wait(ep_fd, events, MAX_EVENTS, 1000); /* 监听红黑树, 1秒没事件满足则返回0 */ 
   if (n_ready > 0) {
      for (i=0; i<n_ready; i++) 
         events[i].data.ptr->call_back(/* void *arg */); //调用回调函数
    }
    else
    /*  
     * 这里可以做很多很多其他的工作,例如定时清除没读完的不要的数据
     *     也可以做点和数据库有关的设置
     *     玩大点你在这里搞搞分布式的代码也可以
     */
}

二.epoll反应堆模型

1.传统的epoll服务器模型

监听可读事件 ⇒ 数据到来 ⇒ 触发事件 ⇒ epoll_wait()返回 ⇒ read消息 ⇒ write回射信息 ⇒ 继续epoll_wait() ⇒ 直到程序停止前都是这么循环

2.epoll反应堆服务器模型

监听可读事件(ET) ⇒ 数据到来 ⇒ 触发事件 ⇒ epoll_wait()返回 ⇒
read完数据(可读事件回调函数内) ⇒ 节点下树(可读事件回调函数内) ⇒ 设置可写事件和对应可写回调函数(可读事件回调函数内) ⇒ 节点上树(可读事件回调函数内) ⇒ 处理数据(可读事件回调函数内)
⇒ 监听可写事件(ET) ⇒ 对方可读 ⇒ 触发事件 ⇒ epoll_wait()返回 ⇒
写完数据(可写事件回调函数内) ⇒ 节点下树(可写事件回调函数内) ⇒ 设置可读事件和对应可读回调函数(可写读事件回调函数内) ⇒ 节点上树(可写事件回调函数内) ⇒ 处理收尾工作(可写事件回调函数内)
⇒ 直到程序停止前一直这么交替循环

3.为什么epoll反应堆模型要这样设计?

1.如此频繁的在红黑树上增添/删除节点是不是浪费CPU资源?

答:epoll反应堆模型中,对于同一个socket而言,完成收发信息至少占用两个树上的位置。而传统的epoll服务器模型中,完成收发信息只需要一个树上位置。任何一种设计方式都会浪费CPU资源,关键看浪费的值不值,此处的耗费能否换来更大的收益是决定是否浪费的标准。

2.为什么要可读以后设置可写?然后一直交替?

服务器read到数据后,并不一定能write成功,原因有二

2.1 滑动窗口机制

服务器read到客户端的数据后,假设刚好此时客户端的接收滑动窗口满,假设不进行可写事件设置,并且客户端是有意让自己的接受滑动窗口满**(黑客)**。那么,当前服务器将阻塞在send函数处,不能向客户端发送数据,导致服务器程序阻塞。

2.2 SIGPIPE信号

客户端send完数据后,突然由于异常停止,这将导致一个FIN发送给服务器。如果服务器不设置可写事件监听,那么服务器在read完数据后,直接向没有读端的套接字中写入数据,TCP协议栈将会给服务器发送RST分节+SIGPIPE信号,导致服务器进程终止。

猜你喜欢

转载自blog.csdn.net/weixin_36750623/article/details/83511784