日期:2014-05-16  浏览次数:20695 次

Linux内核修改实验
Linux内核修改实验
实验目的:
1.      深入理解Linux启动过程;
2.      修改Linux内核,让Linux启动后不执行init进程,而执行自己编写的程序。
实验原理:
1.      Linux的启动有一个清晰的过程,基于Linux的开放性,修改其内核是完全可能的;
2.      当内核被引导并进行初始化之后,内核就可以启动自己的第一个用户空间应用程序init进程。
实验内容:
1.      Linux内核初始化过程
l        内核映像:
在Bootloader的第二阶段会调用内核镜像,当内核映像被加载到内存中,并且Bootloader阶段 2 的引导加载程序释放控制权之后,内核阶段就开始了。内核映像并不是一个可执行的内核,而是一个压缩过的内核映像。在这个内核映像前面是一个例程,它实现少量硬件设置,并对内核映像中包含的内核进行解压,然后将其放入高端内存中,如果有初始 RAM 磁盘映像,就会将它移动到内存中,并标明以后使用。然后该例程会调用内核,并开始启动内核引导的过程。
l        内核引导过程:
        当 bzImage(用于 i386 映像)被调用时,我们从 /XSBase255_Linux_C/Kernel/2.4.18-rmk7-pxa1-XSBase/arch/i386/boot/head.S 的 start 汇编例程开始执行。这个例程会执行一些基本的硬件设置,并调用 /arch/i386/boot/compressed/head.S 中的 startup_32 例程。此例程会设置一个基本的环境(堆栈等),并清除 Block Started by Symbol(BSS)。然后调用一个叫做 decompress_kernel 的 C 函数(在 /arch/i386/boot/compressed/misc.c 中)来解压内核。当内核被解压到内存中之后,就可以调用它了。这是另外一个 startup_32 函数,但是这个函数在 /arch/i386/kernel/head.S 中。
       在这个新的 startup_32 函数(也称为清除程序或进程 0)中,会对页表进行初始化,并启用内存分页功能。然后会为任何可选的浮点单元(FPU)检测 CPU 的类型,并将其存储起来供以后使用。然后调用 start_kernel 函数(在 init/main.c 中),它会将您带入与体系结构无关的 Linux 内核部分。实际上,这就是 Linux 内核的 main 函数。
    通过调用 start_kernel,会调用一系列初始化函数来设置中断,执行进一步的内存配置,并加载初始 RAM 磁盘。最后,要调用 kernel_thread(在 arch/i386/kernel/process.c 中)来启动 init 函数,这是第一个用户空间进程(user-space process)。最后,启动空任务,现在调度器就可以接管控制权了(在调用 cpu_idle 之后)。
l        深入分析内核源代码
    根据上面对内核引导过程的分析,我们可以进入Linux内核源代码跟踪其具体过程。这里我们将图示从./arch/i386/kernel/head.S的Startup_32开始的过程。
call SYMBOL_NAME(start_kernel)

head.S第270行 
        


asmlinkage void __init start_kernel(void)
./init/main.c第545行,函数最后(第628行)调用rest_init();
static void rest_init(void)
int kernel_thread(int (*fn)(void *),
void * arg, unsigned long flags)
./init/main.c第533行:
static void rest_init(void)
{
       kernel_thread(init, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGNAL);
       unlock_kernel();
       current->need_resched = 1;
       cpu_idle();
}
 
 



























                            内核引导过程图示

                     在下一页我们可以看到kernel_thread函数的定义,我们关心的是                     rest_init函数中对kernel_thread的调用,从上面我们可以看到这样的语句:     
              kernel_thread(init, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGNAL);
                     在这里,init当作函数指针传给kernel_thread,这样init将被执行,也就         成为Linux启动后第一个用户进程。可以设想,如果我们将自己的函数指针传        给kernel_thread,并作适当的初始化,就可以实现我们实验的目的。

/XSBase255_Linux_C/Kernel/2.4.18-rmk7-pxa1-XSBase/arch/i386/kernel/process.c第488行开始:
int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
{
       lo