简析linux内核的执行流程(从bootsect.s到启动结束)

简析linux内核的执行流程

----bootsect.smain.c(内核版本0.11

Linux启动的第一阶段(从开机到main.c
3
个任务:

A、启动BIOS,准备实模式下的中断向量表和中断服务程序。

B、从启动盘加载操作系统程序到内存。

C、为执行32main函数做过渡准备。

内存变化如下:

① 、0xFE0000xFFFFFBIOS启动块,其中上电后第一条指令在0xFFFF0

② 、而后0x000000x003FF总共1KB存放中断向量表,而接下去的地址到0x004FF256B存放BIOS数据,从0x0E05B开始的约8KB的内存中存放中断服务程序。

③ 、利用BIOS中断0x19h把硬盘的第一扇区bootsect.s的代码加载到内存中,即0x07c00处,后转到该处执行。

④ 、将bootsect.s的代码复制到0x90000处。

⑤ 、利用中断0x13hsetup.s程序加载到内存0x90200处。

⑥ 、再将剩余的约240个扇区的内容加载到0x10000~0x2EFFF处。

⑦ 、开始转到setup.s处执行,第一件事就利用BIOS提供的中断服务程序从设备上获取内核运行的所需系统数据并存在0x90000的地址处,这时将原来bootsect.s的代码覆盖得只剩2Byte的空间。

⑧ 、关中断并将系统代码复制到0x00000处,将原来放在这里的中断向量表与BIOS数据区覆盖掉,地址范围是0x00000~0x1EFFF。同时制作两表与两寄存器。

⑨ 开地址线A20,寻址空间达到4GB,后对8259重新编程,改变中断号。

⑩ 、转到head.s(大小是25K+184B)执行,执行该程序完后是这样的:

0x00000~0x04FFF:页目录与4个页表,每一项是4KB,20KB

0x05000~0x05400:1KB的空间是软盘缓冲区;

0x05401~0x054b8:184B没用;

0x054b9~0x05cb8:共2KB的空间存中断描述符表;

0x05cb9~0x064b8:共2KB的空间存全局描述符表;

之后就是main函数的代码了!

第二阶段、从main.c函数到系统准备完毕阶段。

第一步:创建进程0,并让进程0具备在32位保护模式下载主机中的运算能力。流程是:(红色字体的是流程,加粗字是注释)

复制根设备和硬盘参数表 main.c中的102110111行)

                  

物理内存规划格局main.c112~126行,其中有

             rd_init函数定义在kernel/ramdisk.c中,此函数用于虚拟盘初始化;而mem_init函数是用于内存管理结构 初始化,定义在mem/memory.c中,该函数页面使用 

虚拟盘设置与初始化 次数均设置成100,然后再依据主内存的 起始位置和终止位置将处于主内存的所有页面的使 用次数全部清零,系统以后把使用次数为0的页面 视为空闲页面。)

内存管理结构mem_map初始化   

  

 

 

异常处理类中断服务程序挂接(在main.c127行,trap()函数定义

            kernel/trap.c,目的是将各种中断与中断描述符表挂接。)

 

 

初始化块设备与字符设备请求项结构(在main.c128129行,blk_dev_init() 定义在 kernel/blk_dev/ll_rw_blk.c

chr_dev_init()定义在kernel/chr_dev/tty_io.c

将串口与显示器外设的中断服务程序挂接(在main.c130行,

  tty_init()定义在kernel/chr_dev/tty_io.c

 

 

开启时间设置(在main.c131行,time_init()定义于main.c函数

  76startup_time

系统开始激活进程0(在main.c131行,sched_init()定义在kernel/

  sched.c函数中,该函数实现进程相关事务设置

依据时钟中断设置,系统调用服务程序挂接。系统调用函数是对用户程序的最基本支持,利用的是

进程相关事务初始化设置 系统调用软中断,详细见下面讲

  的。)

 

 

时钟中断设置

 

 

 

系统调用服务程序挂接

 

 

 

初始化缓冲区管理结构(在main.c133行,               buffer_init(buffer_memory_end)定义fs/buffer.c

 

初始化硬、软盘main.c134135行,hd_initfloppy_init定义kernel/blk_drv/hd.ckernel/blk_drv/floppy.c

开中断main.c136行,sti())

第二步:以进程0为母本创建进程1,使进程1不仅仅具备进程0所拥有的能力,而且还能以文件的形式与外设进行数据交互。流程是:

操作系统为进程0创建进程1做准备  main.c137行,move_to_

user_mode()定义在include/asm/system.h,实现从

内核态到用户态。进程0正式开始执行,而后

执行main.c138行的“if(!fork())”,开始创建

在进程槽中为进程1申请一个空闲位置并获取进程号

进程1,此时将执行unisted.h中的syscall0宏函数

,得到一个编号,对于fork函数,其值是2,具体

在这个程序中的第62行有定义,然后执行软中断

复制进程0的信息之前,先将一些数据压栈

  ,进入系统调用阶段,跳到内核态,执行 kernel/system_call.s中的代码,将一些寄存器的值压栈后,通过刚才在unisted.h中给eax赋值

初步设置进程1管理结构2偏移值在系统调用sys_call_table 中找到sys_fork函数,跳到该函数执行。进 入后首先申请一个空闲位置并获取进程号。

这同样在system_call.s函数中的sys_fork

进程0创建进程1的过程中发生时钟中断 中的_find_copy_

  process,再跳到该函数的定义处kernel/

fork.c中,后返回到sys_fork中,在

从中断返回 复制进程信息前,再将一些数据压栈,

  此时有一个寄存器的值和前面的不一样,

那就是eax,此时是1,它从task[64]

得到的。之后执行copy_process,跳到

调整进程1的管理结构 定义处kernel/fork.c中,设置进程1

  管理结构。假如此时发生时钟中断,系统

会响应并执行kernel/system_call.s函数

中的timer_interrupt定义处,先压栈后

设置进程1的线性地址空间及物理页面 进入kernel/sched.c

do_timer函数,别忘了此时仍在进程

0执行,然后便发现时间片还没完,所以

跳出,并返回到ret_from_sys_call。这是

继续调整进程1的管理结构kernel/system_call.s中定义的,

接着直接将刚才的压栈数据出栈,继续完成

刚才创建进程1的任务,即在fork.c中继续

调整进程1的管理结构,同时设置进程

进程0准备切换到进程11的线性地址空间及物理页面,直到

执行到这条语句“p->state=TASK_

RUNNINGreturn last_pid;”,标志着

系统切换到进程1执行 进程1创建完成。后跳出copy_process.c

函数,返回到system_call.s,将压栈的

进程1开始执行 的寄存器值出栈,此时eax1。后中断返回

  ,进程由从内核态变为用户态,即到了 unisted.h的“if(_res>=0)”这条指令中,

_res的值就是eax的值,一判断成立,就返

进程1开始以数据块的形式操作硬盘 回该值。最后回到了main.c

  的“if(!fork())”中,一判断不成立,跳出执 行下一条指令“for(;;)pause();”,同理

Pause函数也和fork函数一样,这里就不

将找到的缓冲块与请求项挂接 讲了,进入sys_pause()后将进程0

置为可中断等待状态,并调用在kernel

/sched.c定义的schedule()函数切换进程。

进程切换中断返回后执行了第一条语句

将请求项与硬盘处理函数挂接 是“if(_res>=0)”,一判断,

  刚存的eax值为0,返回到“if(!fork())

判断为真,执行init()函数,这在main.c

定义。进入init.c后其程序执行流程见附录,

进行硬盘读盘前的工作准备 各个程序执行目的正如左边写的一

样。

 

给硬盘下达读盘命令

进程1由于等待读盘操作挂起

系统切换到进程0执行

进程0执行过程中发生硬盘中断

 硬盘中断服务程序响应后,进程0继续执行

再次响应硬盘中断,并唤醒进程1

 读盘操作完成后进程1继续执行

 进程1继续设置硬盘管理结构

 进程1获取软盘超级块,为加载根文件系统做准备

 进程1备份超级块数据

 进程1将根文件系统从软盘拷贝到虚拟盘

进程1开始加载根文件系统

进程1准备加载根文件系统超级块

进程1继续加载根文件系统

进程1准备获取根目录节点

进程1加载根目录节点

进程1结束加载根文件系统的过程

进程1与内核文件表挂接,为打开文件做准备

确定打开操作的起点

获取枝梢i节点---dev目录文件的i节点

确定dev的目录文件i节点为枝梢节点

继续返回枝梢i节点

查找tty0文件的i节点

tty0设备文件的i节点,返回给sys_open系统调用

分析tty0文件i节点

设置文件管理结构并返回给用户进程

进程复制tty0文件句柄

进程1继续复制tty0文件句柄

第三步:以进程1为母本创建进程2,使进程2在全面具备进程1所拥有的能力和环境的基础上,进一步具备支持”人机交互“的能力,最终实现准备阶段完成。流程如下:

进程1准备创建进程2  与进程0创建进程1一样,创建进程2,复

                     制进程1的管理结构,复制页表、页目录

                     项等,创建进程2后即执行到init/main.c

复制进程2的管理结构并进行调整  176行与186行,分别是:if(!(pid

                     =fork())中子进程即进程2执行,而父进程(

                     进程1)执行186行的if(pid>0),父进程

调整进程2管理结构中与文件有关的内容   进入wait函数,等待!

                     进入wait函数后还做了一些动作:首先

                     查找自己的子进程,确定是进程2后,判断

进程1执行过程中发生时钟中断  进程2是否处于终止状态或是

                     僵死状态,一判断不是,就将进程1设置为

                     可中断等待状态,继而调用schedule()函数

进程1从时钟中断返回   准备切换到进程2执行   切换到进程2

                      进入进程2后开始加载shell程序。首先执

                     init/main.c180行的“close(0);”,就是

进程1查找它自己的子进程  解除filp[20]file_table[64]的第一项

 关系,这是从进程1继承过来的。然后

                         是“if(open("etc/rc",O_RDONLY,0))”,

对进程2的状态进行处理   其中是将进程2的管理结构指针表filp

                      [20]的第一项与file_table[64]的第一项建立

                      一个关于资源配置的文件,而后再执行

切换到进程2执行     execve函数,并映射到sys_execve系统调用。

                      再调用do_execve函数,目的便是将参数

                      与环境变量加载到主内存的页面中去,中断

为打开/etc/rc文件做准备  返回,但此时在该函数里已经将EIP

                     值指向了shell程序的入口地址,所以返回后

                     执行的是shell的第一条指令,但这时却发现

进程2打开”/etc/rc“配置文件  内存中没有这一条指令的映射,

                     所以发生“缺页中断”,然后调用page_fault

                    函数中的do_no_page

通过压栈为加载shell文件做准备

 

 

为参数和环境变量设置做准备

 

 

得到shell文件i节点

 

 

 

为加载参数和环境变量做准备

 

 

根据i节点,为shell文件进行检测

 

 

检测shell文件头

 

 

备份文件头并进行分析

 

 

shell文件进行进一步分析

 

 

拷贝参数与环境变量

 

 

调整进程2的管理结构

 

 

继续调整进程2的管理结构

 

 

释放进程2继承的页面

 

 

检测协处理器

 

调整shell程序所在的线性地址空间

 

 

shell程序准备参数与环境变量

 

 

继续调整进程2的管理结构

 

 

调整EIP,使其指向shell程序的入口地址

 

 

Shell程序执行发生缺页中断

 

 

缺页中断中shell程序加载前检测

 

 

为即将载入的内容申请页面

 

 

shell载入新获得的页面

 

 

根据shell进程的情况,调整页面内容

 

 

将线性地址空间与程序所在的物理页面相对应

                         Shell程序开始执行,执行的内容是/etc/

                        rc里面的命令,其中便有创建update

Shell进程准备创建update进程 程,该进程是为同步缓冲区与硬盘而

                          设的进程,它会时不时的进入睡眠状

                          态,经过一定的时间再被唤醒然后更新

进程2开始执行/etc/rc文件  缓冲区与硬盘的数据。之后创建完成后

                     Shell进程退出,退出操作当然得释放它占

                     用的资源或者是清除掉,或者是将使用次数

准备加载update进程    1。然后为进程1找到一个子进程,

                    那就是update进程了,发信号告诉进程1

                    shell进程退出,进程1唤醒执行,重新进入

Shell程序检测“etc/rc”文件  main.c函数中执行180行后面的程序

                     再次创建shell进程,现在进程号是4,同样

                      与之前的一样,但此时稍微有点不同的是

Shell进程退出           open的参数是/dev/tty0,这是一个设备

                   文件,与之前的/etc/rc文件不一样,致使这次

                    创建完毕后并不会退出shell进程了,这段区

Shell进程退出善后处理  别是在以下的附录程序中。这样就是完成

                      了所有准备工作,接下去的除了安装文件

                       系统就没有再别的事了,此时shell

进程1清理shell进程管理结构  程序就等着用户输入命令然后执行,

                      剩下的所有操作就是留给用户自己了,一旦

                      用户执行什么命令,包括在shell中输入,

 系统开始重建shell     以及点击应用程序图标,都通过一定的方

                      式映射到内核中执行             

至此系统可以开始正常运转了!!!!!!!!!

参考书籍:《linux内核设计的艺术》新设计团队(著)

linux内核完全注释》陈炯(著)

简析linux内核的执行流程(从bootsect.s到启动结束)

      简析linux内核的执行流程(从bootsect.s到启动结束)

              简析linux内核的执行流程(从bootsect.s到启动结束)

附录2fs/sys_read(unsigned int fd,char *buf,int count)

{

......

68 if(S_ISCHR(inode->i_mode)

69return rw_char(READ,inode->i_zone[0],buf,count,&file->f_pos); 

......

72 if(S_ISDIR(inode->i_mode)||S_ISREG(inode->i_mode)){

73   if(conut+file->f_pos>i_size)

.........

69行是说如果读的文件是字符设备文件,那么返回读的字符串,72行则是如果读的是一般文件,另作处理,在这里第一次读的是一般文件,返回值是ERROR,第二次就不一样了,所以就不会出现退出的情况!


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值