linux内核分析学习笔记:用gdb跟踪linux内核启动过程
来源:互联网 发布:数据分析需要学什么 编辑:程序博客网 时间:2024/05/06 19:34
原创作品(陈晓爽 cxsmarkchan)
转载请注明出处
《Linux内核分析》MOOC课程学习笔记
操作系统的内核启动是一个复杂的过程,在这里,我们仅仅抓住内核启动的主要脉络,了解linux内核的启动过程。需要了解的问题包括:linux内核启动入口、启动时的初始化操作、0号进程和1号进程的启动、主循环的启动等。
本文实验平台为实验楼Linux内核分析,实验所用的代码可以参考这里。
1 linux内核启动流程
打开实验楼Linux内核分析,选择第3个实验:跟踪linux内核启动过程。打开控制台,用cd LinuxKernel
切换到LinuxKernel文件夹,并执行如下命令:
qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img
可以得到如下运行结果:
这里MenuOS是一个简化的操作系统,只有help/version/quit三条命令可以使用。
可以看到,启动过程中有一系列的状态输出,分别代表了内核进程和用户态进程的初始化。初始化过程包含如下部分:
1. 系统启动时有一个入口函数,在本系统中,为init/main.c下的start_kernel函数,在必要的初始化后,从此处开始加载操作系统。
2. start_kernel函数中,对操作系统内核的各个部分进行初始化。在内核初始化完成后,调用rest_init函数。
3. rest_init函数中,加载系统磁盘,启动系统的第一个用户态进程,并启动消息循环。这里的磁盘是rootfs.img,MenuOS就是在磁盘中加载的。
4. 加载完毕后,即得到上图所示的画面。
2 用gdb跟踪linux内核启动
下面我们采用gdb跟踪操作系统的启动进程。
首先以调试态启动操作系统,输入如下命令:
qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img -s -S
这时,系统启动后会直接暂停。此时我们新开一个窗口,输入如下命令:
gdbfile linux-3.18.6/vmlinuxtarget remote:1234
gdb就和已经暂停的系统通过1234端口进行通信。用如下命令设置断点:
break start_kernelbreak rest_initbreak cpu_startup_entry
这样,我们设置了三个断点,在命令行中输入c
即可向下运行,并到达下一个断点。
到达start_kernel断点时,系统的输出如下:
可以看出,系统只是进行了BIOS和ROM的初始化,正准备从start_kernel处加载系统。
start_kernel函数的最后一句是rest_init,到达rest_init时,系统的输出如下:
由此可见,系统已经完成了大量的初始化工作。这里列出rest_init的代码如下:
static noinline void __init_refok rest_init(void){ int pid; rcu_scheduler_starting(); /* * We need to spawn init first so that it obtains pid 1, however * the init task will end up wanting to create kthreads, which, if * we schedule it before we create kthreadd, will OOPS. */ kernel_thread(kernel_init, NULL, CLONE_FS); numa_default_policy(); pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES); rcu_read_lock(); kthreadd_task = find_task_by_pid_ns(pid, &init_pid_ns); rcu_read_unlock(); complete(&kthreadd_done); /* * The boot idle thread must execute schedule() * at least once to get things moving: */ init_idle_bootup_task(current); schedule_preempt_disabled(); /* Call into cpu_idle with preempt disabled */ cpu_startup_entry(CPUHP_ONLINE);}
需要重点关注的是kernel_thread(kernel_init, NULL, CLONE_FS)
语句和cpu_startup_entry(CPUHP_ONLINE)
语句,前者将第一个用户态进程menuOS(在rootfs.img中)加载进来,并在schedule_preempt_disabled()
语句中执行menuOS,后者则是启动CPU消息循环,等待用户输入。
当我们进入cpu_startup_kernel断点时,输出如下:
可见,menuOS已经显示,第一个用户态进程已经加载完毕。
接下来,继续运行cpu_startup_kernel,在函数的末尾会调用cpu_idle_loop函数,加载消息循环,如下:
static void cpu_idle_loop(void){ while (1) { /* * If the arch has a polling bit, we maintain an invariant: * * Our polling bit is clear if we're not scheduled (i.e. if * rq->curr != rq->idle). This means that, if rq->idle has * the polling bit set, then setting need_resched is * guaranteed to cause the cpu to reschedule. */ __current_set_polling(); tick_nohz_idle_enter(); while (!need_resched()) { check_pgt_cache(); rmb(); if (cpu_is_offline(smp_processor_id())) arch_cpu_idle_dead(); local_irq_disable(); arch_cpu_idle_enter(); /* * In poll mode we reenable interrupts and spin. * * Also if we detected in the wakeup from idle * path that the tick broadcast device expired * for us, we don't want to go deep idle as we * know that the IPI is going to arrive right * away */ if (cpu_idle_force_poll || tick_check_broadcast_expired()) cpu_idle_poll(); else cpuidle_idle_call(); arch_cpu_idle_exit(); } /* * Since we fell out of the loop above, we know * TIF_NEED_RESCHED must be set, propagate it into * PREEMPT_NEED_RESCHED. * * This is required because for polling idle loops we will * not have had an IPI to fold the state for us. */ preempt_set_need_resched(); tick_nohz_idle_exit(); __current_clr_polling(); /* * We promise to call sched_ttwu_pending and reschedule * if need_resched is set while polling is set. That * means that clearing polling needs to be visible * before doing these things. */ smp_mb__after_atomic(); sched_ttwu_pending(); schedule_preempt_disabled(); }}
可见,这个函数在一个while(1)循环中,不断检查need_resched()函数的值,判断是否需要进行进程调度。到这里,操作系统也就启动完毕。
3 小结
总结来说,系统的启动过程包括一系列的初始化操作,这些操作很复杂,多数模块只能留待后面继续学习了。在系统部分初始化完毕后,系统会调用rest_init
函数,而rest_init
函数首先启动系统的1号进程kernel_init
,加载menuOS
主进程。然后,rest_init会调用cpu_startup_kernel
函数,启动idle
进程,在idle
进程中不断查询是否需要进程调度。在CPU空闲时,一直由idle
进程控制,一旦有用户输入等操作,CPU的控制权就会交给menuOS
等进程。
- linux内核分析学习笔记:用gdb跟踪linux内核启动过程
- 学习笔记3-跟踪分析Linux内核的启动过程
- 利用gdb跟踪分析Linux内核的启动过程
- 使用gdb跟踪Linux内核启动过程
- 利用GDB跟踪分析linux内核启动
- Linux内核分析:跟踪分析Linux内核的启动过程
- Linux内核分析------跟踪分析Linux内核的启动过程
- 跟踪分析Linux内核的启动过程
- 跟踪分析Linux内核的启动过程
- 跟踪分析Linux内核的启动过程
- 跟踪分析Linux内核的启动过程
- 跟踪分析内核的启动过程(Linux)
- 跟踪分析Linux内核的启动过程
- 跟踪分析Linux内核的启动过程
- 跟踪分析Linux内核的启动过程
- 跟踪分析Linux内核的启动过程
- 跟踪分析Linux内核的启动过程
- 跟踪分析Linux内核的启动过程
- 安卓5.0蓝牙低功耗 代码
- CABasicAnimation的基本使用方法(移动·旋转·放大·缩小)
- iOS9中如何在日历App中创建一个任意时间之前开始的提醒(二)
- Tomcat下的JAAS实例
- Java Socket编程详解
- linux内核分析学习笔记:用gdb跟踪linux内核启动过程
- 剑指offer-链表中倒数第k个结点
- IOS动画Core Animation详解
- php 打印、显示、输出函数 收集
- java 命名规范
- 关键帧动画CAKeyframeAnimation
- vs2010 代码自动排版快捷键
- 通过sleep_for来使线程睡眠一定的时间
- 在ios中运用core animation暂停和继续动画