Mykernel 第三周课程报告

刘子健 +
原创作品转载请注明出处 + 

《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000



使用gdb跟踪调试内核从start_kernel到init进程启动
详细分析从start_kernel到init进程启动的过程

Mykernel 第三周课程报告_第1张图片

  • 使用gdb跟踪调试内核

  1. qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img -s -S # 关于-s和-S选项的说明:
  2. # -S freeze CPU at startup (use ’c’ to start execution)
  3. # -s shorthand for -gdb tcp::1234 若不想使用1234端口,则可以使用-gdb tcp:xxxx来取代-s选项

另开一个shell窗口

  1. gdb
  2. (gdb)file linux-3.18.6/vmlinux # 在gdb界面中targe remote之前加载符号表
  3. (gdb)target remote:1234 # 建立gdb和gdbserver之间的连接,按c 让qemu上的Linux继续运行
  4. (gdb)break start_kernel # 断点的设置可以在target remote之前,也可以在之后






















Linux内核启动过程相关的参考资料

计算机的启动过程概述

x86 CPU启动的第一个动作CS:EIP=FFFF:0000H(换算为物理地址为000FFFF0H,因为16位CPU有20根地址线),即BIOS程序的位置

BIOS例行程序检测完硬件并完成相应的初始化之后就会寻找可引导介质,找到后把引导程序加载到指定内存区域后,就把控制权交给了引导程序。这里一般是把硬盘的第一个扇区MBR和活动分区的引导程序加载到内存(即加载BootLoader),加载完整后把控制权交给BootLoader。

引导程序BootLoader开始负责操作系统初始化,然后起动操作系统。启动操作系统时一般会指定kernel、initrd和root所在的分区和目录,比如root (hd0,0),kernel (hd0,0)/bzImage root=/dev/ram init=/bin/ash,initrd (hd0,0)/myinitrd4M.img

内核启动过程包括start_kernel之前和之后,之前全部是做初始化的汇编指令,之后开始C代码的操作系统初始化,最后执行第一个用户态进程init

一般分两阶段启动,先是利用initrd的内存文件系统,然后切换到硬盘文件系统继续启动。initrd文件的功能主要有两个:1、提供开机必需的但kernel文件(即vmlinuz)没有提供的驱动模块(modules) 2、负责加载硬盘上的根文件系统并执行其中的/sbin/init程序进而将开机过程持续下去




在kernel/fork.c里面实现了kernel_thread函数

Mykernel 第三周课程报告_第2张图片

do_fork就开始fork进程了!


在调用rest_init之前,会有很多初始化的工作,这个工作都由进程0来完成!

直到rest_init被调用,创建进程1.


由于初始化的操作太多, 水平有限,只能适当的选取一些函数进行分析.

注意到,调用了这个函数


函数定义如下: 经调试发现smp_processor_id()的返回值为0.即,用于启动的CPU是0号CPU

Mykernel 第三周课程报告_第3张图片

我们定位函数,发现smp_processor_id()实质上是一个宏定义,然后调用的别人的函数.

  

Mykernel 第三周课程报告_第4张图片  

这里一直到rest_init()都是由init_task进程控制的.而init_task是内核程序员人为创建的进程.

Mykernel 第三周课程报告_第5张图片


以下是rest_init()的实现.

Mykernel 第三周课程报告_第6张图片

通过kernel_thread()函数将创建1号(而不是0号, 0 号就是我们之前一直处于的进程 :-) . 2号进程为kthreadd/

这里1号进程将进入kernel_init()函数

啊哈!我们来看看1号进程里面都是些啥

static int __ref kernel_init(void *unused)
{
	int ret;

	kernel_init_freeable();
	/* need to finish all async __init code before freeing the memory */
	async_synchronize_full();
	free_initmem();
	mark_rodata_ro();//把只读的数据标记为只读
	system_state = SYSTEM_RUNNING;//系统状态标记为正在运行
	numa_default_policy();

	flush_delayed_fput();

	if (ramdisk_execute_command) {
		ret = run_init_process(ramdisk_execute_command);
		if (!ret)
			return 0;
		pr_err("Failed to execute %s (error %d)\n",
		       ramdisk_execute_command, ret);
	}

	/*
	 * We try each of these until one succeeds.
	 *
	 * The Bourne shell can be used instead of init if we are
	 * trying to recover a really broken machine.
	 */
	if (execute_command) { //注意!(我们这里就可以动点"手脚"了)如果我们指定了init程序就可以跑我们的程序,如果没有,这里就进不去if.
		ret = run_init_process(execute_command);
		if (!ret)
			return 0;
		pr_err("Failed to execute %s (error %d).  Attempting defaults...\n",
			execute_command, ret);
	}//到这里系统就会使用系统默认的启动程序, 四个目录下面的
	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"))
		return 0;

	panic("No working init found.  Try passing init= option to kernel. "
	      "See Linux Documentation/init.txt for guidance.");
}


我们看,这是我电脑现在的进程运行情况(我勒个大擦,chrome吃了1G+的内存).好吧重点是,我们看到1号进程还在,它啥也不干的,悠然自得的就在哪儿~初始化结束,系统正确进入用户等待操作的状态之后,进程1也会一直存在(题外话,僵尸进程会变成1号进程的后代.)
Mykernel 第三周课程报告_第7张图片



我只想吐槽,这编译时间,简直了,受不了,读者可以自行去修改config文件,然后别去编译什么鬼module,用不到,这里快点进入我们的init进程,即自己写的menu程序就可以啦

如下图运行结果.

千万千万记得,make之前要做make i386_deconfig

我之前就是缺了这步,然后可能是用的64位的虚拟的,然后就panic了 : )

前车之鉴


Mykernel 第三周课程报告_第8张图片



制作跟文件系统

首先在内核目录树根目录下面创建rootfs目录.

参考文献:

http://blog.chinaunix.net/uid-23769728-id-3127671.html

http://blog.csdn.net/larryliuqing/article/details/10077785



Mykernel 第三周课程报告_第9张图片




你可能感兴趣的:(linux,操作系统,linux内核)