請先參考先前博文: Linux最小系統移植之早期打印CONFIG_DEBUG_LL , 由於eraly_printk其實就是對printch()封裝的html
/* linux-3.10.65/arch/arm/kernel/Makefile */ obj-$(CONFIG_DEBUG_LL) += debug.o obj-$(CONFIG_EARLY_PRINTK) += early_printk.o
先貼出early_printk.c源碼:linux
/* * linux/arch/arm/kernel/early_printk.c * * Copyright (C) 2009 Sascha Hauer <s.hauer@pengutronix.de> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include <linux/kernel.h> #include <linux/console.h> #include <linux/init.h> extern void printch(int); static void early_write(const char *s, unsigned n) { while (n-- > 0) { if (*s == '\n') printch('\r'); printch(*s); s++; } } static void early_console_write(struct console *con, const char *s, unsigned n) { early_write(s, n); } static struct console early_console_dev = { .name = "earlycon", .write = early_console_write, .flags = CON_PRINTBUFFER | CON_BOOT, .index = -1, }; static int __init setup_early_printk(char *buf) { early_console = &early_console_dev; register_console(&early_console_dev); return 0; } early_param("earlyprintk", setup_early_printk);
以及調用的地方:redis
/* linux-3.10.65/kernel/printk.c */ void early_vprintk(const char *fmt, va_list ap) { if (early_console) { char buf[512]; int n = vscnprintf(buf, sizeof(buf), fmt, ap); early_console->write(early_console, buf, n); } } asmlinkage void early_printk(const char *fmt, ...) { va_list ap; va_start(ap, fmt); early_vprintk(fmt, ap); va_end(ap); } #endif
1. 調用者使用early_printk()可變參數打印函數
2. 函數的參數是存放在棧中的,因爲是可變參數, 請問怎麼知道棧存放多少參數? --> 使用va_start()解析
其原理根據第一個參數fmt的地址,以及最後一個參數ap地址知道這塊棧區域都是形參, 記得變量ap要放在函數
第一行表示形參結束。同時,爲確保全部參數都是放置棧(GCC編譯器默認把前四個形參放置R0/1/2/3,後面才放入棧中)
定義了asmlinkage, 告知編譯器所有形參放置棧中
3. vscnprintf()做用就是解析%d,%x, int a等格式, 最終所有轉爲字符放置buf中, 再調用early_console->write()輸出
4. CON_PRINTBUFFER 打印緩衝全部數據, 若是後續串口驅動也設置這個標誌, printk會重複打印eraly_printk 打的,CON_BOOT表示在啓動期間有效
5. early_param("earlyprintk", setup_early_printk); 說明uboot或者DTB傳給kernle 的 cmdline要有這個字段才能生效
6. 因爲early_param是在start_kernel() -> parse_args() 的unknown_bootoption()處理的, 因此必須放置這個函數以後才能使用, 不然early_printk()就是空函數函數
7. 與printascii() printch()最大不一樣在於eraly_printk()能夠打印格式參數%d, %p等, 方便調試源碼分析
測試發現確實只有2打印出來, 1沒有測試