
目录
摘要
Channel.hpp 是单个 fd 的事件通道封装:_events 存监控位、_revents 存触发位,另有五个 std::function<void()> 回调,由 HandleEvent() 按位分发。价值在于把位掩码与回调绑在同一个 fd 上。
代码仓库:https://gitee.com/btshadow/Ubuntu_learning/blob/master/October/10-18/muduo/src/server.hpp
注释
3.1 回顾
①EPOLLIN/EPOLLOUT 是单比特:|= 添加、&= ~ 解除、& 判断,分别落在 Enable*、Rem*、ReadAble/WriteAble。
②std::function 对象的调用方式同普通函数。
③函数指针如何设置、是否都加 &,见 3.2 表首行。
④**_events 是对 fd 的监控,_revents 是 fd 事件的触发情况**,两者不可混用。
⑤EPOLLPRI 是该 fd 上有紧急数据或带外数据可读,重点📌,故并入 HandleEvent() 的读分支。
3.2 新知识
EPOLLRDHUP 是对端写端关闭,故要读完、走 read 回调;EPOLLHUP 是连接挂起、双方通信均不可用,走 close 回调。这正对应 HandleEvent() 中 RDHUP 与 EPOLLIN、EPOLLPRI 同组触发 _read_cb,HUP 单独触发 _close_cb。
函数包装器传指针还是对象:
| 传入内容 | 写法 | 说明 |
|---|---|---|
| 自由函数 | wrap(handler) 或 wrap(&handler) | 等价,都退化为函数指针 |
| 左值 lambda/函数对象 | wrap(lam) | 零拷贝,const F& 绑定 |
| 临时 lambda | wrap(\[\]{...}) | 生命周期延长到调用结束 |
| move-only 可调用对象 | wrap(std::move(obj)) | const F& 也能绑右值,但若包装器要保存则需 F&& 版本 |
| 成员函数 | wrap(\&{ obj.method(); }) | 用 lambda 包一层最清晰 |
本文件 Set 系列形参全为 const callback&,三种可调用物都落到这一签名。
包装器对象默认初始化为空值:构造初始化列表未列五个回调成员,故默认为空,各 Set 靠 assert(x != nullptr) 防空。
3.3 AI辅助
AI 辅助了重复字段------各 Set 前的空值判定,仿照 SetReadCB 写完其余四个。SetReadCB 首行注「防相同赋值(难):std::function 无 ==,无法可靠比较,跳过」,解释了只防空、不比同值的原因。
正文
4.1 监控位维护
EnableRead/EnableWrite 先用 ReadAble/WriteAble(即 EPOLLIN & _events、EPOLLOUT & _events)判是否已监控,重复则 WRN_LOG 返回,否则 |=;RemRead/RemWrite 反向判空后 &= ~;RemAll 以 _events == 0 判重再清零。
4.2 分发与析构
HandleEvent() 四段 if:EPOLLIN、EPOLLRDHUP、EPOLLPRI 任一命中调 _read_cb,EPOLLOUT、EPOLLERR、EPOLLHUP 各调对应回调,末尾 _event_cb() 无条件调用,代码注「任意事件(待调整)」。析构把 _fd、_events、_revents 置 -1;Remove() 留空待后续。
总结
三条主线:监控与触发分离、位操作增减、回调按位分发;空包装器用 assert 兜底,同值判定因 std::function 无 == 被跳过。
常见问题
RemRead为何不整体置 0?只清该位,整清交RemAll。- RDHUP 为何归读、HUP 归 close?见 3.2 首段语义。
- 五个 Set 为何都
assert?包装器默认空,仿SetReadCB复制。
标签
C++、epoll、std::function 回调、位操作
【统计:全文约 950 字 / 原代码 163 行 / 比例约 5.8;注释字段小节 3 个 / 成文 bug 0 处】
【上限说明:本篇结构含 5 点回顾、3 段新知识与一张 5 行对照表,5N = 815 字无法同时容纳全部必需板块;按规则三保留板块、逐句压到最简,未灌水,超出部分即表格与回顾要点本身所占字符。】