本文以ARM为例
一、功能说明
printk的log输出是由console实现(会在其他文章中说明)。由于在kernel刚启动的过程中,还没有为串口等设备等注册console(在device probe阶段实现),此时无法通过正常的console来输出log。
为此,linux提供了early console机制,用于实现为设备注册console之前的早期log的输出,对应console也称为boot console,简称bcon。这个console在kernel启动的早期阶段就会被注册,主要通过输出设备(比如串口设备)的简单的write方法直接进行数据打印。而这个write方法也就是平台实现。
注意,这时候作为输出的串口设备是基于bootloader中已经初始化完成的。
early console机制有两种实现方式,早期的early_printk实现和后面的earlycon实现。在这里主要说明early_printk的实现方式.
early_printk与earlycon相比较为落后,其差异可以参考《earlycon实现流程》文章,建议使用earlycon的实现方式来做early console功能
二、需要打开的宏,如何使能
1、需要打开的宏
ENTRY(printch)定义在arch/arm/debug.S中,需要用这个宏来打开。
setup_early_printk的定义。解析cmdline中的early_printk参数并安装boot console。
early_printk输出函数的定义。
2、如何使能
在cmdline中添加“earlyprintk”字符串,如下:
三、如何使用
1、printascii、printk、early_print、early_printk的区别。
2、在setup.c中加对应例子并打印
比如:
(1)printascii(不建议使用)
(2)early_printk
(3)printk(通过early_console(bcon)来进行输出的)
(4)early_print(不建议使用)
内部包含了printascii和printk
加一条early_print测试一下会不会打印两次
四、early console代码流程
由于early_printk和printk都是基于early console的基础上实现。所以先说明early console的流程。
1、解析cmdline中的“early_printk参数”
arch/arm/kernel/early_printk.c
early_param和__setup相似,在cmdline中解析到”earlyprintk”字符串时,调用setup_early_printk。
__setup与early_param不同的是,early_param 宏注册的内核选项必须要在其他内核选项之前被处理。在函数start_kernel中,parse_early_param处理early_param定义的参数,parse_args处理__setup定义的参数,这里不详细说明,只需要知道当从cmdline中查找到对应字符串时,对应的setup函数会被调用。
2、setup_early_printk
arch/arm/kernel/early_printk.c
设置了early_console的实现。
调用register_console向kernel注册一个console(具体细节在console篇中说明)。
3、early_console_dev实现
arch/arm/kernel/early_printk.c
其中write方法用于实现console输出的入口,也early console的核心。
CON_PRINTBUFFER标识,表示注册这个console的时候,需要把printk的buf中的log通过这个console进行输出。
CON_BOOT标识,表示这是一个boot console(bcon)。当启动过程了注册其他非boot console的时候,需要先卸载掉这个console。
4、early_console_write也就是要实现printk和early_printk的核心
arch/arm/kernel/early_printk.c
调用到printch函数中。
5、printch实现
arch/arm/kernel/debug.S
addruart_current宏定义如下:
调用到addruart,也就是真正做写数据动作的位置,这也是平台上要实现的东西,也是说移植的核心就是实现这个函数。
以s5pv210为例
arch/arm/include/debug/s5pv210.S
注意在这里,只是基于bootloader中对于uart已经初始化完成并且可以正常使用的基础上,直接往uart的tx寄存器中写入数据,从而实现串口输出的目的。
至此,有两种方法通过early_console来输出log。
(1)直接调用early_console->write。
early_console->write(early_console, buf, n);
early_printk函数就是通过这种方法实现。
(2)通过标准printk接口调用到console的write函数。
printk函数就是通过这种方法实现。
下面会详细说明。
五、early_printk软件流程
kernel/printk/printk.c
直接通过early_console->write来进行console的输出。也就是上述四的内容。
六、printk软件流程(当early_console作为console)
当四中的register_console(&early_console_dev);完成之后,console子系统中的console_drivers就存在了early_console_dev这个console(具体参考“console”的文章)。
经过printk的标准调用之后
在call_console_drivers调用如下:
early_console_dev作为当前console_drivers一个con,其write函数也会被调用。
early_console_dev->write(con, text, len);
也就是上述第四节的内容,可以实现输出的目的。
七、Q&A
1、为什么就算early printk功能没有打开,只要串口console被注册,启动早期的log也会被打印出来?
printk buffer的存在。
串口console中包含CON_PRINTBUFFER标识,可以打印出printk buffer中的log。具体可以看register_console的实现。
八、backup
1、printascii软件流程(废弃)
arch/arm/kernel/debug.S
ENTRY(printascii)
addruart_current r3, r1, r2
ENDPROC(printascii)
同样也是通过addruart_current来实现。
具体参考《四、5实现》
2、 early_print软件流程(同样不推荐使用)
arch/arm/kernel/setup.c
通过printascii和printk来实现打印。
我今天才知道,我之所以漂泊就是在向你靠近。
--《廊桥遗梦》
领取专属 10元无门槛券
私享最新 技术干货