内核初始化
内核的启动从入口函数 start_kernel() 开始。在 init/main.c 文件中,start_kernel 相当于内核的 main 函数,内部有很多init函数:
Init_task
首先:创建0号进程init_task
定义是 struct task_struct init_task = INIT_TASK(init_task)。它是系统创建的第一个进程,我们称为 0 号进程。这是唯一一个没有通过 fork 或者 kernel_thread 产生的进程,是进程列表的第一个。
该进程就是后续其他项目复制的根。
trap_init()中断初始化
里面设置了很多中断门(Interrupt Gate),用于处理各种中断
mm_init() 初始化内存管理模块
mnt_init()->init_rootfs()
注册了VFS 虚拟文件系统的类型,vfs_caches_init()函数在VFS虚拟文件系统里面注册了基于内存的文件系统rootfs
文件系统是我们的项目资料库,为了兼容各种各样的文件系统,我们需要将文件的相关数据结构和操作抽象出来,形成一个抽象层对上提供统一的接口,这个抽象层就是 VFS(Virtual File System),虚拟文件系统。
sched_init() 初始化调度模块
sched_init() 就是用于初始化调度模块。
rest_init()其他方面的初始化
rest_init 的第一大工作是,用 kernel_thread(kernel_init, NULL, CLONE_FS) 创建第二个进程,这个是 1 号进程。是所有用户线程的父亲。
权限划分:
将能够访问关键资源的代码放在 Ring0,我们称为内核态(Kernel Mode);将普通的程序代码放在 Ring3,我们称为用户态(User Mode)
用户态到内核态过程:
用户态 - 系统调用 - 保存寄存器 - 内核态执行系统调用 - 恢复寄存器 - 返回用户态
struct pt_regs,这个结构就是在系统调用的时候,内核中保存用户态运行上下文的,里面将用户态的代码段 CS 设置为 __USER_CS,将用户态的数据段 DS 设置为 __USER_DS,以及指令指针寄存器 IP、栈指针寄存器 SP。这里相当于补上了原来系统调用里,保存寄存器的一个步骤
调用完恢复的时候,就是根据pt_regs存储的上下文进行恢复
ramdisk 的作用
ramdisk是解决init程序的存储问题,在内核启动过程中需要init文件,如果从文件系统直接获取那么我们必须有各种磁盘的驱动才能从磁盘之上的文件系统读取到我们需要的文件,这样内核就复杂化啦,而采用ramdisk就是弱化磁盘驱动依赖,采用内存保存,这样就能直接通过总线访问。
将进程从内核态切换到用户态,然后开始加载驱动并设置真正的根文件系统(根据外部存储设备)
创建 2 号进程
创建的时候调用的是,kernel_thread,成为thread而不是进程是因为。
从内核态来看,无论是进程,还是线程,我们都可以统称为任务(Task),都使用相同的数据结构,平放在同一个链表中
2 号进程负责所有内核态的线程的调度和管理,是内核态所有线程运行的祖先。