分析Linux内核的启动过程

时间:2022-06-07 04:46:08

第一章 环境

Ubuntu 14.10

Linux Kernel 3.18.6

第二章 代码及调试过程

环境搭建与内核准备:

cd ~/LinuxKernel/
wget https://www.kernel.org/pub/linux/kernel/v3.x/linux-3.18.6.tar.xz
xz -d linux-3.18.6.tar.xz
tar -xvf linux-3.18.6.tar
cd linux-3.18.6
make i386_defconfig
make

cd ~/LinuxKernel/
mkdir rootfs
git clone https://github.com/mengning/menu.git
cd menu
gcc -o init linktable.c menu.c test.c -m32 -static –lpthread
cd ../rootfs
cp ../menu/init ./
find . | cpio -o -Hnewc |gzip -9 > ../rootfs.img

cd ~/LinuxKernel/
qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img
重新编译内核:
make menuconfigkernel hacking—>compile-time checks and compile options[*] compile the kernel with debug info
开始调试:
qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img -s -S 
同时另开一个窗口,运行GDB:
gdb(gdb)file linux-3.18.6/vmlinux (gdb)target remote:1234 (gdb)break start_kernel
(gdb)list

这一次代码就不贴出来了,在init/mian.c下,主要是代码太长。
http://codelab.shiyanlou.com/xref/linux-3.18.6/init/main.c

单击相应的链接可以找到引用的变量位置,我下载了几个。

这里打算分析:

cpu_startup_entry

page_address_init()

rest_init()

page_alloc_init()

trap_init()

tick_init()

profile_init()

key_init()

security_init()

buffer_init()

init_task 0号进程

run_init_process(const char *init_filename) 1号进程

0号进程init_task的结构:

#define INIT_TASK(tsk)	\
{ \
.state = 0, \
.stack = &init_thread_info, \
.usage = ATOMIC_INIT(2), \
.flags = PF_KTHREAD, \
.prio = MAX_PRIO-20, \
.static_prio = MAX_PRIO-20, \
.normal_prio = MAX_PRIO-20, \
.policy = SCHED_NORMAL, \
.cpus_allowed = CPU_MASK_ALL, \
.nr_cpus_allowed= NR_CPUS, \
.mm = NULL, \
.active_mm = &init_mm, \
.se = { \
.group_node = LIST_HEAD_INIT(tsk.se.group_node), \
}, \
.rt = { \
.run_list = LIST_HEAD_INIT(tsk.rt.run_list), \
.time_slice = RR_TIMESLICE, \
}, \
.tasks = LIST_HEAD_INIT(tsk.tasks), \
INIT_PUSHABLE_TASKS(tsk) \
INIT_CGROUP_SCHED(tsk) \
.ptraced = LIST_HEAD_INIT(tsk.ptraced), \
.ptrace_entry = LIST_HEAD_INIT(tsk.ptrace_entry), \
.real_parent = &tsk, \
.parent = &tsk, \
.children = LIST_HEAD_INIT(tsk.children), \
.sibling = LIST_HEAD_INIT(tsk.sibling), \
.group_leader = &tsk, \
RCU_POINTER_INITIALIZER(real_cred, &init_cred), \
RCU_POINTER_INITIALIZER(cred, &init_cred), \
.comm = INIT_TASK_COMM, \
.thread = INIT_THREAD, \
.fs = &init_fs, \
.files = &init_files, \
.signal = &init_signals, \
.sighand = &init_sighand, \
.nsproxy = &init_nsproxy, \
.pending = { \
.list = LIST_HEAD_INIT(tsk.pending.list), \
.signal = {{0}}}, \
.blocked = {{0}}, \
.alloc_lock = __SPIN_LOCK_UNLOCKED(tsk.alloc_lock), \
.journal_info = NULL, \
.cpu_timers = INIT_CPU_TIMERS(tsk.cpu_timers), \
.pi_lock = __RAW_SPIN_LOCK_UNLOCKED(tsk.pi_lock), \
.timer_slack_ns = 50000, /* 50 usec default slack */ \
.pids = { \
[PIDTYPE_PID] = INIT_PID_LINK(PIDTYPE_PID), \
[PIDTYPE_PGID] = INIT_PID_LINK(PIDTYPE_PGID), \
[PIDTYPE_SID] = INIT_PID_LINK(PIDTYPE_SID), \
}, \
.thread_group = LIST_HEAD_INIT(tsk.thread_group), \
.thread_node = LIST_HEAD_INIT(init_signals.thread_head), \
INIT_IDS \
INIT_PERF_EVENTS(tsk) \
INIT_TRACE_IRQFLAGS \
INIT_LOCKDEP \
INIT_FTRACE_GRAPH \
INIT_TRACE_RECURSION \
INIT_TASK_RCU_PREEMPT(tsk) \
INIT_TASK_RCU_TASKS(tsk) \
INIT_CPUSET_SEQ(tsk) \
INIT_RT_MUTEXES(tsk) \
INIT_VTIME(tsk) \
}

第三章 调试

设置断点:一共13个。

命令为:

(gdb)break <function name>
接着输入c进行调试。

流程如下:

分析Linux内核的启动过程

来几张有代表性的调试图片:

start_kernel:

分析Linux内核的启动过程

page_address_init:

分析Linux内核的启动过程

buffer_init:

分析Linux内核的启动过程

security_init:

分析Linux内核的启动过程

cpu_startup_entry:

分析Linux内核的启动过程

分析Linux内核的启动过程

run_init_process:

分析Linux内核的启动过程

第四章 总结

通过这个方式我们知道了,Linux内核通过调用那些函数来启动,看起来在启动的时候只有些跳动的字符,可是在内部是很忙碌的。希望以后能了解到每一个函数的具体作用。不过,这就非常困难的了。

set_task_stack_end_magic确立一个init_task,这个便是后来的0号进程,也就是idle。

通过参考资料:http://blog.chinaunix.net/uid-27767798-id-3577069.html

通过检测时钟中断来提醒idle进程来复制自己的进程信息,来创建一个进程。就好像之前的myKernel一样。

然后idle进程就会检测,如果有新的任务便会中断运行,生成并释放系统资源让进程使用。当进程结束,系统便将资源收回返回给idle进程。

当执行到run_init_process时,1号进程便开始运行。

附录


卢晅 + 原创作品转载请注明出处 + 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000