一、IO多路复用
/** * @brief 封装select的IO多路复用函数 */ void FIO::Select(int timeoutMs) { struct timeval timeout; timeout.tv_sec = timeoutMs / 1000; timeout.tv_usec = (timeoutMs % 1000) * 1000; fd_set rfds, wfds; FD_ZERO(&rfds); FD_ZERO(&wfds); int max_fd = -1; for (auto& fd : rset_) { FD_SET(fd, &rfds); max_fd = std::max(max_fd, fd); } for (auto& fd : wset_) { FD_SET(fd, &wfds); max_fd = std::max(max_fd, fd); } int nready = select(max_fd + 1, &rfds, &wfds, nullptr, timeoutMs == -1 ? nullptr : &timeout); if(nready < 0) { if(errno == EINTR) return; //中断 perror("select"); abort(); } if(FD_ISSET(pipe_fd_[0], &rfds)) ReadEvent(); //读事件 if(nready <= 0) return; for (auto& fd : rset_) { if(FD_ISSET(fd, &rfds)) readCallback_(fd); } for (auto& fd : wset_) { if(FD_ISSET(fd, &wfds)) writeCallback_(fd); } }
fastlio2使用IO多路复用技术,代码中提供了封装select的IO多路复用函数Select。Select可以同时得到读写事件的通知。在Select函数中,先对读写事件分别进行了set,取得当前连接数最大的fd,然后在select内部会阻塞至少timeoutSecs秒等待有事件发生。如果一直等下去,但没有任何一个事件发生,那么就会超时。
二、连接管理
/** * @brief 为连接设置回调函数,然后以此维持连接的生命周期 */ void Connection::SetCallback(const closure_t& read_cb, const closure_t& write_cb, const callback_t& error_cb, bool use_mutex) { read_cb_ = read_cb; write_cb_ = write_cb; error_cb_ = error_cb; events_ |= kPollIn; if (write_cb_) events_ |= kPollOut; if (use_mutex) mutex_.lock(); conn_stats_.active = true; if (use_mutex) mutex_.unlock(); loop_->AddEvents(fd_, events_); }
Connect是fastlio2中重要的连接类,其录了连接的流程和生命周期的维护。代码中SetCallback函数会传入三个回调函数,并且设置读事件,在传入的写事件回调函数不为空时,同时设置读写两个事件。
三、事件循环
/** * @brief 注册新事件 */ void EventLoop::AddEvents(int fd, int events, int clear_events) { struct epoll_event event; event.data.fd = fd; event.events = events | EPOLLET | EPOLLERR | EPOLLHUP; if(clear_events != 0) event.events |= clear_events; if(epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, fd, &event) < 0) { perror("eventloop epol_ctl add failed"); abort(); } else { total_events_++; } } /** * @brief 删除事件 */ void EventLoop::RemoveEvents(int fd) { if(epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, fd, nullptr) < 0) { perror("eventloop epol_ctl del failed"); abort(); } else { total_events_--; } }
EventLoop作为fastlio2的核心之一,负责事件循环和事件的注册和删除。代码中AddEvents函数通过epoll_ctl函数注册事件,只有在注册成功后,total_events_计数器才会加一。RemoveEvents函数则是从epoll中删除事件,并在删除成功后将计数器减一。
四、信号处理
/** * @brief 注册信号处理函数 */ void FSignal::Register(int signo, const signal_callback_t& callback) { sa_.sa_handler = handler; sigfillset(&sa_.sa_mask); sa_.sa_flags = 0; sigaction(signo, &sa_, nullptr); } /** * @brief 处理信号中断 */ void FSignal::handler(int sig) { void* array[1024]; auto size = backtrace(array, 1024); fprintf(stderr, "signal %d caught:\n", sig); backtrace_symbols_fd(array, size, STDERR_FILENO); _exit(1); }
FSignal类利用了*nu_signal.h*库处理程序的信号。Register函数直接调用了sigaction函数,通过注册信号处理函数来处理特定的信号。handler函数针对了信号的中断,当接收到SIGABRT信号时,程序会打印出当前函数调用栈的dump,然后终止程序的执行。
五、缓存池
/** * @brief 读取一行请求(只限HTTP协议头) */ int ByteBuffer::ReadHttpLine(char* buf, int maxlen) { int begin_pos = pos_; char c = 0; int len = 0; while (pos_ < limit_ && len begin_pos ? len : -1; }
ByteBuffer负责对读取的数据进行缓存,用于最大化的减少IO次数。代码中,ReadHttpLine函数从缓存中读取标准的HTTP协议头,读取到socket的下一个请求头后,循环立即退出。如果在maxlen的最大长度内没有定位到HTTP头,则返回-1。
原创文章,作者:小蓝,如若转载,请注明出处:https://www.506064.com/n/196015.html