linux内核的总体流程主要为:
系统上电后,先进行
(1)cpu自身的初始化
自身初始化后,cpu从某个固定位置取得指令执行,该指令为跳转指令,会跳转到bios指令的首部
(2)加电自检
这个是bios的第一个任务,即所谓的post power on self test,完成系统的内存检测、系统总线检测等。
(3)加载内核引导程序
post之后,会读取启动设备的第一个扇区,即512字节的信息,该扇区叫主引导扇区,MBR,MBR引导记录中的次引导程序。
MBR扫描分区表,寻找活动分区,讲活动分区的次引导程序加载到内存中运行,次引导程序加载linux内核镜像,将控制权转交给内核。
(4)内核自解压
zImage 是小内核,bzImage是大内核,内核被调用后,首先执行/arch/i386/boot/head.S的start汇编例程,进行基本的硬件设置,调用startup_32函数->decompress_kernel()->另一个startup_32(),用于对页表进行初始化,启动内存分页功能。最后,执行init/main.c中的start_kernel函数。
内核初始化过程
从start_kernel函数开始
大体函数流程有,随便记录一下
local_irq_disable 关闭当前中断
early_init_irq_lock_class ; 设置中断描述符锁
page_address_init()初始化页地址
printk(linux_banner); 显示内核版本信息
setup_arch(&command_line);
sched_init();进程调度器初始化
time_init();初始化时钟源
console_init();初始化控制台,前面的打印都是在缓存i里
vfs_caches_init_early();虚拟化文件系统的初始化
fork_init(num_physpages)(根据物理内存大小,计算允许创建进程的数量)
上面的调用都是在star_kernel函数中,进行了前期的初始化。后续的初始化由reset_init函数进行
reset_init函数-->调用kernel_thread(kernel_init,NULL,CLONE_FS|CLONE_SIGHAND),这个函数非常关键,启动了内核线程kernel_init
reset_init函数的最后调用的函数为cpu_idle();进入idle循环以消耗空闲的cpu时间片,当内核中没有其他进程时,就执行这个。
内核线程kernel_init函数调用do_basic_setup(),完成设备驱动的初始化,在这之前,体系相关部分的初始化完成,然后调用init_post函数启动用户空间的init进程
init_post用于创建用户空间的第一个进程
执行流程为
->free_initmem();至此,所有初始化函数已被调用,因此free_initmem函数可以舍弃内存_init_begin至_init_end之间的数据
->初始化控制台,标准输入,文件描述符为0,标准输出,文件描述符为1,标准错误输出,文件描述符为2
->run_init_process("/sbin/init");
->run_init_process("/etc/init");
->run_init_process("/bin/init");
->run_init_process("sbin/sh");
查找到一个init程序,不然系统会崩溃
init 是第一个用户空间应用程序,进程编号为1,init复杂触发其他进程,init是所有其他进程的起源,init进程产生getty进程,getty进程产生login进程,login进程又产生shell进程,然后使用shell产生每一个需要的进程。
内核允许用户传递内核配置选项给内核
parse_args函数对选项进行解析
比如加载usbcore时指定模块参数autosuspend的值为2
modprobe usbcore autosuspend=2
若在内核启动时指定,则使用usbcore.autosuspend=2
内核选项解析完之后,子系统进入初始化函数的调用
kernel_init->do_basic_setup函数->do_initcalls for循环由_initcall_start开始到_initcall_end结束的函数,驱动程序的注册都在这里
kbuild构建内核时,首先从根目录makefile执行,从中获得体系无关的变量和依赖关系,同时从arch/*/makefile中获得体系结构特定的变量信息,然后进入各子系统,根据配置信息,决定该编译哪些。