在启动过程中,start_kernel() (init/main.c) 在比较靠后的位置调用console_init()。
console_init()定义在kernel/printk/printk.c,它调用存储在.con_initcall.init section中的函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23  | /* * Initialize the console device. This is called *early*, so * we can't necessarily depend on lots of kernel help here. * Just do some early initializations, and do the complex setup * later. */void __init console_init(void){    initcall_t *call;    /* Setup the default TTY line discipline. */    n_tty_init();    /*     * set up the console device so that later boot sequences can     * inform about problems etc..     */    call = __con_initcall_start;                                                                                                                                                                                        while (call < __con_initcall_end) {        (*call)();        call++;    }    } | 
在树莓派3 Model B 64位内核,drivers/tty/serial/8250/8250_core.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21  | static struct console univ8250_console = {    .name       = "ttyS",    .write      = univ8250_console_write,    .device     = uart_console_device,    .setup      = univ8250_console_setup,    .match      = univ8250_console_match,    .flags      = CON_PRINTBUFFER | CON_ANYTIME,    .index      = -1,    .data       = &serial8250_reg,};static int __init univ8250_console_init(void)                                                                                                                                                                       {    if (nr_uarts == 0)        return -ENODEV;    serial8250_isa_init_ports();    register_console(&univ8250_console);    return 0;}console_initcall(univ8250_console_init); | 
register_console注册一个console。console_initcall定义个函数指针变量,存储在.con_initcall.init section中。
1 2 3  | #define console_initcall(fn)                    \                                                                                                                                                                       static initcall_t __initcall_##fn           \    __used __section(.con_initcall.init) = fn | 
注意,树莓派 64位内核,8250注册的不是CON_BOOT类型的console。在console_init()之前的printk将被缓存起来,不会打印,直到注册了console。这里的逻辑比较复杂,不追究了。