首页 | Linux 基础 | 资讯动态 | Linux 应用 | Linux 服务器 | Linux 开发 | Linux 安全 | 专题 | 联盟论坛
  当前位置:主页>Linux 开发>linux 内核>文章内容
深入学习LINUX内核之七(图文讲解)
来源:http://www.unix5.com 作者:riechelr_hl 发布时间:2008-04-15  



5.8.1初始化陪段

开机初始化时(bootsect.s,setup.s)

当bootsect代码被ROM BIOS开机载入到实体记忆体0x7c00虞睛,并没有设置堆栈,当然程式也没有使用堆栈。直到bootsect被移勤到Ox9000:O处时,才把堆栈段暂存器SS设置为Ox9000,堆栈指标esp暂存器设置为Oxff00,也即堆栈顶端在Ox9000:0xff00处,参见boot/bootsect.s第61、62行。Setup.s程式中也沿用了bootsect设置的堆栈段。这就是系统初始化时临时使用的堆栈。


进入保护模式时fhead.s

从head.s程式起,系统开始正式在保护模式下执行。此时堆栈段被设置为内核资料段(0x10),堆栈指标esp设置成指向user_stack阵列的顶端(参兄head.s,第3l行),保留了1页记忆体(4K)作为堆栈使用。user_stack阵列定义在sched.c的67- -72行,共含有1024个字。它在实体记忆体中的位置示意图可参见下图5-24所示。此时堆栈是内核程式自己使用的堆栈。其中的给出位址是大约值,它们与编译时的实际设置参数有关。这些位址位置是从编译内核时生成的system.map楷案中查到的。






初始化阶段(main.c)

在init/main.c程式中,在执行move_to_user_mode( )代碣把控制权移交给任务0之前,系统一直使用上述堆栈。而在执行过move_to_user_mode( )之後,main.c的代码被“切换”成任务0中孰行。透过执行fork( )系梳呼叫,main.c中的init( )将在任务1中执行,并使用任务1的堆栈。而main( )本身则在被“切换”成为任务0後,仍然继续使用上述内核程式自己的堆栈作为任务0的用户态堆栈。关于任务0所使用堆栈的详细描述後请见后面说明。

5.8.2任务的堆栈

每个任务都有两个堆栈,分别用于用户态和内核态程式的执行,并且分别称为用户态堆栈和内核态堆栈。除了处于不同CPU特权极中,这两个堆栈之间的主要区别在於任移的内核太堆栈很小,所保存的资料量最多不能超过(4096 – 任务资料结构区块)个位元组,大约为3K位元组。而任务的 用户态堆栈却可以在用户的64MB空同内延伸。


在用戶态执行时

每个任务(除了任务0任务1)有自己的64MB位址空间。当一个任务(行程)刚被建立时,它的用户态堆栈指标被设置在其位址空间的靠近末端(64MB顶端)部分。实际上末端部分还要包括执行程式的参数和环境变数,然后才是用戶堆栈空间,见图5-25所示 。应用程式在用戶态下执行时就一直使用这个堆栈。堆栈实际使用的实体记忆则由CPU分页机制确定。由於Linux实现了写时复制功能(Copy on Write),因此在行程被建立后,若该行程及其父行程都沒有使用堆栈,则两者共用同一堆栈对应的实体记忆体页面。只有当其中一个行程执行堆栈写操作(例如push操作)时內核记忆体管理程式才会为写操作行程分配新的记忆体页面。而行程 0和行程1的用戶堆栈比较特殊,见后面說明。







在内核态执行时

每个任务有其自己的内核态堆栈,用於任务在內核代码中执行期间。其所在线性位址中的位置由该任务TSS段中ss0和esp0两个栏位指定。ss0是任务內核态堆栈的段选择符,esp0是堆栈栈底指标。因此每当任务从用戶代码转移进入內核代码中执行时,任务的內核态堆栈总是空的。任务內核态堆栈被设置在位於其任务资料结构所在页面的末端,即与任务的任务资料结构(task_struct)放在同一页面內。这是在建立新任务时,fork( )程式在任务tss段的內核级堆栈栏位(tss.esp0和tss.ss0)中设置的,参见kernel/fork.c,92行:

p->tss.espO = PAGE_SIZE + (1ong)p ;
p->tss.ssO = 0x10 ;

其中p是新任务的任务资料结构指标,tss是任务状态段结构。內核为新任务申请记忆体用作保存其task_struct结构资料,而tss结构(段)是task_struct中的一个栏位。该任务的 内核堆栈段值tss.ss0也被设置成为0x10(即內核资料段选择符),而tss.esp0 则指向保存task_struct结构页面的末端。见图5-26所示。实际上tss.espO被设置指向该页面(外)上一位元组处(图中堆栈底处)。这是因为Intel CPU执行堆栈操作时是先递減堆栈指标esp值,然后在esp指标处保存入堆栈內容。






为什麼从主记忆体区申请得来的用於保存任务资料结构的一页记忆体也能被设置成內核资料段中的资料呢,也即tss.ss0为什麼能被设置成0x10呢? 这是因为用戶內核态堆栈仍然属于内核资料空间。我们可以从內核代码段的长度范围来說明。在head.s程式的末端,分別设置了內核代码段和资料段的描述符,段长度都被设置成16MB。这个长度值是Linux 0.12內核所能支持的最大实体记忆体长度(参见head.s,110行开始的注释)。因此,內核代码可以定址到整个实体记忆体范围中的任何位置,当然也包括主记忆体区。每当任务执行內核程式而需要使用其內核堆栈时,CPU就会利用TSS结构把它的內核态堆栈设置成由tss.ss0和tss.espO这两个值构成。在任务切換时,老任务的內核堆栈指标esp0不会被保存。对CPU来讲,这两个值是唯读的。因此每当一个任务进入內核态执行时,其內核态堆栈总是空。


任务0和任务1的堆栈

任务0(空閒行程idle)和任务1(初始化行程init)的堆栈比较特殊,需要特別予以說明。任务0和任务1的代码段和资料段相同,限长也都是640KB,但它们被映射到不同的线性位址范围中。任务0的段基底位址从线性位址。开始,而任务1的段基底位址从64MB开始。但是它们全都映射到实体位址O- -640KB范围中。这个位址范围也就是內核代码和基本资料所存放的地方,在执行了move_to_user_mode( ),任务O和任务1的內核态堆栈分別位於各自
任务资料结构所在页面的末端,而任务0的用戶态堆栈就是前面进入保护模式后所使用的堆栈,即sched.c的user_stack[]阵列的位置。由于任务1在建立时复制了任务0的用戶堆栈,因此刚开始时任务0和任务l共用使用同一个用戶堆栈空间。但是当任务1开始执行,由于任务1映射到user_stack[]处的页表项被设置成唯读,使得任务l在执行堆栈操作时将会引起写页面異常,从而內核会使用写时复制机制²为任务1另行分配主记忆体区页面作为堆栈空间使用。只有到此时,任务1才开始使用自己独立的用戶堆栈记忆体页面。因此任务0的堆栈需要在任务1实际开始使用之前保持“干淨”,即任务0此时不能使用堆栈,以确保复制的堆栈页面中不含有任务0的资料。
共12页: 上一页 [1] [2] [3] [4] [5] [6] 7 [8] [9] [10] [11] [12] 下一页
 
如果您对本文有任何疑问或者建议,请到论坛讨论区发表您的意见: >> 论坛入口
[收藏] [推荐] [评论(0条)] [返回顶部] [打印本页] [关闭窗口]  
  热点文章
·使用 Linux 系统调用的内核命令
·Linux 2.6.11内核文件IO系统调用
·Linux操作系统的源代码目录树结
·Linux用户态与内核态的交互讲解
·Linux内核对I/O端口的管理实现(
·深入分析 Linux操作系统的内核链
·Linux内核可装载模块对设备驱动
·概述Linux系统的驱动框架及驱动
·详解Linux 2.6内核新文件系统变
·Linux系统可卸载内核模块完全指
·FreeBSD手册讲解(一)--配置FreeB
·编译Linux操作系统的内核讲解
  相关文章
·深入学习LINUX内核之六(图文讲解
·深入学习LINUX内核之五(图文讲解
·深入学习LINUX内核之四(图文讲解
·Linux系统内核漏洞分析
·深入学习LINUX内核之三(图文讲解
·如何在Linux内核中的实现SYN Coo
·深入学习LINUX内核之二(图文讲解
·简析Linux与FreeBSD的syscall
·深入学习Linux内核文档一(图文讲
·Linux操作系统“警惕”内核汉化
·Linux操作系统核心的汉字显示机
·如何利用异常表处理Linux内核态

本站信息源至:互联网络,均为学习,交流所用,如有版权问题,请联系我们.
站长QQ:397422079 E_mail:riechelr_hl@unix5.com
转载本站内容请注明原作者名.谢谢!