docs/zh-cn/device_dev_guide/kernel/logging/printf.md

92 lines
4.2 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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)