第一章 环境
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 menuconfig kernel 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进行调试。
流程例如以下:
来几张有代表性的调试图片:
start_kernel:
page_address_init:
buffer_init:
security_init:
cpu_startup_entry:
run_init_process:
第四章 总结
通过这个方式我们知道了。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