SylixOS里的打印【7】--- 内核日志信息打印接口printk

2023-11-03 06:08

本文主要是介绍SylixOS里的打印【7】--- 内核日志信息打印接口printk,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

概述

内核日志信息打印接口printk, 用于内核信息输出,可以在中断函数或者信号句柄中运行,不能在应用层中调用。

内核日志信息打印接口printk本质是API_LogPrintk函数的宏别名。

#ifndef printk
#define printk           API_LogPrintk
#endif

更多printk的详细信息可以查看SylixOS日志系统。

信息等级

printk输出具备信息等级,信息等级用来控制printk打印的这条信息是否在终端上显示的,当日志级别的数值小于控制台级别时,printk要打印的信息才会在控制台打印出来,否则不会显示在控制台!

信息等级通过在字符串开头添加“<n>”(n为0~7)这三个字符来设置,如果不设置则使用默认等级。也可以用宏来设置,可读性更强。

系统定义了8个内核信息等级,和POSIX 标准兼容。

#define KERN_EMERG      "<0>"                                   /* system is unusable                   */
#define KERN_ALERT      "<1>"                                   /* action must be taken immediately     */
#define KERN_CRIT       "<2>"                                   /* critical conditions                  */
#define KERN_ERR        "<3>"                                   /* error conditions                     */
#define KERN_WARNING    "<4>"                                   /* warning conditions                   */
#define KERN_NOTICE     "<5>"                                   /* normal but significant condition     */
#define KERN_INFO       "<6>"                                   /* informational                        */
#define KERN_DEBUG      "<7>"                                   /* debug-level messages                 */
  • KERN_EMERG:会导致主机系统不可用的情况;
  • KERN_ALERT:必须马上采取措施解决的问题;
  • KERN_CRIT:比较严重的情况;
  • KERN_ERR:运行出现错误;
  • KERN_WARNING:可能会影响系统功能的事件;
  • KERN_NOTICE:不会影响系统但值得注意;
  • KERN_INFO:一般信息;
  • KERN_DEBUG:程序或系统调试信息等。

日志等级从上到下依次变低,通常对于系统来说,如果发现等级KERN_EMERG的日志,则代表发生了严重的问题导致系统不可以再运行。等级KERN_DEBUG通常被用于一些调试信息的打印,在SylixOS驱动的开发中,经常使用等级KERN_ERR来打印一些错误信息,使用等级KERN_INFO来打印一些普通信息。

系统还定义了一些等级变量,如终端级别,printk默认级别, 让用户使用的最小级别,默认终端级别。

#define DEFAULT_MESSAGE_LOGLEVEL    4                                   /*  KERN_WARNING                */
#define MINIMUM_CONSOLE_LOGLEVEL    0                                   /*  让用户使用的最小级别        */
#define DEFAULT_CONSOLE_LOGLEVEL    7                                   /*  anything MORE serious than  *//*  KERN_DEBUG                  */
int     console_printk[4] = {DEFAULT_CONSOLE_LOGLEVEL,                                        /*  终端级别                    */DEFAULT_MESSAGE_LOGLEVEL,                                        /*  默认级别                    */MINIMUM_CONSOLE_LOGLEVEL,                                        /*  让用户使用的最小级别        */DEFAULT_CONSOLE_LOGLEVEL,                                        /*  默认终端级别                */
};#define console_loglevel            (console_printk[0])
#define default_message_loglevel    (console_printk[1])
#define minimum_console_loglevel    (console_printk[2])
#define default_console_loglevel    (console_printk[3])

系统初始化时(bspInit.c中)会将终端级别初始化为默认终端级别,代码如下:

console_loglevel = default_message_loglevel;

系统还提供一个loglevel命令,用于查看和设置终端级别。用法如下:
在这里插入图片描述

输出通道

在日志系统初始化完成前,printk也是通过bspDebugMsg函数输出的。使用 bspDebugMsg() 输出时, 需要将 \n 变为 \r\n 序列。

在日志系统初始化完成后,printk输出通道改为一个文件描述符的集合,会向文件集中的所有文件输出。这些文件可以是设备文件(如口/dev/ttyS0),普通文件,socket通道等各种有效文件类型。

系统初始化时向日志文件集添加的第一个文件就是内核标准输出文件(STD_OUT ),所以一般内核打印只能在内核终端看到,在进程终端(如Telnet)中是看不到的。

/*********************************************************************************************************
** 函数名称: halLogInit
** 功能描述: 初始化目标系统日志系统
** 输 入  : NONE
** 输 出  : NONE
*********************************************************************************************************/
#if LW_CFG_LOG_LIB_EN > 0static VOID  halLogInit (VOID)
{fd_set      fdLog;FD_ZERO(&fdLog);FD_SET(STD_OUT, &fdLog);API_LogFdSet(STD_OUT + 1, &fdLog);                                  /*  初始化日志                  */
}#endif   

系统提供logfiles命令用于查看日志文件集中有哪些文件。
在这里插入图片描述

实现原理

API_LogPrintk 函数先借助vsnprintf函数将可变参数格式化为固定字符串,将该字符串拷贝到一个定长的缓存中,然后通过输出接口将字符串输出。

在日志系统初始化完成前API_LogPrintk 使用的是静态缓存,并通过bspDebugMsg函数输出。
在日志系统初始化完成后API_LogPrintk 使用缓存池中动态分配出的缓存,并先发往消息队列,后由日志服务的线程再转发至日志文件集。

API_LogPrintk 的详细实现原理请查看SylixOS日志系统,这里只列了API_LogPrintk 函数的源码,里面对主要过程有详细注释。

/*********************************************************************************************************
** 函数名称: API_LogPrintk
** 功能描述: 记录格式化日志信息
** 输 入  : pcFormat                   格式化字串
**           ...                        变长字串
** 输 出  : 打印长度
*********************************************************************************************************/
INT  API_LogPrintk (CPCHAR   pcFormat, ...)
{static   CHAR          cBspMsgBuf[__MAX_MSG_LEN];                   /*  没有初始化之前暂时使用      *//*  线程不安全!                 */va_list       varlist;LW_LOG_MSG    logmsg;REGISTER INT           iRet;REGISTER PCHAR         pcBuffer;REGISTER ULONG         ulError;BOOL          bHaveLevel = LW_FALSE;BOOL          bBspMsg    = LW_FALSE;/** 获取打印缓存* 根据日志消息队列是否已初始化,选择使用静态数组或内存池分配空间* 总之使用一个固定大小的内存,LW_CFG_LOG_MSG_LEN_MAX一般为1024*/if (_G_hLogMsgHandle == LW_OBJECT_HANDLE_INVALID) {                 /*  log 还没有初始化            */pcBuffer = cBspMsgBuf;bBspMsg  = LW_TRUE;} else {pcBuffer = (PCHAR)__LOG_PRINTK_GET_BUFFER();if (pcBuffer == LW_NULL) {_ErrorHandle(ERROR_SYSTEM_LOW_MEMORY);return  (PX_ERROR);}}/** 获取打印等级* 先读取默认打印等级* 如果参数中设置了打印等级则解析并覆盖*/logmsg.LOGMSG_iLevel = default_message_loglevel;if (lib_strnlen(pcFormat, 3) >= 3) {if ((pcFormat[0] == '<') && (pcFormat[2] == '>')) {if ((pcFormat[1] <= '9') && (pcFormat[1] >= '0')) {logmsg.LOGMSG_iLevel = pcFormat[1] - '0';bHaveLevel = LW_TRUE;}}}/** 检查本次打印等级是否大于控制台级别* 当日志级别的数值小于控制台级别时,printk要打印的信息才会在控* 制台打印出来,否则不会显示在控制台!*/if (logmsg.LOGMSG_iLevel > console_loglevel) {                      /*  至少应该为 7                */if (bBspMsg == LW_FALSE) {__LOG_PRINTK_FREE_BUFFER(pcBuffer);}return  (ERROR_NONE);                                           /*  等级太低, 无法打印          */}/** 格式化输出到缓存中* 如果显示设置了打印等级,则跳过设置,即不输出打印等级字串* 通过vsnprintf函数解析打印格式* 超出__MAX_MSG_LEN长度的内容会被丢弃*/if (bHaveLevel) {va_start(varlist, pcFormat);iRet = vsnprintf(pcBuffer, __MAX_MSG_LEN, &pcFormat[3], varlist);va_end(varlist);} else {va_start(varlist, pcFormat);iRet = vsnprintf(pcBuffer, __MAX_MSG_LEN, pcFormat, varlist);va_end(varlist);}logmsg.LOGMSG_pcPrintk = pcBuffer;logmsg.LOGMSG_pcFormat = pcFormat;logmsg.LOGMSG_bIsNeedHeader = LW_FALSE;                             /*  不需要打印头部              */logmsg.LOGMSG_ulThreadId    = LW_OBJECT_HANDLE_INVALID;/** 输出日志信息* 根据日志消息队列是否已初始化,选择通过内核调试接口输出还是* 先发送至消息队列,后由日志服务的线程再转发至日志文件集* 消息队列发送操作可能存在失败,失败时打印输出丢弃*/if (bBspMsg) {                                                      /*  log 还没有初始化            *///通过内核调试接口输出__logBspMsg(pcBuffer);} else {//先发送至消息队列,后由日志服务的线程再转发至日志文件集ulError = API_MsgQueueSend(_G_hLogMsgHandle, &logmsg, sizeof(LW_LOG_MSG));if (ulError) {__LOG_PRINTK_FREE_BUFFER(pcBuffer);_G_iLogMsgsLost++;_DebugHandle(__ERRORMESSAGE_LEVEL, "log message lost.\r\n");_ErrorHandle(ERROR_LOG_LOST);return  (PX_ERROR);}}return  (iRet);
}

这篇关于SylixOS里的打印【7】--- 内核日志信息打印接口printk的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/336114

相关文章

C#实现系统信息监控与获取功能

《C#实现系统信息监控与获取功能》在C#开发的众多应用场景中,获取系统信息以及监控用户操作有着广泛的用途,比如在系统性能优化工具中,需要实时读取CPU、GPU资源信息,本文将详细介绍如何使用C#来实现... 目录前言一、C# 监控键盘1. 原理与实现思路2. 代码实现二、读取 CPU、GPU 资源信息1.

详解Java如何向http/https接口发出请求

《详解Java如何向http/https接口发出请求》这篇文章主要为大家详细介绍了Java如何实现向http/https接口发出请求,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 用Java发送web请求所用到的包都在java.net下,在具体使用时可以用如下代码,你可以把它封装成一

在C#中获取端口号与系统信息的高效实践

《在C#中获取端口号与系统信息的高效实践》在现代软件开发中,尤其是系统管理、运维、监控和性能优化等场景中,了解计算机硬件和网络的状态至关重要,C#作为一种广泛应用的编程语言,提供了丰富的API来帮助开... 目录引言1. 获取端口号信息1.1 获取活动的 TCP 和 UDP 连接说明:应用场景:2. 获取硬

SpringBoot使用Apache Tika检测敏感信息

《SpringBoot使用ApacheTika检测敏感信息》ApacheTika是一个功能强大的内容分析工具,它能够从多种文件格式中提取文本、元数据以及其他结构化信息,下面我们来看看如何使用Ap... 目录Tika 主要特性1. 多格式支持2. 自动文件类型检测3. 文本和元数据提取4. 支持 OCR(光学

C#实现获取电脑中的端口号和硬件信息

《C#实现获取电脑中的端口号和硬件信息》这篇文章主要为大家详细介绍了C#实现获取电脑中的端口号和硬件信息的相关方法,文中的示例代码讲解详细,有需要的小伙伴可以参考一下... 我们经常在使用一个串口软件的时候,发现软件中的端口号并不是普通的COM1,而是带有硬件信息的。那么如果我们使用C#编写软件时候,如

Java后端接口中提取请求头中的Cookie和Token的方法

《Java后端接口中提取请求头中的Cookie和Token的方法》在现代Web开发中,HTTP请求头(Header)是客户端与服务器之间传递信息的重要方式之一,本文将详细介绍如何在Java后端(以Sp... 目录引言1. 背景1.1 什么是 HTTP 请求头?1.2 为什么需要提取请求头?2. 使用 Spr

C++中实现调试日志输出

《C++中实现调试日志输出》在C++编程中,调试日志对于定位问题和优化代码至关重要,本文将介绍几种常用的调试日志输出方法,并教你如何在日志中添加时间戳,希望对大家有所帮助... 目录1. 使用 #ifdef _DEBUG 宏2. 加入时间戳:精确到毫秒3.Windows 和 MFC 中的调试日志方法MFC

SpringBoot如何使用TraceId日志链路追踪

《SpringBoot如何使用TraceId日志链路追踪》文章介绍了如何使用TraceId进行日志链路追踪,通过在日志中添加TraceId关键字,可以将同一次业务调用链上的日志串起来,本文通过实例代码... 目录项目场景:实现步骤1、pom.XML 依赖2、整合logback,打印日志,logback-sp

通过C#获取PDF中指定文本或所有文本的字体信息

《通过C#获取PDF中指定文本或所有文本的字体信息》在设计和出版行业中,字体的选择和使用对最终作品的质量有着重要影响,然而,有时我们可能会遇到包含未知字体的PDF文件,这使得我们无法准确地复制或修改文... 目录引言C# 获取PDF中指定文本的字体信息C# 获取PDF文档中用到的所有字体信息引言在设计和出

C#读取本地网络配置信息全攻略分享

《C#读取本地网络配置信息全攻略分享》在当今数字化时代,网络已深度融入我们生活与工作的方方面面,对于软件开发而言,掌握本地计算机的网络配置信息显得尤为关键,而在C#编程的世界里,我们又该如何巧妙地读取... 目录一、引言二、C# 读取本地网络配置信息的基础准备2.1 引入关键命名空间2.2 理解核心类与方法