x86 系统中的保护模式,给系统的安全性提供了很大的保障,但是在我们之前的文章中,一直都淡化了特权级别这个概念。
本文以linux0.11版本为基础,分析进程的内存布局,现代版本已经发生比较大的变化,都是很多原理都是类似的。 系统维护了一个全局的数据结构叫GDT( Global Descriptor Table),他保存了所有进程的代码段数据段的一些信息。系统有专门的寄存器保存了GDT的地址,叫GDTR。GTDR的格式如下。
Intel 微处理器的段机制是从8086 开始提出的, 那时引入的段机制解决了从CPU 内部 16 位地址到20 位实地址的转换。为了保持这种兼容性,386 仍然使用段机制,但比以前复杂。 因此,Linux 内核的设计并没有全部采用Intel 所提供的段方案,仅仅有限度地使用 了一下分段机制。这不仅简化了Linux 内核的设计,而且为把Linux 移植到其他平台创造了 条件,因为很多RISC 处理器并不支持段机制。但是,对段机制相关知识的了解是进入Linux 内核的必经之路。
这一篇大致说一下进程的创建,有兴趣的可以参考之前的一些文章或者直接上代码https://github.com/theanarkh/read-linux-0.11。
---- 保护模式 什么实模式和保护模式 这是CPU的两种工作模式,解析指令的方式不同。 在实模式下,16位寄存器需要通过段:偏移的方法才能达到1MB的寻址能力。 物理地址 = 段值 x 16 + 偏移 此时段值还可以看成地址的一部分,段值为XXXXh表示以XXXX0h开始的一段内存。 在保护模式下,CPU有着巨大的寻址能力,并为操作系统提供了虚拟内存和内存保护。 虽然物理地址的仍然用上面的公式表示,但此时“段”的概念发生了变化,它变成了一个索引,指向一个数据结构的一个表项,表项中详细定义了段的
进程的地址有三种,分别是虚拟地址(逻辑地址)、线性地址、物理地址。在分析之前先讲一下进程执行的时候,地址的解析过程。在保护模式下,段寄存器保存的是段选择子,当进程被系统选中执行时,会把tss和ldt等信息加载到寄存器中,tss是保存进程上下文的,ldt是保存进程代码和数据段的首地址偏移以及权限等信息的。假设当前执行cs:ip指向的代码,系统根据ldt的值从gdt中选择一个元素,里面保存的是idt结构的首地址。然后根据cs的值选择idt表格中的一项,从而得到代码段的基地址和限长,用基地址加上ip指向的偏移得到一个线性地址,这个线性地址分为三个部分,分别是页目录索引,页表索引,物理地址偏移。然后到页目录吧和页表中找到物理地址基地址,再加线性地址中的偏移部分,得到物理地址。下面我们看看这些内容是怎么设置的,使得执行的时候能正确找到我们想要的地址去执行代码。我们从fork函数开始。到进程被调度执行时所发生的事情。fork函数的具体调用过程之前已经分析过。下面贴一下主要的代码。
经过多篇文章的介绍,我们对 32 位保护模式已经有了很深的认识,尤其是分段机制以及由此带来的对内存的保护。 进军保护模式 保护模式进阶 — 再回实模式
注:本分类下文章大多整理自《深入分析linux内核源代码》一书,另有参考其他一些资料如《linux内核完全剖析》、《linux c 编程一站式学习》等,只是为了更好地理清系统编程和网络编程中的一些概念性问题,并没有深入地阅读分析源码,我也是草草翻过这本书,请有兴趣的朋友自己参考相关资料。此书出版较早,分析的版本为2.4.16,故出现的一些概念可能跟最新版本内核不同。
PS:原先实模式下的各个段寄存器作为保护模式下的段选择器,80486中有6个(即CS,SS,DS,ES,FS,GS)80位的段寄存器。由选择器CS对应表示的段仍为代码段,选择器SS对应表示的段仍为堆栈段。
每一个进程都有一张段表LDT。整个系统有一张GDT表。且整个系统仅仅有一个总页表。
以上就是对GDT表或者 LDT表的描述 总结来说 GDT或者LDT 就是一块内存. 也可以看成一个数组. 数组的每一项其实保存的都是段描述符 段选择子就是下标 3.1.2 GDTR寄存器与GDT表了解. 根据Inter手册所属. GDTR寄存器 保存了 GDT的 32位基地址 和16位表界限 基地址指的就是GDT从0字节开始的线性地址.可以理解为就是数组首地址. 表界限.可以理解为就是数组的大小. 所以说GDTR 寄存器是一个48位寄存器 按照C语言结构来来表是就如下 struct GDTR { DWORD *GdtBase, SHORT limit; } LGDT 与SGDT 汇编指令 分别是用来获取和保存 GDTR寄存器的. 电脑开机之后,通电之后.GDT就开始初始化了. 总结:
因为程序是分段在内存中存放的,因此需要额外的空间记录每个段的存放位置和占用大小,这就引出了段表,这里的段表又被称为LDT表,每个进程都对应一个LDT表:
此前我们对操作系统中的分段、分页机制以及虚拟地址、逻辑地址、线性地址、物理地址进行了较为详细的介绍。 操作系统的内存管理 — 分段与分页、虚拟地址、逻辑地址、线性地址、物理地址
http://bbs.chinaunix.net/thread-2083672-1-1.html
不论是在 x86 平台上,还是在嵌入式平台上,系统的启动一般都经历了 bootloader 到 操作系统,再到应用程序,这样的三级跳过程。
进程,这个词大家应该耳熟能详了,那进程是什么呢?我们说程序一般是外存上的一个可执行文件,而进程就是这个可执行文件在内存中的一个执行实例。概念始终只会是一个抽象的概念,进程系列文章通过 $xv6$ 的实例来将进程这个概念具象化。本篇主要介绍进程涉及到的一些数据结构,废话不多说,直接来看
分段内存段间的内存空间太大(16位64k),碎片太多,段+偏移转换为线性地址后,通过分页管理,映射到新的地址空间,页目录+页表+页内偏移(12位4k),减小内存间隙的大小
上一节,我们开发了一个流氓程序,当他运行起来后,能够把自己的数据写入到另一个进程的数据内存中。之所以产生这样的漏洞,是因为被入侵进程的数据段所对应的全局描述符在全局描述符表中。恶意程序通过在全局描述符表中查找,当找到目标程序的内存描述符后,将对应的描述符加载到自己的ds寄存器里,于是恶意程序访问内存时,就相当于读写目标程序的内存。 要防范此类入侵,最好的办法是让恶意程序无法读取自己内存段对应的描述符,但是如果不把自己的内存描述符放置在全局描述符表中的话,还能放哪里呢?Intel X86架构还给我们提供了另一
---- 我们可以把一个单独的任务所用到的所有东西封装在一个LDT中,这种思想是多任务处理的雏形。 多任务所用的段类型如下图,使用LDT来隔离每个应用程序任务的方法,正是关键保护需求之一:
很多小伙伴在学操作系统的时候,学习到内存管理的部分时,都会接触到分段内存管理、分页内存管理。
经过一系列的文章,我们通过汇编语言,体验了保护模式下分段、分页、特权级跳转、中断、异常等机制。 那么,事到如今,你是否已经深谙保护模式的设计之道了呢?究竟什么是保护模式,保护模式又在“保护”什么呢?他为了什么诞生,又和实模式有什么区别呢? 本文我们就来详细总结一下。
本文讲解系统的进程管理相关内容,系统的进程管理是有关系统的所有进程的调度、排序、分配资源、创建、销毁等,是比较重要的内容。
学习Windows程序设计也有一些时间了,为了记录自己的学习成果,以便以后查看,我希望自己能够坚持写下一系列的学习心得,对自己学习的内容进行总结,同时与大家交流。因为刚学习所以可能有的地方写不不正确,希望大家能够指出。
操作系统接口并不是直接暴露给用户使用的,用户是通过应用软件间接调用到操作系统接口的。
理论上来讲,可以把那些可靠性介于操作系统和应用程序之间的程序安排在这两个特权级上。
操作系统 内存使用与分段--10 如何让内存用起来? 那就让首先程序进入内存 重定位: 修改程序中的地址(是相对地址) 程序载入后还需要移动… 重定位最合适的时机 - 运行时重定位 整理一下思路 引入
硬件逻辑设计为加电瞬间强行设置:CS=0xF000,IP=0xFFF0,CS:IP=0xFFFF0
这里说的物理地址是内存中的内存单元实际地址,不是外部总线连接的其他电子元件的地址!
所以执行fork函数就会执行system_call函数,但是在这之前,还有些事情需要做,就是保存现场。下面是操作系统执行系统调用前,在内核栈里保存的寄存器,这个压入的寄存器和iret中断返回指令出栈的寄存器是对应的。其中ip指向的是调用系统调用返回后的下一句代码。
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u014688145/article/details/50608829
看完了进入内核前的工作后,我网络编程课的抄写作业自然是可以圆满完成啦,不过看了一部分后觉得确实很有意思,所以也是决定继续看下去,并且计划看完linux源码后跟着MIT6.s081写一个小的操作系统内核,希望我能够在6.29之前完成这个工作哈哈也就是我开始军训之前,补军训确实是个令人苦恼的事情。
进入 /home/moocos/ucore_lab/labcodes_answer/lab1_result 目录下
进程是对逻辑的抽象,我们从操作系统的书籍中对进程有了很多的认识,但是对进程的实现可能不太了解,这篇文章尝试解释一下关于进程实现的大致原理。 进程的实现,其实和我们平时写代码的时候一样,比如我们要表示一个东西,我们会定义一个数据结构。进程也不例外。所以进程的本质就是一个数据结构,他保存了一系列的数据。操作系统以数组或者链表的形式和全部的进程管理起来。进程可以说分为两种 1 系统初始化时第一个进程, 2 除了第一个进程外的其他进程,他们都是由fork或者fork+execute系统调用创建出来的。 我们首先看一下进程的结构体都有什么信息。
之前所说 GDT表 中存储了一些段描述符. 比如有调用门 段描述符. 代码段段描述符. 数据段段描述符 TSS段段描述符
中断描述符表是保护模式下用于存储中断处理程序的数据结构。CPU在接收到中断时,会根据中断向量在中断描述符表中检索对应的描述符。
GDT在内存中的地址和大小存放在CPU的gdtr控制寄存器中,而LDT则在ldtr寄存器中。
80386的各种寄存器一览:通用寄存器(32位)、段寄存器(16位)、标志寄存器(32位)、系统地址寄存器、调试寄存器和测试寄存器(32位)。
分段让操作系统具备了对内存的保护能力,通过描述符表、选择子的多级跳转,让每一段内存都增加了一系列属性,从而可以实现读、写、执行等权限以及为不同程序赋予不同特权的保护功能。 在此前的文章中,我们已经提到,通过 LDT 来解决进程间内存独立的问题,其代价是寄存器的反复加载,这对于 CPU 来说是一件较为耗时的操作,于是,80386 开始,Intel 引入了内存分页功能,相比于 LDT,更为灵活高效,因此 LDT 已经基本不会被使用了。 那么,分页究竟是一种什么样的机制,又是如何实现的呢?本文我们就来一探究竟。
实模式: 指的是操作系统在启动的是否,这时候访问的内存都是实际的物理内存.而在这个是否,操作系统会填写内核中的内中表格.比如今天讲的表(全局描述符表 GDT)
我们的ring3和ring0通讯的时候.ring3会给一个虚拟地址. 然后内核中的参数会通过IRP来获取.
8086CPU以后总线寻址和CPU位数趋于一致,操作系统结构向下兼容,线性地址基址置0:
本文涉及的硬件平台是X86,如果是其他平台的话,如ARM,是会使用到MMU,但是没有使用到分段机制; 最近在学习Linux内核,读到《深入理解Linux内核》的内存寻址一章。原本以为自己对分段分页机制已经理解了,结果发现其实是一知半解。于是,查找了很多资料,最终理顺了内存寻址的知识。现在把我的理解记录下来,希望对内核学习者有一定帮助,也希望大家指出错误之处。
CS部分后面又4个0,相当于是左移了4位。总之就是要让CS左移4位之后加上EIP来得到要跳转的地址。
在前面文章图解中断 | 中断从产生到消失的一生中提到了中断的整个生命周期,其中有一个关键的环节是CPU在接收到中断向量号后是如何找到对应的中断服务程序的,今天我们就来一探究竟。
与硬件相关的代码全部放在 arch(architecture 一词的缩写,即体系结构相关)目录下。
摘 要:本文通过解剖Linux操作系统的虚拟存储管理机制,说明了Linux虚拟存储的特点、虚拟存储器的实现方法,并基于Linux Kernel Source 1.0,详细分析有关虚拟存诸管理的主要数据结构之间的关系。
通过执行g++ -c test.cpp以后生成obj文件,然后通过objdump -d test.o输出编译后的指令得到
中断描述符表简单来说说是定义了发生中断/异常时,CPU按这张表中定义的行为来处理对应的中断/异常。
领取专属 10元无门槛券
手把手带您无忧上云