linux kernel在start_kernel里面调用console_init执行控制台的初始化。使用printk执行打印。
但是在console_init之前有一部分printk。需要在console_init之后才能打印出来,也就是说之前prinkt的数据都保存在了一个缓冲区内,等到console_init以后才打印出来。
console_init的执行流程在/drivers/tty的tty_io.c里面
void __init console_init(void)
{
initcall_t *call;
/* Setup the default TTY line discipline. */
tty_ldisc_begin();
/*
* 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++;
}
}
tty_ldisc_begin()暂时先不看。直接看后面注册函数的调用。
也就是从 __con_initcall_start到__con_initcall_end的函数都会被调用执行。
在linux/init.h里面有
#define console_initcall(fn) \
static initcall_t __initcall_##fn \
__used __section(.con_initcall.init) = fn
通过 console_initcall(fn)定义的fn函数都会被放到.con_initcall.init段里面。这个段的开始是__con_initcall_start,结束是__con_initcall_end
我的树莓派里面定义了一个函数在drivers\tty\serial\8250\8250_core.c里面
console_initcall(univ8250_console_init);
所以console_init就会调用univ8250_console_init。
static int __init univ8250_console_init(void)
{
serial8250_isa_init_ports();
register_console(&univ8250_console);
return 0;
}
console_initcall(univ8250_console_init);
univ8250_console_init首先初始化了ports然后调用register_console注册了这个univ8250_console设备
在kernel/printk/printk.c中有register_console函数
register_console 将这个设备注册到console_drivers里面。后面printk->vprintk_emit->console_unlock->call_console_drivers->con->write(con, text, len);实现了输出