在启动过程中,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。这里的逻辑比较复杂,不追究了。