92 lines
4.2 KiB
Markdown
92 lines
4.2 KiB
Markdown
# printf 函数使用规范
|
||
|
||
\[ [English](../../../../en/device_dev_guide/kernel/logging/printf.md) | 简体中文 \]
|
||
|
||
## 一、概述
|
||
|
||
`printf` 函数主要用于开发和调试阶段,在需要与用户直接交互的命令行工具中向标准输出(stdout)打印格式化信息。
|
||
|
||
**核心原则**: 严禁在内核模块和后台服务中使用 `printf`。在此类场景下,应使用 `syslog` 作为标准日志记录方案。错误地使用 `printf` 会引发多核系统中的严重问题,例如服务阻塞、资源耗尽和日志混乱,对系统稳定性和实时性构成威胁。
|
||
|
||
## 二、适用场景:命令行交互
|
||
|
||
`printf` 的唯一推荐使用场景是开发需要直接与用户交互的命令行(CLI)应用程序。
|
||
|
||
- **功能**:向控制台(终端)实时输出程序状态、用户提示或调试信息。
|
||
- **示例**:一个需要用户输入参数并立即显示结果的工具。
|
||
|
||
## 三、禁用场景与核心风险分析
|
||
|
||
在以下场景中,必须禁止使用 `printf`:
|
||
|
||
- **内核模块(Kernel Modules)**
|
||
- **后台服务与守护进程(Background Services & Daemons)**
|
||
|
||
主要风险分析如下:
|
||
|
||
### 1、多核阻塞与 IPC 资源耗尽
|
||
|
||
在多核系统中,`printf` 的实现依赖于跨核的处理器间通信(IPC)。
|
||
|
||
- **工作机制**:当非主核调用 `printf` 时,系统会通过 `uart_rpmsg` IPC 机制将打印内容发送到主核,由主核上的 `cu` 等终端工具负责显示。
|
||
- **风险**:如果主核上的终端工具没有监听对应的非主核设备(例如,未执行 `cu -l /dev/ttyRBT`),IPC 消息会积压在缓冲区中,导致以下严重后果:
|
||
- **IPC 缓冲区耗尽**:由于消息无法被消费,IPC 缓冲区将迅速耗尽,导致新的 IPC 请求失败。
|
||
- **系统服务阻塞**:依赖 IPC 的其他关键系统服务(如心跳、数据同步)将因无法获取缓冲区而阻塞,最终可能导致系统挂起或崩溃。
|
||
- **日志混乱与截断**:与 `syslog` 混合使用时,`printf` 的同步阻塞特性会干扰 `syslog` 的异步日志流,导致日志条目被截断、内容交叉或完全丢失,使其不可读。
|
||
|
||
### 2、线程阻塞
|
||
|
||
`printf` 的底层实现涉及文件 I/O 操作,这是一个阻塞式调用。调用 `printf` 的线程将被挂起,直到数据完全写入输出缓冲区。对于有实时性要求的任务,这种不确定的阻塞时间是不可接受的。
|
||
|
||
### 3、中断上下文(ISR)中禁用
|
||
|
||
`printf` 不是可重入函数且可能引起阻塞,在中断服务程序(ISR)中调用会破坏系统实时性,甚至导致系统崩溃。
|
||
|
||
### 4、数据易失性
|
||
|
||
`printf` 的输出直接流向标准输出设备(如串口终端),并不会被持久化存储为日志文件。系统重启或会话关闭后,所有打印信息都会丢失。
|
||
|
||
## 四、最佳实践与替代方案
|
||
|
||
### 1、使用 `syslog` 进行日志记录
|
||
|
||
对于内核和服务,必须使用 `syslog` API 进行日志记录。`syslog` 具备以下优势:
|
||
|
||
- **异步非阻塞**:调用 `syslog` 不会阻塞当前线程。
|
||
- **统一管理**:支持日志等级(如 DEBUG, INFO, ERROR),易于过滤和管理。
|
||
- **持久化**:可配置将日志保存到文件或发送到远程服务器。
|
||
|
||
> **注意**:在 Linux 内核环境中,`printk` 函数的功能和定位类似于 `syslog`,用于向内核环形缓冲区输出日志,而非直接对应用户态的 `printf`。
|
||
|
||
### 2、确保跨平台兼容性
|
||
|
||
当您在适用场景(CLI 工具)中使用 `printf` 时,为确保代码在不同架构(如 32位/64位)平台间的可移植性,推荐使用 [<inttypes.h>](https://cplusplus.com/reference/cinttypes/) 头文件中定义的宏来格式化整型。
|
||
|
||
**示例:**
|
||
|
||
```C
|
||
#include <inttypes.h>
|
||
#include <stdint.h>
|
||
#include <stdio.h>
|
||
int main() {
|
||
uint64_t my_large_number = 12345678901234567890ULL;
|
||
|
||
// 使用 PRIu64 宏确保正确打印 uint64_t 类型
|
||
printf("My large number is: %" PRIu64 "\n", my_large_number);
|
||
return 0;
|
||
}
|
||
```
|
||
|
||
## 五、函数原型
|
||
|
||
```C
|
||
#include <stdio.h>
|
||
|
||
int printf(const char *format, ...);
|
||
```
|
||
|
||
## 六、相关文档
|
||
|
||
- [系统日志 (Syslog) 深度解析](./syslog.md)
|
||
- [日志管理与故障排查](./troubleshooting.md)
|