Linux内核启动过程

关于linux内核启动,重点关注/init/Main.c文件.

Bootloader完成系统初始化工作后,将运行控制权交给Linux内核。根据内核是否压缩以及内核是否在本地执行,Linux通常有以下两种可选的启动方式:

Flash本地运行方式:内核的未经压缩的可执行映像固化在Flash,系统启动时内核在Flash中开始逐句执行。kernel映象为非压缩格式,通过make Image获得,那么真正的入口就是arch/arm/kernel/head_armv.S

压缩内核加载方式:内核的压缩映像固化在Flash上,系统启动时由附加在压缩映像前的解压复制程序读取压缩映像,在内存中解压后执行,这种方式相对复杂,但是运行速度更快(RAM的存取速率要比Flash高)。如果是zImage,那么程序的入口是arch/arm/boot/compressed/head.S

本节我们介绍压缩内核的启动方式,内核的Flash本地运行方式不再详细中介绍。

本地运行时内核的启动包括特定体系结构设置和Linux系统初始化两步,内核启动的入口文件是arch/arm/kernel/head-armv.s

特定体系结构设置

本过程由汇编文件head-armv.s完成。

Head-armv.s文件位于./arch/arm/kernel/目录下,是Boot Loader将控制权交给内核后执行的第一个程序。下面是head-armv.s的基本运行过程:

1  配置系统寄存器;

2  初始化ROMRAM以及总线控制寄存器等;

3  设置堆栈指针,将bss段清零;

4  修改pc指针,跳转到./init/main.c中的start_kernel函数,开始Linux系统的初始化。

Linux系统初始化

程序跳转到start_kernel函数执行,在这里完成处理器结构的初始化、中断的初始化、进程相关的初始化以及内存初始化等重要工作。

下面在start_kernel函数中列举了主要的初始化过程:

asmlinkage void __init start_kernel(void)

{

       char * command_line;

       unsigned long mempages;

       extern char saved_command_line[];

/*

 * Interrupts are still disabled. Do necessary setups, then enable them

 */

       lock_kernel();

       printk(Linux_banner);                 //--1

       setup_arch(&command_line);              //--2

       printk("Kernel command line: %s\n", saved_command_line);

       parse_options(command_line);

       trap_init();                                  //--3

       init_IRQ();                                         //--4

       sched_init();                               //--5

       softirq_init();                              //--6

       time_init();                                  //--7

 

       /*

        * HACK ALERT! This is early. We're enabling the console before

        * we've done PCI setups etc, and console_init() must be aware of

        * this. But we do want output early, in case something goes wrong.

        */

       console_init();                             //--8

#ifdef CONFIG_MODULES

       init_modules();                                   //--9

#endif

       if (prof_shift) {

              unsigned int size;

              /* only text is profiled */

              prof_len = (unsigned long) &_etext - (unsigned long) &_stext;

              prof_len >>= prof_shift;

             

              size = prof_len * sizeof(unsigned int) + PAGE_SIZE-1;

              prof_buffer = (unsigned int *) alloc_bootmem(size);

       }

 

       kmem_cache_init();                            //--10

       sti();                                                 //--11

       calibrate_delay();                         //--12

#ifdef CONFIG_BLK_DEV_INITRD         

       if (initrd_start && !initrd_below_start_ok &&

                     initrd_start < min_low_pfn << PAGE_SHIFT) {

              printk(KERN_CRIT "initrd overwritten (0x%08lx < 0x%08lx) - "

                  "disabling it.\n",initrd_start,min_low_pfn << PAGE_SHIFT);

              initrd_start = 0;

       }

#endif

       mem_init();                                        //--13

       kmem_cache_sizes_init();            //--14

       pgtable_cache_init();                    //--15

 

#ifdef CONFIG_PERFMON

       perfmon_init();

#endif

       mempages = num_physpages;

 

       fork_init(mempages);                   //--16

       proc_caches_init();                      //--17

       vfs_caches_init(mempages);         //--18

       buffer_init(mempages);                //--19

       page_cache_init(mempages);        //--20

#if defined(CONFIG_ARCH_S390)

       ccwcache_init();

#endif

       signals_init();                              //--21

#ifdef CONFIG_PROC_FS

       proc_root_init();                          //--22

#endif

#if defined(CONFIG_SYSVIPC)

       ipc_init();                                          //--23

#endif

       check_bugs();                             //--24

       printk("POSIX conformance testing by UNIFIX\n");

 

       /*

        *    We count on the initial thread going ok

        *    Like idlers init is an unlocked kernel thread, which will

        *    make syscalls (and thus be locked).

        */

       smp_init();                                         //--25

       rest_init();                                  //--26

}

1

Pintk函数位于./kernel/printk.c函数中,它是内核中的打印输出函数,在这里输出Linux版本,编译器版本以及编译者的信息。

2

setup_arch函数位于./arch/arm/kernel/setup.c中,它完成特定于体系结构的设置,包括初始化硬件寄存器、标识根设备和系统中可用的DRAMFlash的大小、指定系统中可用页面的数目、文件系统大小等等。所有这些信息都以参数形式从引导装载程序传递到内核。setup_arch 还需要对Flash存储库、系统寄存器和其它特定设备执行内存映射。一旦完成了特定于体系结构的设置,控制就返回到初始化系统其余部分的 start_kernel 函数。

3

trap_init函数在./arch/arm/kernel/traps.c文件中。它对中断向量表进行初始化,初始化处理器的一些中断处理。它通过调用一些宏对中断向量表填写中断响应程序的偏移地址。中断要等到calibrate_delay()函数运行之前才允许被调用。

4

init_IRQ函数的源程序在./arch/arm/kernel/irq.c文件中。该函数初始化中断控制器,但是它们的中断输出要到系统设备和内核模块调用request_irq()函数后才能使用。

5

sched_init函数的源程序在./arch/arm/kernel/sched.c中。该函数初始化内核pidhash[]表(这是一个快速映射进程ID到进程描述符的查询表)。

6

softirq_init函数的源程序在./arch/arm/kernel/softirq.c中。初始化软中断,

7

time_init函数的源程序在linux-2.4.x/arch/arm/kernel/time.c中。该函数初始化目标板系统硬件时钟。

8

console_init函数的源程序在./drivers/char/tty_io.c中。初始化控制台

9

init_modules函数的源程序在./arch/arm/kernel/module.c中。初始化模块,

10

kmem_cache_init函数的源程序在./mm/slab.c中。该函数初始化内核SLAB内存管理子系统。SLAB是内核内部结构的动态内存管理。

11

Sti宏定义在./include/asm-arm/system.h中。设中断。

12

calibrate_delay函数的源程序在./init/main.c中。该函数执行内核的BogoMips(校准Linux内部循环延时,使得延时的时间和不同速率的处理器都保持在一个大概相同的比率上)运算。

13

mem_init函数的源程序在./arch/arm/mm/init.c中。该函数初始化内核的内存管理系统,并打印出内核所有可用的内存和内核已经占用的内存。

14

kmem_cache_sizes_init函数的源程序在./mm/slab.c中。该函数完成kmem_cache_init()函数开始的SLAB的初始化。

15

pgtable_cache_init函数的源程序在./arch/arm/mm/mm-armv.c中,该函数完成pgtable cache的初始化。

16

fork_init(mempages)函数的源程序在./kernel/fork.c中。该函数初始化max_threadsinit_task变量。这些变量会在调用fork()函数时用到。

17

proc_caches_init函数的源程序在./kernel/fork.c中。该函数初始化内核使用的SLAB缓存,类似malloc()的初始化。

18

vfs_caches_init(mempages)函数的源程序在./fs/dcache.c中。该函数初始化虚拟文件系统使用的SLAB缓存。

19

buffer_init(mempages) 函数的源程序在./fs/buffer.c中。该函数初始化内核缓冲区。

20

page_cache_init(mempages) 函数的源程序在./mm/filemap.c中。该函数初始化内核页面缓存系统。

21

signals_init函数的源程序在./kernel/signal.c中。该程序初始化内核信号队列。

22

proc_root_init函数的创建proc文件系统,源程序在./fs/proc/root.c中。该函数初始化proc文件系统,创建几个类似/proc/bus/porc/driver的标准目录。

23

ipc_init函数的源程序在./ipc/util.c中。该函数初始化System V的进程通信资源,包括信号灯(sem_init()子函数)、消息队列(msg_init子函数)和共享内存(shm_init()子系统)。

24

check_bugs函数的源程序在./include/asm-arm/bugs.c中。该函数能够检查一些处理器的错误。

25

smp_init函数的源程序在./init/main.c中。如果处理器类型是SMP-capable x86,该函数调用IO_APIC_init_uniprocessor()函数配置APIC设备。如果是其他的处理器,该函数不做任何处理。

26

rest_init函数的源程序在./init/main.c中。该函数释放初始化函数占用的内存,调用init()函数创建kernel_thread结束内核启动进程,初始化内核PCI和网络,加载操作系统。

 

 

源代码阅读网站

http://lxr.oss.org.cn/

你可能感兴趣的:(Linux内核启动过程)