printk执行过程

  • 参考代码

    • Linux 4.9.88

      kernel/printk.c
      include/linux/kernel.h
      kernel/printk/internal.h
      
    • Linux 5.4

      kernel/printk.c
      include/linux/kernel.h
      kernel/printk/printk_safe.c
      

1. printk的使用

1.1 printk使用示例

调试内核、驱动的最简单方法,是使用printk函数打印信息。

printk函数与用户空间的printf函数格式完全相同,它所打印的字符串头部可以加入“\001n”样式的字符。

其中n为0~7,表示这条信息的记录级别,n数值越小级别越高。

注意:linux 2.x内核里,打印级别是用""来表示。

在驱动程序中,可以这样使用printk:

printk("This is an example\n");
printk("\0014This is an example\n");
printk("\0014""This is an example\n");
printk(KERN_WARNING"This is an example\n");

在上述例子中:

  • 第一条语句没有明确表明打印级别,它被处理前内核会在前面添加默认的打印级别:“<4>”

  • KERN_WARNING是一个宏,它也表示打印级别:

#define KERN_SOH    "\001"        /* ASCII Start Of Header */
#define KERN_WARNING    KERN_SOH"4"    /* warning conditions */

现在我们知道了,内核的每条打印信息都有自己的级别,当自己的级别在数值上小于某个阈值时,内核才会打印该信息。

1.2 printk函数的记录级别

在内核代码include/linux/kernel.h中,下面几个宏确定了printk函数怎么处理打印级别:

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

举例说明这几个宏的含义:

① 对于printk(“……”),只有n小于console_loglevel时,这个信息才会被打印。

② 假设default_message_loglevel的值等于4,如果printk的参数开头没有“”样式的字符,则在printk函数中进一步处理前会自动加上“<4>”;

③ minimum_console_logleve是一个预设值,平时不起作用。通过其他工具来设置console_loglevel的值时,这个值不能小于minimum_console_logleve。

④ default_console_loglevel也是一个预设值,平时不起作用。它表示设置console_loglevel时的默认值,通过其他工具来设置console_loglevel的值时,用到这个值。

上面代码中,console_printk是一个数组,它在kernel/printk.c中定义:

/* 数组里的宏在include/linux/printk.h中定义*/
int console_printk[4] = {CONSOLE_LOGLEVEL_DEFAULT, /* console_loglevel */MESSAGE_LOGLEVEL_DEFAULT,   /* default_message_loglevel */CONSOLE_LOGLEVEL_MIN,       /* minimum_console_loglevel */CONSOLE_LOGLEVEL_DEFAULT,   /* default_console_loglevel */
};/* Linux 4.9.88 include/linux/printk.h */
#define CONSOLE_LOGLEVEL_DEFAULT 7/* anything MORE serious than KERN_DEBUG */
#define MESSAGE_LOGLEVEL_DEFAULT CONFIG_MESSAGE_LOGLEVEL_DEFAULT
#define CONSOLE_LOGLEVEL_MIN     1/* Minimum loglevel we let people use *//* Linux 5.4 include/linux/printk.h */
#define CONSOLE_LOGLEVEL_DEFAULT CONFIG_CONSOLE_LOGLEVEL_DEFAULT
#define MESSAGE_LOGLEVEL_DEFAULT CONFIG_MESSAGE_LOGLEVEL_DEFAULT
#define CONSOLE_LOGLEVEL_MIN     1/* Minimum loglevel we let people use */

1.3 在用户空间修改printk函数的记录级别

挂接proc文件系统后,读取/proc/sys/kernel/printk文件可以得知console_loglevel、default_message_loglevel、minimum_console_loglevel和default_console_loglevel这4个值。

比如执行以下命令,它的结果“7 4 1 7”表示这4个值:

也可以直接修改/proc/sys/kernel/printk文件来改变这4个值,比如:

# echo "1 4 1 7" > /proc/sys/kernel/printk

这使得console_loglevel被改为1,于是所有的printk信息都不会被打印。

1.4 printk函数记录级别的名称及使用

在内核代码include/linux/kernel.h中,有如下代码,它们表示0~7这8个记录级别的名称:

#define KERN_SOH    "\001"        /* ASCII Start Of Header */
#define KERN_SOH_ASCII  '\001'#define KERN_EMERG KERN_SOH"0"    /* system is unusable */
#define KERN_ALERT  KERN_SOH"1"    /* action must be taken immediately */
#define KERN_CRIT   KERN_SOH"2"    /* critical conditions */
#define KERN_ERR    KERN_SOH"3"    /* error conditions */
#define KERN_WARNING    KERN_SOH"4"    /* warning conditions */
#define KERN_NOTICE KERN_SOH"5"    /* normal but significant condition */
#define KERN_INFO   KERN_SOH"6"    /* informational */
#define KERN_DEBUG  KERN_SOH"7"    /* debug-level messages */

在使用printk函数时,可以这样使用记录级别;

printk(KERN_WARNING”there is a warning here!\n”)

2. printk执行过程

2.1 函数调用过程

在嵌入式Linux开发中,printk信息常常从串口输出,这时串口被称为串口控制台。从内核kernel/printk.c的printk函数开始,往下查看它的调用关系,可以知道printk函数是如何与具体设备的输出函数挂钩的。

printk函数调用的子函数的主要脉落如下:

printk// linux 4.9: kernel/printk/internal.h// linux 5.4: kernel/printk/printk_safe.cvprintk_func vprintk_default(fmt, args);vprintk_emitvprintk_store // 把要打印的信息保存在log_buf中log_outputpreempt_disable();if (console_trylock_spinning())console_unlock();preempt_enable();console_unlockfor (;;) {msg = log_from_idx(console_idx);if (suppress_message_printing(msg->level)) {/* 如果消息的级别数值大于console_loglevel, 则不打印此信息 */}printk_safe_enter_irqsave(flags);call_console_drivers(ext_text, ext_len, text, len);printk_safe_exit_irqrestore(flags);}

call_console_drivers函数调用驱动程序打印信息,此函数在kernel\printk\printk.c中,代码如下:

static void call_console_drivers(const char *ext_text, size_t ext_len,const char *text, size_t len)
{struct console *con;trace_console_rcuidle(text, len);if (!console_drivers)return;for_each_console(con) {if (exclusive_console && con != exclusive_console)continue;if (!(con->flags & CON_ENABLED))continue;if (!con->write)continue;if (!cpu_online(smp_processor_id()) &&!(con->flags & CON_ANYTIME))continue;if (con->flags & CON_EXTENDED)con->write(con, ext_text, ext_len);elsecon->write(con, text, len);}
}

2.2 内核打印信息保存在哪

我们执行dmesg命令可以打印以前的内核信息,所以这些信息必定是保存在内核buffer中。

kernel\printk\printk.c中,定义有一个全局buffer:

/* record buffer */
#define LOG_ALIGN __alignof__(struct printk_log)
#define __LOG_BUF_LEN (1 << CONFIG_LOG_BUF_SHIFT)
static char __log_buf[__LOG_BUF_LEN] __aligned(LOG_ALIGN);
static char *log_buf = __log_buf;
static u32 log_buf_len = __LOG_BUF_LEN;

执行dmesg命令时,它就是访问虚拟文件/proc/kmsg,把log_buf中的信息打印出来。

2.3 printk信息从哪些设备打印出来?

在内核的启动信息中,有类似这样的命令行参数:

/* IMX6ULL */
[root@100ask:~]# cat /proc/cmdline
console=ttymxc0,115200 root=/dev/mmcblk1p2 rootwait rw/* STM32MP157 */
[root@100ask:~]# cat /proc/cmdline
root=PARTUUID=491f6117-415d-4f53-88c9-6e0de54deac6 rootwait rw console=ttySTM0,115200

在命令行参数中,“console=ttymxc0”、"console=ttySTM0"就是用来选择printk设备的。

可以指定多个"console="参数,表示从多个设备打印信息。

命令行信息来自哪里?

  • 设备树

    / {chosen {bootargs = "console=ttymxc1,115200";};
    };
    
  • UBOOT根据环境参数修改设备树:IMX6ULL

    /* 进入IMX6ULL的UBOOT */
    => print mmcargs
    mmcargs=setenv bootargs console=${console},${baudrate} root=${mmcroot}
    => print console
    console=ttymxc0
    => print baudrate
    baudrate=115200
    
  • UBOOT从启动文件修改设备树:STM32MP157

    [root@100ask:~]# mount /dev/mmcblk2p2 /boot
    [root@100ask:~]# cd /boot
    [root@100ask:/boot]# cat mmc0_extlinux/stm32mp157c-100ask-512d-v1_extlinux.conf
    # Generic Distro Configuration file generated by OpenEmbedded
    menu title Select the boot mode
    MENU BACKGROUND /splash.bmp
    TIMEOUT 20
    DEFAULT 100ask-lcd
    LABEL 100ask-coreKERNEL /uImageFDT /stm32mp157c-100ask-512d-v1.dtbINITRD /uInitrdAPPEND root=/dev/mmcblk1p5  rootwait rw console=ttySTM0,115200
    LABEL 100ask-hdmiKERNEL /uImageFDT /stm32mp157c-100ask-512d-hdmi-v1.dtbINITRD /uInitrdAPPEND root=/dev/mmcblk1p5  rootwait rw console=ttySTM0,115200
    LABEL 100ask-lcdKERNEL /uImageFDT /stm32mp157c-100ask-512d-lcd-v1.dtbINITRD /uInitrdAPPEND root=/dev/mmcblk1p5  rootwait rw console=ttySTM0,115200
    

printk的执行过程相关推荐

  1. SQL执行过程中的性能负载点

    一.SQL执行过程 1.用户连接数据库,执行SQL语句: 2.先在内存进行内存读,找到了所需数据就直接交给用户工作空间: 3.内存读失败,也就说在内存中没找到支持SQL所需数据,就进行物理读,也就是到 ...

  2. python 二进制流转图片_Python零基础入门到精通-5.1节:Python程序的执行过程

    教程引言: 系统地讲解计算机基础知识,Python的基础知识, 高级知识,web开发框架,爬虫开发,数据结构与算法,nginx, 系统架构.一步步地帮助你从入门到就业. 5.1.1 在命令行中执行Py ...

  3. JSP的执行过程(详解)

    要了解JSP的执行过程,首要要搞懂什么是JSP,JSP的全称是Java Server Pages,里面包含html标签.css样式.JavaScript脚本和Java代码. 下面我们来说说JSP的执行 ...

  4. oracle 与 client端执行结果不一致_不同模式下Spark应用的执行过程

    根据应用执行的3个阶段,不同执行模式下各个阶段的执行逻辑不相同,本文分析不同模式下的执行逻辑. Yarn-Client模式的执行流程 Yarn的组成 Yarn是hadoop自带的资源管理框架,它的设计 ...

  5. Sql Server 因为触发器问题导致数据库更新报错“在触发器执行过程中引发了错误,批处理已中止”的问题处理...

    在维护一个非常旧的项目时,由于该项目版本已经非常老了,而且在客户现场运行的非常稳定,更要命的是本人目前没有找到该项目的代码,为了处理一个新的需求而且还不能修改程序代码,于是决定从数据库入手,毕竟该项目 ...

  6. saiku执行过程代码跟踪

    使用了很久的saiku,决定跟踪一下代码,看看它的执行核心过程: 一.入口controller代码 1.1.页面打开之后,会发送一个ajax请求 Request URL: http://l-tdata ...

  7. mysql查询解析过程_MySQL查询执行过程详解

    查询是用户通过设置某些查询条件,从表或其他查询中选取全部或者部分数据,以表的形式显示数据供用户浏览.查询是一个独立的.功能强大的.具有计算功能和条件检索功能的数据库对象.MySQL数据库中,MySQL ...

  8. servlet必知细节(二)--servlet执行过程

    servlet必知细节(二)--servlet执行过程 我们知道,servlet没有main函数,那么,servlet是怎么调用的呢? 实际上,servlet 是由tomcat调用的,tomcat调用 ...

  9. Hadoop学习之Mapreduce执行过程详解

    一.MapReduce执行过程 MapReduce运行时,首先通过Map读取HDFS中的数据,然后经过拆分,将每个文件中的每行数据分拆成键值对,最后输出作为Reduce的输入,大体执行流程如下图所示: ...

最新文章

  1. mysql以及mysql bench安装教程
  2. 实战Registry和RegistryKey类,一个简单的可疑文件扫描程序
  3. android webview java_Android Webview中调用本地java方法
  4. go chapter 8 - 初始化对象
  5. oracle伪客户端的安装(oracle不安装客户端)
  6. CCF CTO Club 官宣:活动Logo诞生啦!
  7. distributed processing(分布式处理)
  8. 【虚拟化】Dockerfile构建JDK镜像
  9. eclipse连接mysql_专题一、flask构建mysql数据库正确姿势
  10. Mybatis多条件直接查询Param注解版
  11. getline函数(精华版)
  12. 3 微信开发本地代理环境的搭建--实现将内网ip映射到外网
  13. 请熟悉ECO开发的朋友解答我的一些小问题!
  14. 解码jpg图片c语言,图像解码之一——使用libjpeg解码jpeg图片
  15. python登录系统账号检测_使用Python脚本检测邮件账户密码是否被泄漏,提高你的账户安全性...
  16. So Who's Counting? by Erin McHugh and Emily Luchetti
  17. 想进大公司先测你EQ
  18. 【Vite】1380- 详解 Vite 依赖预构建流程
  19. 微信开放平台-第三方平台-全网发布接入【java版本】
  20. Linux Oracle install studing

热门文章

  1. 站在两个世界的边缘—世间慨1
  2. Java中如何导入DW当中_用Dreamweaver插入Java特效方法
  3. 【效果展示】面部追踪+情绪识别
  4. Python去除字符串中的非数字、非字母
  5. 建议收藏!Axure中用例事件与用例动作的中英文对照表
  6. 怒之铁拳重置Linux版,西班牙《怒之铁拳重制版V5.0》~~~~~~~~~~115+RF盘分享
  7. 通信工程的岗位有哪些?
  8. BUUCTF闯关日记--[MRCTF2020]你传你呢(超详解)
  9. 程序设计【Week9】作业
  10. php三维数组定义,PHP数组之三维数组