linux 单步启动_Linux内核的启动过程初见

讲真,因为启动过程太复杂,这个博客很难写,想了几天,不知道从哪里开始讲起。不过,不开始,永远不知道有多难写,那么就试试看。

***一般的学习主线是:Start_kernel(); –> rest_init(); -> kernel_init(); ***

在写的过程中,感觉到自己文字的生硬,完全是硬解,而不能算得上是真的理解。

内核代码交叉引用链接

在本地制作Menu OS,成功。

惯例,实验步骤如下:

启动Linux内核,但是在启动的时候使得CPU进入freeze状态,因为我们等下要用gdb单步调试。

cd LinuxKernel/

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)

#-s shorthand for -gdb tcp::1234 若不想使用1234端口,则可以使用-gdb tcp:xxxx来取代-s选项

另外需要一个terminal窗口,建立gdb和之前我们在启动内核时qemu -s选项所启动的gdb server之间的连接。

gdb

(gdb)file linux-3.18.6/vmlinux # 加载符号表

(gdb)target remote:1234 # 建立gdb和gdbserver之间的连接

(gdb)b start_kernel # 在start_kernel处设置断点

(gdb)c # 继续运行

如下:

从上图可以看得出来,系统已经停止在了start_kernel处,接下来便是我们要分析的地方。

首先是lockdep_init,只是初始化一次hash表。

紧接着的是[set_task_stack_end_magic]((http://lxr.free-electrons.com/ident?v=3.18;i=set_task_stack_end_magic)(&init_task);从下图看得到,意图很简单,仅仅是为init_task设置堆栈的边界点,所谓的魔数,用来防止堆栈溢出。

对于单CPU,smp_setup_processor_id无作用。

接下来debug_objects_early_init,初始化buckets,即obj_hash。把static object pool数组的元素初始化成链表;

boot_init_stack_canary,初始化带防止栈溢出攻击保护的堆栈;

cgroup_init_early,初始化cgroup以及需要尽早启动的子系统;

local_irq_disable,关闭当前CPU的所有中断响应;

early_boot_irqs_disabled = true;告诉我们,在‘early bootup code’阶段,boot processor只能运行在中断禁止模式。只有当这个标志位为false的时候,才能运行一些被禁的操作。

紧接着下面的一大堆代码都是各种系统必要的初始化。这些初始化步骤很是复杂,每个都值得去深挖进去折腾好久。然而,我们目前的目的是搞清楚这个过程,要分清主干和枝叶。不然看得越深,陷得越深。

每个函数都够自己吃一壶的,哎。

WARN(!irqs_disabled(), "Interrupts were enabled early\n"); early_boot_irqs_disabled = false; local_irq_enable();

这两句代码和我们之前的early_boot_irqs_disabled = true;相呼应,查询是否中断已经被提前打开,是的话发出警告。同时告诉系统,现在中断已经被使能了,之前不能做的事情现在可以做了。

此处产生idle进程

从代码中可以看到,idle进程产生之后,立刻将其状态改变idle->state = TASK_RUNNING;

当console_init运行的时候,系统会设置控制台tty,把和控制台相关的东西都初始化,控制台初始化完毕,通过一系列函数指针的调用,系统就会打印出很多东西。

最引人注意的是从内核态进入用户态的rest_init函数 了。

可以看到,系统是在kernel_thread中调用kernel_init,kernel_init 调用do_fork来产生1号进程的。

通过跟踪,我们发现,在schedule_preempt_disabled()函数中执行上下文切换,执行完schedule()之后立刻调度到kernet_init执行。

可以看到,在kernel_init中会调用run_init_process

跟踪调试的时候,发现总是不执行到下面的这一段语句:

if (!try_to_run_init_process("/sbin/init") ||

!try_to_run_init_process("/etc/init") ||

!try_to_run_init_process("/bin/init") ||

!try_to_run_init_process("/bin/sh"))

run_init_process又调用do_execve。

跟踪之后,发现每次都是ramdisk_execute_command为真的时候,执行run_init_process,然后返回值ret为0(在gdb中打印的时候,总是无法打印出来之,提示optimized out,原因在于编译代码的过程中采用了-Ox的优化等级,不过可以猜测出来ret是0)。

显然函数返回了之后应该是进入用户态了吧。代码太庞杂。

到汇编了,就跟不下去了。可以用ni,si。

先到这里吧,慢慢品味。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值