本文以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、需要打开的宏
CONFIG_DEBUG_LL

ENTRY(printch)定义在arch/arm/debug.S中,需要用这个宏来打开。

CONFIG_EARLY_PRINTK

setup_early_printk的定义。解析cmdline中的early_printk参数并安装boot console。
early_printk输出函数的定义。

2、如何使能
在cmdline中添加“earlyprintk”字符串,如下:

"console=ttySAC0,115200n8 root=/dev/mmcblk0p1 rw rootwait ignore_loglevel earlyprintk"

三、如何使用
1、printascii、printk、early_print、early_printk的区别。
2、在setup.c中加对应例子并打印
比如:

printascii("early_printk success")
early_printk("early_printk success");
printk("printk success")
early_print("early_print success")

(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("earlyprintk", setup_early_printk);

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

static int __init setup_early_printk(char *buf)
{
    early_console = &early_console_dev;
    register_console(&early_console_dev);
    return 0;
}

设置了early_console的实现。
调用register_console向kernel注册一个console(具体细节在console篇中说明)。

3、early_console_dev实现
arch/arm/kernel/early_printk.c

static struct console early_console_dev = {
    .name =        "earlycon",
    .write =    early_console_write,
    .flags =    CON_PRINTBUFFER | CON_BOOT,
    .index =    -1,
};

其中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

static void early_console_write(struct console *con, const char *s, unsigned n)
{
    early_write(s, n);
}
static void early_write(const char *s, unsigned n)
{
    while (n-- > 0) {
        if (*s == '\n')
            printch('\r');
        printch(*s);
        s++;
    }
}

调用到printch函数中。

5、printch实现
arch/arm/kernel/debug.S

ENTRY(printch)
        addruart_current r3, r1, r2
        mov    r1, r0
        mov    r0, #0
        b    1b
ENDPROC(printch)

addruart_current宏定义如下:

.macro    addruart_current, rx, tmp1, tmp2
        addruart    \tmp1, \tmp2, \rx
        mrc        p15, 0, \rx, c1, c0
        tst        \rx, #1
        moveq        \rx, \tmp1
        movne        \rx, \tmp2
        .endm

调用到addruart,也就是真正做写数据动作的位置,这也是平台上要实现的东西,也是说移植的核心就是实现这个函数。
以s5pv210为例
arch/arm/include/debug/s5pv210.S

.macro addruart, rp, rv, tmp
        ldr    \rp, =S5PV210_PA_UART
        ldr    \rv, =S3C_VA_UART
#if CONFIG_DEBUG_S3C_UART != 0
        add    \rp, \rp, #(0x400 * CONFIG_DEBUG_S3C_UART)
        add    \rv, \rv, #(0x400 * CONFIG_DEBUG_S3C_UART)
#endif
    .endm

注意在这里,只是基于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

#ifdef CONFIG_EARLY_PRINTK
struct console *early_console;
asmlinkage __visible void early_printk(const char *fmt, ...)
{
    va_list ap;
    char buf[512];
    int n;
    if (!early_console)
        return;
    va_start(ap, fmt);
    n = vscnprintf(buf, sizeof(buf), fmt, ap);
    va_end(ap);
    early_console->write(early_console, buf, n);
}
#endif

直接通过early_console->write来进行console的输出。也就是上述四的内容。

六、printk软件流程(当early_console作为console)
当四中的register_console(&early_console_dev);完成之后,console子系统中的console_drivers就存在了early_console_dev这个console(具体参考“console”的文章)。
经过printk的标准调用之后

printk->vprintk->console_unlock->call_console_drivers

在call_console_drivers调用如下:

for_each_console(con) {
        con->write(con, text, len);
    }
#define for_each_console(con) \
        for (con = console_drivers; con != NULL; con = con->next)

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

void __init early_print(const char *str, ...)
{
    extern void printascii(const char *);
    char buf[256];
    va_list ap;
    va_start(ap, str);
    vsnprintf(buf, sizeof(buf), str, ap);
    va_end(ap);
#ifdef CONFIG_DEBUG_LL
    printascii(buf);
#endif
    printk("%s", buf);
}

通过printascii和printk来实现打印。
————————————————
版权声明:本文为CSDN博主「ooonebook」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/ooonebook/article/details/52654120

[console] early printk实现流程相关推荐

  1. linux的early printk的探讨

    http://mcuos.com/thread-8169-1-1.html (一)知识背景: [color=Red]Uncompressing Linux... done, booting the k ...

  2. linux console

    1.system.map映射表 参考之前写的一篇博客 点击打开链接 2.从内核第二阶段启动分析控制台console注册流程 路径: linux-3.10.101/init/main.c asmlink ...

  3. lk启动流程详细分析

    转载请注明来源:cuixiaolei的技术博客 这篇文章是lk启动流程分析(以高通为例),将会详细介绍下面的内容: 1).正常开机引导流程 2).recovery引导流程 3).fastboot引导流 ...

  4. JBoss 系列八十: jBPM 6 中使用 jbpm-console 创建执行 BPM 流程 - I

    2019独角兽企业重金招聘Python工程师标准>>> 本文通过如下步骤说明如何在 jBPM console 创建执行BPM流程. 安装jBPM6,添加用户kylin到jBPM 6用 ...

  5. JS_异步任务之流程控制

    需知: 1,JavaScript只有一个核心的主线程,但它有存放异步任务的任务队列(task queue). 2,主线程中是正在运行的同步任务(异步任务开始运行则也会变为同步任务),每次同步任务完成后 ...

  6. linux kernel下输入输出console如何实现

    转载地址:http://blog.csdn.net/skyflying2012/article/details/41078349 最近工作在调试usb虚拟串口,让其作为kernel启动的调试串口,以及 ...

  7. .NET CORE——Console中使用依赖注入

    我们都知道,在 ASP.NET CORE 中通过依赖注入的方式来使用服务十分的简单,而在 Console 中,其实也只是稍微绕了个小弯子而已.不管是内置 DI 组件或者第三方的 DI 组件(如Auto ...

  8. nodejs异步流程控制co 模块

    万恶的回调 对前端工程师来说,异步回调是再熟悉不过了,浏览器中的各种交互逻辑都是通过事件回调实现的,前端逻辑越来越复杂,导致回调函数越来越多,同时 nodejs 的流行也让 javascript 在后 ...

  9. printk在应用层的设置方式及读取内核打印信息的方法

    如果 printk 中没有加调试级别,则使用默认的调试级别.注意,调试级别和格式化字符串之间没有逗号.当前控制台的各打印级别可以通过下面的命令来查看. # cat /proc/sys/kernel/p ...

最新文章

  1. 堆栈溢出从入门到提高
  2. h5 与原生 app 交互的原理
  3. leetcode121买卖股票的最佳时机
  4. 基于heartbeatV2版本的ha-gui工具对httpd做高可用集群(1)
  5. python中两个集合的运算并交补_三种方式实现 Python 中的集合的交、并、补运算...
  6. HTML标签(持续更新)
  7. python groupby agg_Python数据分析:探索性分析
  8. 《深度学习》花书训练营day01-线性代数
  9. cad快速看图能合并图纸吗_怎样才能把2张CAD图纸合并
  10. keytool命令详解 自签名证书
  11. 控制欲强的人都是什么心理?
  12. python参数类型为uint8_Python 改变数组类型为uint8的实现
  13. Redis 哨兵模式安装方法
  14. Shell中的感叹号
  15. 华为PPPoE、PPP/MP、IP-Trunk配置
  16. 云主机试用,云主机最高14天试用
  17. 游戏开发中常用的算法
  18. GitHub上传教程,图文并茂
  19. c语言 程序段 数据段,C语言程序的段
  20. java如何计算吞吐量_如何计算进程调度算法的吞吐量(How to calculate throughput of a process scheduling algorithm)...

热门文章

  1. 读懂华为U8825Dupdater-script刷机脚本
  2. visio和preject冲突_Office 365 和 Visio Project Pro 2019 共存安装方法
  3. MATLAB编程实现实验数据的处理
  4. SAP BW-Function Module 数据源ABAP开发
  5. 虚幻引擎图文笔记:调整网格的光照贴图分辨率(Light Map Res)改善光照烘焙质量
  6. TestEngine with ID ‘junit-vintage‘ failed to discover tests
  7. 微信公众平台认证步骤详解及服务号和订阅号区别---之微信开发一
  8. 【转】史上最全的买卖股票技巧 总有一款适合你
  9. 工具综合症?资料收集狂?
  10. vs(c#)做table(表格)之GridView