Linux0.11系统中堆栈的使用方法

Linux 0.11系统中共使用了四种堆栈
一、系统引导初始化临时使用的堆栈。
二、进入保护模式后提供内核程序始化使用的堆栈,该堆栈也是后来任务0使用的用户态堆栈。
三、每个任务通过系统调用,执行内核程序时使用的堆栈,称之为任务的内核态堆栈,每个任务都有自己独立的内核态堆栈。
四、任务在用户态执行的堆栈,位于任务(进程 )逻辑地址空间近末端处使用多个栈或在不同情况下使用不同栈的主要原因。
一、由于从实模式进入保护模式,使得CPU对内存寻址访问方式发生了变化,因此需要重新设置堆栈区域。
二、为了解决不同CPU特权级共享使用堆栈带来的保护问题,执行0级的内核代码和执行3级的用户代码需要使用不同的栈。当一个任务进入内核态运行时,就会使用其TSS段中给出的特权级0的堆栈指针tss.ss0.tss.esp0,即内核栈,原用户栈指针会保存在内核栈中,而当从内核态返回用户态时,就会恢复使用用户态的堆栈。

以下分别说明:
开机初始化时(bootsect.s,setup.s)
当bootsect代码被ROM
BIOS引导加载到物理内存0x7c00处时,并没有设置堆栈段,程序也没有使用堆栈,直到bootsect被移动到0x9000:0处时,才把堆栈段寄存器SS设置为0x9000,堆栈指针esp寄存器设置为0xff00,所以堆栈堆栈在0x9000:0xff00处(boot/bootsect.s
L61,62)setup.s也使用这个堆栈进入保护模式时候(head.s,L31)此时堆栈段被设置为内核数据段(0x10),堆栈指针esp设置成指向user_stack数组(sched.c L67~72)的顶端,保留了1页内存作为堆栈使用初始化时(main.c)
在执行move_to_user_mode()代码把控制权移交给任务0之前,系统一直使用上述堆栈,而在执行过move_to_user_mode()
之后,main.c的代码被“切换”成任务0中执行。通过执行fork()系统调用,main.c中的init()将在任务1中执行,并使用任务1的堆栈,而main()本身则在被“切换”成为任务0后,仍热继续使用上述内核程序自己的堆栈作为任务0的用户态堆栈。

任务的堆栈
每个任务都有两个堆栈,分别用于用户态和内核态程序的执行,并且分别称为用户态堆栈和内核态堆栈。
除了处于不同CPU特权级中,这两个堆栈之间的主要区别在于任务的内核态堆栈很小,所保存的数据最多不能超过4096个字节,而任务的用户态堆栈却可以在用户的64MB空间中延伸在用户态运行时。
每个任务(除了任务0和任务1)有自己的64MB地址空间,当一个任务(进程)刚被创建时,它的用户态堆栈指针被设置在其地址空间的靠近末端部分,应用程序在用户态下运行时就一直使用这个堆栈,实际物理地址内存则由CPU分页机制确定。

在内核态运行时
每个任务有其自己的内核态堆栈,用于任务在内核代码中执行期间。其所在的线性地址中位置由该任务TSS段中ss0和esp0两个字段指定,任务内核态堆栈
被设置在位于其任务数据结构所在页面的末端,即于任务的任务数据结构(task_struct)放在同一页面中,参见kernel/fork.c L93
p->tss.esp0 = PAGE_SIZE + (long)p;
p->tss.ss0 = 0x10
*为什么从主存区申请得来的用于保存任务数据结构的一页内存也能被设置成内核数据段中的数据呢?就是说tss.ss0为什么可以是0x10?
用户内核态仍然属于内核数据空间,在head.s中设置内核代码段和数据段的描述符,段长度都设置成了16MB,这个长度值是Linux0.11内核所能
支持的最大物理内存长度(head.s,110开始的注释),所以,内核代码可以寻址到整个物理内存范围中的任何位置,当然也包括主存区,每当任务执行内
核程序而需要使用其内核栈时,CPU就会利用TSS结构把它的内核态堆栈设置成由tss.ss0和tss.esp0这两个值构成。

任务0(空闲进程idle)和任务1(初始化进程init)的堆栈
任务0和任务1的代码段和数据段相同,限长都是640KB,但它们被映射到不同的线性地址空间,任务0的段基址从线性地址0开始,而任务1的段基址从64MB开始,但他们全部映射到物理地址0~640KB范围中,这个地址也就是内核代码和基本数据所存放的地方,在执行了
move_to_user_mode()后,任务0和任务1的内核态堆栈分别位于各自任务数据结构所在页面的末端,而任务0的用户态堆栈就是前面进入保护模式后使用的堆栈,即user_stack[]数组的位置,由于任务1在创建时复制了任务0的用户堆栈,所以刚开始时任务0和任务1共享使用同一个用户堆栈空间,但是当任务1开始运行时,写时复制机制会为任务1另行分配主存区页面作为堆栈空间使用,只有到这个时候,任务1才开始使用自己独立的用户堆栈内存
页面,因此任务0的堆栈需要在任务1实际开始使用之前保持干净,即任务0此时不能使用堆栈,以确保复制的堆栈页面中不含任务0的数据。

更多精彩linux视频教程,尽在51CTO学院:

http://edu.51cto.com/course/courseList/id-48.html

时间: 2024-10-03 19:33:11

Linux0.11系统中堆栈的使用方法的相关文章

Linux 系统中堆栈的使用方法

本节内容概要描述了Linux内核从开机引导到系统正常运行过程中对堆栈的使用方式.这部分内容的说明与内核代码关系比较密切,可以先跳过.在开始阅读相应代码时再回来仔细研究. Linux 0.12系统中共使用了4种堆栈.第1种是系统引导初始化时临时使用的堆栈:第2种是进入保护模式之后提供内核程序初始化使用的堆栈,位于内核代码地址空间固定位置处.该堆栈也是后来任务0使用的用户态堆栈:第3种是每个任务通过系统调用,执行内核程序时使用的堆栈,我们称之为任务的内核态堆栈.每个任务都有自己独立的内核态堆栈:第4

虚拟机上在Linux系统中安装JDK的方法

1.   mkdir /soft      (创建一个目录,用于存放安装软件 ) 2.     cd /soft      (切换到soft目录 ) 3. rz(从windows中选择安装包) 4.rmp  -ivh jdk-7u45-linux-x64.rpm   (安装JDK) 5.find / -name 'jdk' 6.vi /etc/profile  (编辑环境变量)  在profile的最后写下面的地址 export JAVA_HOME=/usr/java/jdk1.7.0_45ex

学习Linux系统中命令的简单方法

如果说如何快速学习.了解Linux的话,我的答案是学命令.背命令!为何呢?对于一名新手来说,去学习Linux的思想.了解Linux的架构.明白Linux中"一切皆文件"概念虽然说是没有错,是对的.但是个人认为去学习这些"高大上"的东西不是一时半会的事儿,它需要一定的时间和经验去沉淀才能掌握.那么如何最快速了解Linux并使用呢?我依然觉得学命令.背命令,掌握命令是比较笨但却是比较快的方式. 我开始学习Linux的时候,问了前辈:我入门Linux需要掌握哪些命令呢?前

老男孩教育每日一题-2017年5月12日-磁盘知识点:linux系统中LVM配置实现方法?

1.题目 2.参考答案 01:将一个或多个物理分区创建为一个PV # pvcreate /dev/sdb{1,2} Physical volume "/dev/sdb1" successfully created Physical volume "/dev/sdb2" successfully created # pvs          #<- 查看系统中的PV信息 PV                 VG   Fmt      Attr     PSiz

一站式linux0.11内核head.s代码段图表详解

阅读本文章需要的基础: 计算机组成原理:针对8086,80386CPU架构的计算机硬件体系要有清楚的认知,我们都知道操作系统是用来管理硬件的,那我们就要对本版本的操作系统所依赖的硬件体系有系统的了解,有了系统的了解后才能全面的管理它,我们对8086,80386CPU架构的计算机硬件体系如果有非常深刻的认识,我们看源代码内核的时候,就可以更可能的以一种开发者的角度去思考代码的作用,先从全局的角度去思考问题,而不是采用一种众人摸象的思维从头看到末尾. 计算机编程C语言基础:linux内核基本都是用C

第一次作业:基于Linux0.11操作系统的进程模型分析

1.前言 本文基于Linux0.11操作系统的源代码,分析其进程模型. Linux0.11下载地址:https://zhidao.baidu.com/share/20396e17045cc4ce24058aa43a81bf7b.html 2.进程的定义 程序是一个可执行的文件,而进程(process)是一个执行中的程序实例. 进程和程序的区别: 几个进程可以并发的执行一个程序 一个进程可以顺序的执行几个程序 进程由可执行的指令代码.数据和堆栈区组成.进程中的代码和数据部分分别对应一个执行文件中的

OS10.11系统下 安装cocoapods 以及 安装cocoapods-xcode-plugin-master插件来加载三方框架

OS10.11系统下 安装cocoapods 以及 安装cocoapods-xcode-plugin-master插件来加载三方框架A.安装cocoapods:1.sudo gem update --system 更新gem(1.1 sudo gem uninstall cocoapods 该情况看是否需要卸载cocoapods 一般不必使用该命令) 2.gem source -l http://rubygems.org/ 查看安装ruby环境的路径 3.gem source -a http:/

在苹果Mac OS X 10.8/10.7/10.6系统中显示隐藏文件

在苹果Mac OS X 10.8/10.7/10.6系统中显示隐藏文件的方法为,先在苹果系统桌面空白处点一下,再点最上面的菜单-前往-实用工具-终端,输入以下命令(注意大小写和空格)-- defaults write com.apple.finder AppleShowAllFiles -bool true 要隐藏回去,只需把后面的true改为false: defaults write com.apple.finder AppleShowAllFiles -bool false

从linux0.11中起动部分代码看汇编调用c语言函数

上一篇分析了c语言的函数调用栈情况,知道了c语言的函数调用机制后,我们来看一下,linux0.11中起动部分的代码是如何从汇编跳入c语言函数的.在LINUX 0.11中的head.s文件中会看到如下一段代码(linux0.11的启动分析部分会在另一部分中再分析,由于此文仅涉及c与汇编代码的问题,). after_page_tables: pushl $0 # These are the parameters to main :-) pushl $0 pushl $0 pushl $L6 # re