POLLHUP vs POLLNVAL vs EPOLLHUP

POLLNVAL

POLLNVALis equivalent to EBADF: it means the file descriptor does not actually refer to any open file, i.e. it was closed or never open to begin with. This can never happen except as a result of a programming error or intentional attempt to query whether a file descriptor is invalid. External conditions, such as a peer closing its end of a network socket or pipe, can never close your file descriptor to your end of the socket or pipe. If it could, this would lead to massive vulnerabilities in basically any program using sockets/pipes/etc.
也就是说当文件描述符没有打开,或者实际上并未引用任何打开的文件,会有这个事件.
除非由于编程错误或故意尝试查询文件描述符是否无效,否则这种情况永远不会发生。

POLLHUP

相反,是说你这个fd是有效的, 但是处于这样一个状态:
A device has been disconnected, or a pipe or FIFO has been closed by the last process that had it open for writing. Once set, the hangup state of a FIFO shall persist until some process opens the FIFO for writing

这段代码是 `tplogd_output_console_add_entry` 函数的实现,它是日志系统中用于 **将日志条目输出到控制台(console)** 的核心函数。该函数不仅负责日志的格式化输出,还具备 **容错与恢复机制**,确保在控制台输出失败时能尝试恢复输出能力。 --- ### 🔍 **函数功能概述** ```c static int tplogd_output_console_add_entry( struct tplogd_output_class *co, struct tplog_entry_hdr *hdr, const char *datav[], int lenv[]) ``` - **作用**:将一条日志条目格式化后输出到控制台。 - **参数说明**: - `co`:输出类结构体,包含输出配置和私有数据。 - `hdr`:日志条目的头部信息。 - `datav[]`:日志数据的三个部分(如时间戳、模块名、消息)。 - `lenv[]`:每个数据部分的长度(可能未被使用)。 --- ### 🧩 **逐行分析** ```c struct tplogd_output_console *con = co->priv; struct pollfd pfd; int recovery; int ret; ``` - 定义局部变量: - `con`:指向控制台输出结构体。 - `pfd`:用于 `poll()` 系统调用。 - `recovery`:是否需要进行恢复操作。 - `ret`:用于保存函数返回值。 --- ```c if (!con->is_default) { ``` - 如果是自定义控制台(非默认控制台),则进入输出前的可用性检测。 --- ```c recovery = 0; pfd.fd = con->fd; pfd.events = POLLOUT; ret = poll(&pfd, 1, 5 * 1000); ``` - 使用 `poll()` 检查文件描述符是否可写,超时时间为 5 秒。 - `POLLOUT` 表示可写状态。 --- ```c if (ret == -1) { if (errno == EINTR) { return -EINTR; } /* recovery by other error */ recovery = 1; } else { if (pfd.revents & (POLLERR | POLLHUP | POLLNVAL)) { /* recovery by fd error */ recovery = 1; } else { if (ret == 0) { con->timeout_weight += 1; if (con->timeout_weight >= 2) { /* recovery by wait more than 5 seconds two times */ recovery = 1; } else { ret = poll(&pfd, 1, 15 * 1000); if (ret != 1 || (pfd.revents & (POLLERR | POLLHUP | POLLNVAL))) { /* recovery by wait more than 20 seconds */ recovery = 1; } } } else { /* ok to write */ con->timeout_weight = 0; } } } ``` - 如果 `poll()` 返回错误或超时,标记需要恢复。 - 如果连续两次超时超过 5 秒,则触发恢复。 - 如果再次等待 15 秒后仍然失败,也触发恢复。 --- ```c if (recovery) { tplogd_output_console_set_enable(co, 0); tplogd_output_console_set_enable(co, 1); } ``` - 如果需要恢复,先禁用再启用控制台输出,尝试重新打开设备或文件。 --- ```c again: con = co->priv; if (!con) { return -1; } ``` - 使用 `goto again` 实现重试机制。 - 检查控制台是否仍有效。 --- ```c if (co->core->cfg.console_line_spacing > 0) { fputc('\n', con->f); } ``` - 如果配置了行间距,输出一个换行符。 --- ```c ret = tplog_entry_print(con->f, hdr, datav[0], datav[1], datav[2], con->highlight, co->core->cfg.console_verbose); ``` - 调用 `tplog_entry_print` 函数将日志格式化输出到 `FILE *`。 --- ```c if (ret < 0 && !con->is_default) { /* recovery by printf failed */ tplogd_output_console_set_enable(co, 0); tplogd_output_console_set_enable(co, 1); if (co->enabled) { goto again; } } ``` - 如果格式化输出失败且不是默认控制台,尝试恢复并重试。 --- ```c return ret; ``` - 返回输出结果。 --- ### ✅ **关键设计思想** | 技术点 | 说明 | |--------|------| | 控制台输出 | 支持向终端、文件或串口输出日志 | | 输出前检查 | 使用 `poll()` 检查设备是否可写 | | 容错机制 | 支持自动恢复失败的输出通道 | | 重试机制 | 使用 `goto` 实现日志重发 | | 配置支持 | 支持行间距、高亮、详细输出等 | --- ### 🧪 **应用场景** 该函数适用于以下日志输出场景: - 嵌入式系统调试日志输出到串口 - 服务器日志输出到终端或文件 - 需要容错的日志系统 --- ### 📦 **扩展建议** - 支持日志级别过滤 - 支持日志颜色配置(ANSI) - 支持异步写入(如使用队列) - 支持日志加密或签名 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值