一.搭建Linux内核编译、阅读环境。
1.下载内核源码:
wget
xz -d linux-3.18.28.tar.xz
tar-xvf linux-3.18.28.tar
cd linux-3.18.28
make i386_defconfig
make menuconfig -> kernel hacking—>[*] compile the kernel with debug info //这样会在内核调试阶段加入debug信息。
make # 一般要编译很长时间,少则20分钟多则数小时
有的系统可能出现:
curses.h: No such file or directory 这个错误,这个是因为本机开启menuconfig界面时,需要creses字符库的支持,所以只需要在本机上安装库就可以了,我使用的是Ubuntu10.04,安装命令为:
apt-get install libncurses5-dev
2.制作文件系统
# 制作根文件系统
mkdir rootfs
git clone
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
3.运行内核
qemu -kernel linux-3.18.28/arch/x86/boot/bzImage -initrd rootfs.img
这时可以看到运行起来的menuos界面,并且通过 help命令可以查看支持的其他命令。
4.使用vim搭建代码阅读环境,这个可以参考其他文档资料,我使用的是ctag+vim,参考来源为:
http://blog.chinaunix.net/uid-22891435-id-380187.html
二.使用gdb跟踪内核代码
1.内核启动时的参数:
qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img -s -S # 关于-s和-S选项的说明:
# -S freeze CPU at startup (use ’c’ to start execution)冻结cpu
# -s shorthand for -gdb tcp::1234 若不想使用1234端口,则可以使用-gdb tcp:xxxx来取代-s选项
2.gdb启动参数:
gdb
(gdb)file linux-3.18.6/vmlinux # 在gdb界面中targe remote之前加载符号表
(gdb)target remote:1234 # 建立gdb和gdbserver之间的连接,按c 让qemu上的Linux继续运行
(gdb)break start_kernel # 断点的设置可以在target remote之前,也可以在之后
运行起来的效果图就如上面,区别在于,我们可以通过gdb添加断点来调试内核,跟踪内核的启动流程。
a.我们添加第一个断点:
在gdb中输入如下:break start_kernel #break也可以使用单个字符 b 来代替
然后 输入 continue #continue 可以使用 单个字符 c 来代替
进入如下打印片段,这是qemu已经进入了内核启动的开始阶段。
-
Breakpoint 1, start_kernel () at init/main.c:501
-
501 {
-
(gdb)
我们可以进入init/main.c 501行,可以看到很多初始化函数,下面只列举几个函数:
-
void set_task_stack_end_magic(struct task_struct *tsk) //这个表示对任务列表进行初始化
-
{
-
unsigned long *stackend;
-
-
stackend = end_of_stack(tsk);
-
*stackend = STACK_END_MAGIC; /* for overflow detection */
-
}
在main.c中的调用为:
-
set_task_stack_end_magic(&init_task); //对task进行初始化
-
void __init trap_init(void); //完成硬件中断初始化
-
mm_init(); //内存管理初始化
-
/*
* Set up the scheduler prior starting any interrupts (such as the
* timer interrupt). Full topology setup happens at smp_init()
* time - but meanwhile we still have a functioning scheduler.
*/
sched_init(); //如上所述,完成进程调度管理的初始化
-
init_IRQ(); //初始化中断处理
-
/*
-
-
从rest_init开始,Linux开始产生进程,因为init_task是静态制造出来的,pid=0,它试图将从最早的汇编代码一直到start_kernel的执行都纳入到init_task进程上下文中。在rest_init函数中,内核将通过下面的代码产生第一个真正的进程(pid=1):
-
*/
-
rest_init();
rest_init();函数结束后,整个内核就开始了。
作者程大鹏, 转载请注明出处 http://blog.chinaunix.net/blog/post.html
Linux内核分析》MOOC课程 ”
阅读(2358) | 评论(0) | 转发(0) |