在linux初始化过程中,除非启用了early console,否则直到console_init调用之前是没有任何输出的,它们的输出都放在__log_buf这个缓冲内的,在console_init调用时再将这个缓冲区内的数据一次性输出。
void __init console_init(void)
{
initcall_t *call;
/* Setup the default TTY line discipline. */
(void) tty_register_ldisc(N_TTY, &tty_ldisc_N_TTY);
/*
* set up the console device so that later boot sequences can
* inform about problems etc..
*/
#ifdef CONFIG_EARLY_PRINTK
disable_early_printk();
#endif
call = __con_initcall_start;/*zswan*/
while (call < __con_initcall_end) {
(*call)();
call++;
}
}
说实话这段代码比2.4里面的代码简单多了,linux进步不少啊 。
/*****************************************************************************/
void) tty_register_ldisc(N_TTY, &tty_ldisc_N_TTY);这段code主要的用途就是
注册tty线路规程的,大家研究tty的驱动就会发现了在用户和硬件之间tty的驱动是分了三层的
最底层当然是tty驱动程序了,主要负责从硬件接受数据,和格式化上层发下来的数据后给硬件。
在驱动程序之上就是线路规程了,他负责把从tty核心层或者tty驱动层接受的数据进行特殊的按着某个协议的格式化,就像是
ppp或者蓝牙协议,然后在分发出去的。
在tty线路规程之上就是tty核心层了。大家可参考ldd3学习一下。
/*****************************************************************8/
call = __con_initcall_start;/*zswan*/
while (call < __con_initcall_end) {
(*call)();
call++;
}
这段代码写得的确高深,比2.4的好,2.4的一看就知道干啥了,这个我看了半天,原来就是初始化个终端啊,晕!
看看这段代码吧:
在vmlinux.lds.S中连接脚本汇编中有这段代码
__con_initcall_start = .;
*(.con_initcall.init)
__con_initcall_end = .;
原来的call = __con_initcall_start;/*zswan*/就是把__con_initcall_start的虚拟地址给call
去执行在 __con_initcall_start = .;和__con_initcall_end = .;之间的con_initcall.init,设想一下吧linux惯用做法肯定是把某个
实际初始化函数的指针数据是放到了con_initcall.init的节,那么是怎么放的呢?
那么再看下面的code吧,
在init.h里面有这么一句宏定义啊
#define console_initcall(fn) /
static initcall_t __initcall_##fn /
__attribute_used__ __attribute__((__section__(".con_initcall.init")))=fn
这句的含义就是构建一个.con_initcall.init节的指向初始函数的指针。
在我的串口程序里面有这么一句:
console_initcall(serial_pxa_console_init);
大家这回就看出来了,通过#define console_initcall(fn) /宏,我的这段serial_pxa_console_init函数代码
就被节的指针所指向了。
总结大致的流程就很简单了start_kernel===>console_init===>serial_pxa_console_init
达到初始化控制台的目的了。说得比较乱了,大家知道这个意思自己在研究一下吧!
未完待续。。