Linux内核源代码分析一(Linux0.12)

Linux内核源代码分析一(Linux0.12)

Linux 内核主要由 5 个模块构成,它们分别是:进程调度模块、内存管理模块、文件系统模块、进程间通信模块和网络接口模块。

1.引导启动程序(boot)

首先说一下pc机的启动流程,在加电后,80X86会先进入实模式并进入地址0XFFFF0开始自动执行代码,这个地址一般带表了BIOS的代码地址,PC的BIOS将会执行硬件检测和诊断功能,并在0地址进行中断向量的初始化,最后BIOS会将启动设备的第一个扇区读入地址0X7C00位置,并从这个位置开始执行代码。

boot目录下包含三个汇编代码文件

Bootsect.s

bootsect文件是在记录在磁盘的第一个扇区中,BIOS系统会将其加载到0X7C00的位置并执行。

在bootsect.s文件中做的主要的事情如下:

1.将自身移动到0X9000位置,这一步主要是为了后面讲head和setup的代码放到boot sect上面

entry start
start:
   mov    ax,#BOOTSEG
   mov    ds,ax
   mov    ax,#INITSEG
   mov    es,ax
   mov    cx,#256
   sub    si,si
   sub    di,di
   rep
   movw
   jmpi   go,INITSEG

2.是利用 ROM BIOS 中断 INT 0x13 将 setup 模块从磁盘第 2 个扇区开始读到0x90200 开始处,共读 4 个扇区。在读操作过程中如果读出错,则显示磁盘上出错扇区位置,然后复位驱动器并重试,没有退路。

load_setup:
   xor    dx, dx       ! drive 0, head 0
   mov    cx,#0x0002    ! sector 2, track 0
   mov    bx,#0x0200    ! address = 512, in INITSEG
   mov    ax,#0x0200+SETUPLEN    ! service 2, nr of sectors
   int    0x13         ! read it
   jnc    ok_load_setup     ! ok - continue

   push   ax       ! dump error code
   call   print_nl
   mov    bp, sp
   call   print_hex
   pop    ax 
   
   xor    dl, dl       ! reset FDC
   xor    ah, ah
   int    0x13
   j  load_setup

ok_load_setup:

3.将 system 模块加载到 0x10000(64KB)开始处

4.检查要使用哪个根文件系统设备

5.到此,所有程序都加载完毕,我们就跳转到被加载在 bootsect 后面的 setup 程序去。

Setup.s

setup.s的主要作用如下:

1.通过BIOS系统调用(0X13)获取系统参数并将系统参数存储在0X90000的位置(原存放bootsect.s的位置)

2.初始化GDT(全局表述表)和IDT(中断表述表)以及GDTR和IDTR寄存器

3.将system模块从bootsect.s加载进来的位置,移动到0X00000的位置

Head.s

head.s被作为system的一部分并置于system的头部,所以被命名为head,其次还负责将eip指向main.c从而执行main.c进行系统中各个模块的初始化。

2.初始化程序(init)

init目录下仅存在一个main.c文件,系统在执行完 boot/head.s 程序后就会将执行权交给 main.c。该程序虽然不长,但却包括了内核初始化的所有工作。

main.c的主体流程可以从main方法来看

1.将setup.s获取的系统参数从0X90080处取出并存到内核的全局变量中

2.计算内存的结束位置,也就是计算memory_end(机器具有的物理内存容量(字节数))的大小

3.根据物理内存容:量计算高速缓冲区的大小(buffer_memory_end)

4.计算主内存的起始位置,main_memory_start代表了主内存的位置

此时内存的分布为:system(内核程序)->高速缓冲区->虚拟盘(如果有的话)->主内存

// 内核初始化主程序。初始化结束后将以任务0(idle任务即空闲任务)的身份运行。
void main(void)       /* This really IS void, no error here. */
{        /* The startup routine assumes (well, ...) this */
/*
 * Interrupts are still disabled. Do necessary setups, then
 * enable them
 */
    // 下面这段代码用于保存:
    // 根设备号 ->ROOT_DEV;高速缓存末端地址->buffer_memory_end;
    // 机器内存数->memory_end;主内存开始地址->main_memory_start;
    // 其中ROOT_DEV已在前面包含进的fs.h文件中声明为extern int
   ROOT_DEV = ORIG_ROOT_DEV;
   drive_info = DRIVE_INFO;        // 复制0x90080处的硬盘参数
   memory_end = (1<<20) + (EXT_MEM_K<<10);     // 内存大小=1Mb + 扩展内存(k)*1024 byte
   memory_end &= 0xfffff000;                   // 忽略不到4kb(1页)的内存数
   if (memory_end > 16*1024*1024)              // 内存超过16Mb,则按16Mb计
      memory_end = 16*1024*1024;
   if (memory_end > 12*1024*1024)              // 如果内存>12Mb,则设置缓冲区末端=4Mb 
      buffer_memory_end = 4*1024*1024;
   else if (memory_end > 6*1024*1024)          // 否则若内存>6Mb,则设置缓冲区末端=2Mb
      buffer_memory_end = 2*1024*1024;
   else
      buffer_memory_end = 1*1024*1024;        // 否则设置缓冲区末端=1Mb
   main_memory_start = buffer_memory_end;
    // 如果在Makefile文件中定义了内存虚拟盘符号RAMDISK,则初始化虚拟盘。此时主内存将减少。
#ifdef RAMDISK
   main_memory_start += rd_init(main_memory_start, RAMDISK*1024);
#endif
    // 以下是内核进行所有方面的初始化工作。阅读时最好跟着调用的程序深入进去看,若实在
    // 看不下去了,就先放一放,继续看下一个初始化调用。——这是经验之谈。o(∩_∩)o 。;-)
   mem_init(main_memory_start,memory_end); // 主内存区初始化。mm/memory.c
   trap_init();                            // 陷阱门(硬件中断向量)初始化,kernel/traps.c
   blk_dev_init();                         // 块设备初始化,kernel/blk_drv/ll_rw_blk.c
   chr_dev_init();                         // 字符设备初始化, kernel/chr_drv/tty_io.c
   tty_init();                             // tty初始化, kernel/chr_drv/tty_io.c
   time_init();                            // 设置开机启动时间 startup_time
   sched_init();                           // 调度程序初始化(加载任务0的tr,ldtr)(kernel/sched.c)
    // 缓冲管理初始化,建内存链表等。(fs/buffer.c)
   buffer_init(buffer_memory_end);
   hd_init();                              // 硬盘初始化,kernel/blk_drv/hd.c
   floppy_init();                          // 软驱初始化,kernel/blk_drv/floppy.c
   sti();                                  // 所有初始化工作都做完了,开启中断
    // 下面过程通过在堆栈中设置的参数,利用中断返回指令启动任务0执行。
   move_to_user_mode();                    // 移到用户模式下执行
   if (!fork()) {    /* we count on this going ok */
      init();                             // 在新建的子进程(任务1)中执行。
   }
/*
 *   NOTE!!   For any other task 'pause()' would mean we have to get a
 * signal to awaken, but task0 is the sole exception (see 'schedule()')
 * as task 0 gets activated at every idle moment (when no other tasks
 * can run). For task0 'pause()' just means we go check if some other
 * task can run, and if not we return here.
 */
    // pause系统调用会把任务0转换成可中断等待状态,再执行调度函数。但是调度函数只要发现系统中
    // 没有其他任务可以运行是就会切换到任务0,而不依赖于任务0的状态。
   for(;;) pause();
}

你可能感兴趣的:(Linux,linux)