1.8 time_init()
这个函数用来做体系相关的timer的初始化,armnommu的在arch/armnommu/kernel/time.c。
这里调用了在 include/asm-armnommu/arch-xxxx/time.h中的inline函数setup_timer。
setup_timer()函数的设计与硬件设计紧密相关,主要是根据硬件设计情况设置时钟中断号和时钟频率等。
1 void __inline__ setup_timer (void)2 {3 /*----- disable timer -----*/
4 CSR_WRITE(TCR0, xxx);5
6 CSR_WRITE (AIC_SCR7, xxx); /*setting priority level to high*/
7 /*timer 0: 100 ticks/sec*/
8 CSR_WRITE(TICR0, xxx);9
10 timer_irq.handler =xxxxxx_timer_interrupt;11 setup_arm_irq(IRQ_TIMER, &timer_irq); /*IRQ_TIMER is the interrupt number*/
12
13 INT_ENABLE(IRQ_TIMER);14 /*Clear interrupt flag*/
15 CSR_WRITE(TISR, xxx);16
17 /*enable timer*/
18 CSR_WRITE(TCR0, xxx);19 }
View Code
1.9 console_init()
控制台初始化。控制台也是一种驱动程序,由于其特殊性,提前到该处完成初始化,主要是为了提前看到输出信息,据此判断内核运行情况。
很多嵌入式Linux操作系统由于没有在/dev目录下正确配置console设备,造成启动时发生诸如unable to open an initial console的错误。
1.10 init_modules()
模块初始化。如果编译内核时使能该选项,则内核支持模块化加载/卸载功能
1.11 kmem_cache_init()
内核Cache初始化
1.12 sti()
使能中断,这里开始,中断系统开始正常工作。
1.13 calibrate_delay()
近似计算BogoMIPS数字的内核函数。作为第一次估算,calibrate_delay计算出在每一秒内执行多少次__delay循环,也就是每个定时器滴答(timer tick)―百分之一秒内延时循环可以执行多少次。这种计算只是一种估算,结果并不能精确到纳秒,但这个数字供内核使用已经足够精确了。
BogoMIPS的数字由内核计算并在系统初始化的时候打印。它近似的给出了每秒钟CPU可以执行一个短延迟循环的次数。在内核中,这个结果主要用于需要等待非常短周期的设备驱动程序――例如,等待几微秒并查看设备的某些信息是否已经可用。
计算一个定时器滴答内可以执行多少次循环需要在滴答开始时就开始计数,或者应该尽可能与它接近。全局变量jiffies中存储了从内核开始保持跟踪时间开始到现在已经经过的定时器滴答数, jiffies保持异步更新,在一个中断内——每秒一百次,内核暂时挂起正在处理的内容,更新变量,然后继续刚才的工作。
1.15 kmem_cache_sizes_init()
内核内存管理器的初始化,也就是初始化cache和SLAB分配机制。
1.16 pgtable_cache_init()
页表cache初始化。
1.17 fork_init()
这里根据硬件的内存情况,如果计算出的max_threads数量太大,可以自行定义。
1.18 proc_caches_init();
为proc文件系统创建高速缓冲
1.19 vfs_caches_init(num_physpages);
为VFS创建SLAB高速缓冲
1.20 buffer_init(num_physpages);
初始化buffer
1.21 page_cache_init(num_physpages);
页缓冲初始化
1.22 signals_init();
创建信号队列高速缓冲
1.23 proc_root_init();
在内存中创建包括根结点在内的所有节点
1.24 check_bugs();
检查与处理器相关的bug
1.25 smp_init();
1.26 rest_init();
此函数调用kernel_thread(init, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGNAL)函数。
1.26.1 kernel_thread()函数分析
这里调用了arch/armnommu/kernel/process.c中的函数kernel_thread,kernel_thread函数中通过__syscall(clone)创建新线程。
__syscall(clone)函数参见armnommu/kernel目录下的entry- common.S文件。
1.26.2 init()完成下列功能:
init()函数通过kernel_thread(init, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGNAL)的回调函数执行,完成下列功能。
do_basic_setup()在该函数里,sock_init()函数进行网络相关的初始化,占用相当多的内存,如果所开发系统不支持网络功能,可以把该函数的执行注释掉。
do_initcalls()实现驱动的初始化, 这里需要与vmlinux.lds联系起来看才能明白其中奥妙。
static void __init do_initcalls(void)
{
initcall_t*call;
call= &__initcall_start;do{
(*call)();
call++;
}while (call < &__initcall_end);/*Make sure there is no pending stuff from the initcall sequence*/flush_scheduled_tasks();
}
查看 /arch/i386/vmlinux.lds,其中有一段代码
__initcall_start =.;
.initcall.init : {*(.initcall.init) }
__initcall_end= .;
其含义是__initcall_start指向代码节.initcall.init的节首,而__initcall_end指向.initcall.init的节尾。
do_initcalls所作的是系统中有关驱动部分的初始化工作,那么这些函数指针数据是怎样放到了.initcall.init节呢?
在include/linux/init.h文件中有如下3个定义:
1. #define __init_call __attribute__ ((unused,__section__ (".initcall.init" ))
__attribute__的含义就是构建一个在.initcall.init节的指向初始函数的指针。
2. #define __initcall(fn) static initcall_t __initcall_##fn __init_call = fn
##意思就是在可变参数使用宏定义的时候构建一个变量名称为所指向的函数的名称,并且在前面加上__initcall_
3. #define module_init(x) __initcall(x);
很多驱动中都有类似module_init(usb_init)的代码,通过该宏定义逐层解释存放到.initcall.int节中。