2020-2021-1 20209320 《Linux内核原理与分析》第四周作业
1.实验过程
启动memu系统
cd LinuxKernel/
qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img
使用gdb跟踪调试内核
$ qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img -s -S
关于-s和-S选项的说明:
1. -S freeze CPU at startup (use ’c’ to start execution)
2. -s shorthand for -gdb tcp::1234
若不想使用1234端口,则可以使用-gdb tcp:xxxx来取代-s选项
gdb指令打开gdb调试器
start_kernel函数
【
start_kernel执行过程
1.调用sched_init()函数来初始化调度程序
2.调用build_all_zonelists()函数俩初始化内存管理
3.调用page_alloc_init()函数来初始化伙伴系统分配程序
4.调用trap_init()函数和init_IRQ()函数以初始化IDT
5.调用softing_init()函数初始化TASKLET_SOFTIRQ和HI_SOFTIRQ(软中断)
6.调用time_init()初始化系统日期时间
7.调用kmem_cache_init()函数初始化slab分配器(普通和高速缓存)
8.调用calibrate_delay()函数用于确定CPU时钟(延迟函数)
9.调用kernel_thread()函数为进程1创建内个线程,这个内核线程又会创建其他的内核线程并执行/sbin/init程序
10.在start_kernel()开始执行之后会显示linux版本,除此之外,在init程序和内核线程执行的最后阶段还会显示很多其他信息。最后,就会在控制台上出现熟悉的登陆提示,通知用户Linux内核已经启动正在运行。
Linux内核的启动在宏观上来看,就是start_kernel()来进行各种初始化工作,最终执行到rest_init()来初始化0号进程和1号用户态的进程。然后操作系统就运行起来了。
rest_init()最后执行cpu_startup_entry();cpu_startup_entry会调用cpu_idle_loop(), 在cpu_idle_loop()里面有个while(1)的循环一直执行,作为idle进程,pid是0号,此进程会一直执行下去,并且在系统没有任何需要执行的进程时,调度到此进程。
rest_init()函数分析
- rest_init中调用kernel_thread函数启动了2个内核线程,分别是:kernel_init和kthreadd
- 调用schedule函数开启了内核的调度系统,从此linux系统开始转起来了。
- rest_init最终调用cpu_idle函数结束了整个内核的启动。也就是说linux内核最终结束了一个函数cpu_idle。这个函数里面肯定是死循环。
- 简单来说,linux内核最终的状态是:有事干的时候去执行有意义的工作(执行各个进程任务),实在没活干的时候就去死循环(实际上死循环也可以看成是一个任务)。
- 之前已经启动了内核调度系统,调度系统会负责考评系统中所有的进程,这些进程里面只有有哪个需要被运行,调度系统就会终止cpu_idle死循环进程(空闲进程)转而去执行有意义的干活的进程。这样操作系统就转起来了。
总结
linux内核的启动过程从start_kernel到init,整个linux系统的所有进程是一个树形结构,树根是0号进程 。Linux一开始先在无进程的情况下将一直从初始化部分的代码执行到start_kernel,然后再到其最后一个函数调用rest_init。从rest_init开始,Linux开始产生进程,在rest_init中,通过init_task产生pid=0的进程,即0号进程(idle进程),它是内核状态下的进程;在rest_init函数中,内核通过kernel_ini创建1号进程,它是第一个用户态进程。关于init_task(也就是idle),当运行队列中没有别的就绪进程时,init_task(也就是idle)将会被调用,它的核心是一个while(1)循环,在循环中它将会调用schedule函数以便在运行队列中有新进程加入时切换到该新进程上。