操作系统是如何保护自己的? CPU与保护模式

ce9cbc11ca41fa81611e05af1abf3a3d.gif

作者 | 陆小凤

在回答这个问题之前,你可能会首先想:为什么操作系统需要保护自己呢?

操作系统其实就是一个大管家,负责给应用程序搭建舞台,好让程序们过好自己的一生,但偏偏有不听话的程序可能想抢操作系统的戏,显然这会影响所有其它正在程序,因此操作系统必须要有能力保护自己。

我们从历史的角度了解了x86最开始是没有任何保护机制的,应用程序竟然可以与操作系统平起平坐,操作系统能读写的内存区域应用程序也一样可以读写,操作系统可以控制的硬件应用程序一样不在话下。

应用程序和操作系统这么平等还何谈保护?其实从某种程度讲,保护自己就是限制别人,那么操作系统该怎样限制应用程序呢?

程序也是分三六九等的

核心就在一点:权限

这一点相信对于打工人都深有体会,在公司里有的文档你无权查看,有的数据库你无权读取,有的门禁你的卡刷不开等等。

这里也是一样的道理,但是操作系统和普通的应用程序都是软件,从本质上讲没有任何区别,在CPU眼里都是机器指令,显然从软件这一层面上看操作系统没有很好的办法能控制应用程序,这就不得不借助硬件的帮忙了,借助谁的帮忙呢?显然是CPU。

我们刚才提到过,不管是操作系统还是应用程序在CPU眼里都是机器指令,CPU闭着眼执行就完事儿,从时间角度上看CPU就是一条又一条的在执行指令:

7f34932c8b8956bcb4ae9a4218926b90.png

然而,CPU也不能对此一点都不关心,CPU必须能区分出哪些指令属于操作系统,哪些指令属于普通的应用程序!

该怎么区分呢?很简单,其中一种方法是这样的,我们添加一些特殊的机器指令,假设是指令A和B,CPU执行到该指令A时就知道接下来要执行的指令属于操作系统,当执行完指令B时就知道接下来要执行的属于普通应用程序,这两条指令在x86(32位)中就是int与iret指令,这两个指令对应的背后就是所谓的系统调用。

有了这样的指令,CPU可以清楚的执行什么时候在执行普通程序,什么是在运行操作系统(程序),CPU能区分清楚那么就能给它们赋予不同的权限,这就是所谓用户态与内核态的由来,用户态对应的是普通程序,内核态对应的是操作系统,它们的权限是不一样的。

f8130cb9229484118d46c062090879ee.png

x86 CPU支持四种权限等级,0,1,2,3,一般的操作系统都使用两种特权0和3,0是最高权限,显然这是操作系统也就是内核态所拥有的权限,而3是普通程序运行的权限,相对较低。

同时,一些指令只有在内核态下才可以执行,这些就是所谓特权指令,当CPU在用户态(普通程序)时是没有办法执行特权指令的,这样就从机器指令这个层面确保了普通程序不能执行某些特权操作。

bcb679a8663ff0518e389901f8f5c17d.png

我们知道程序除了机器指令外还有指令依赖的数据,而数据又是保存在内存中,那么接下来的问题是操作系统该怎样保护自己的内存不会普通程序读写呢?

访问内存也需要权限

答案和我们刚才讲解的机器指令的特权划分是一样的。

我们规定操作系统所在的内存区域只有CPU处于内核态时才可以访问,如果位于用户态的程序试图访问内核所在的地址空间那么将立刻被操作系统kill掉。

那么接下来的问题就是我们该怎样给一段内存添加上权限信息呢?

显然我们需要一张“表”,这张表中记录一段内存区域并且记录下访问这块内存所需要的权限信息,类似这样:

序号   起始地址   长度     所需权限
0     0x7c00    0x1000  0
1     0x9a00    0x2000  3
...

当CPU试图访问这段内存区域时会根据CPU自身所在的权限(内核态或者用户态)与这段内存需要的权限进行比对,只有当CPU所在权限比访问这段内存所需要的权限高或者相等时才能读写这段内存,否则将触发异常。

假设CPU当前正在执行用户态程序,也就是运行在用户态,因此其权限等级为3,此时如果CPU试图访问第0号内存块时发现读写该内存块所需要的权限为0(内核态),这时CPU本身将产生异常,该异常将被操作系统捕获,此时操作系统会发现应用程序试图读写程序不具备权限的内存,因此操作系统手起刀落将该进程kill掉,这样操作系统就保证了自己的内存区域不会被普通程序所读写。

7ef0baa8b6e49f5ee3e6978f4ab5a540.png

就这样操作系统成功保护了自己的内存数据以及机器指令。

现在是时候总结一下了。

为了将操作系统和普通程序区别开来,我们需要给机器指令赋予权限等级,该权限信息会保存在CPU中,显然CPU中需要特定寄存器来保存该信息,于此同时我们也为内存区域赋予了权限等级,只有当前CPU的权限大于或者等于该内存区域所需权限时才能读写,这就要求有一张“表”来保存内存起始地址、长度、权限等信息,这张表就是所谓的Global Descriptor Table,GDT,以及Local Descriptor Table,LDT。

内核所在内存区域以及一些共享内存区域信息就保存在GDT中,这就是叫做Global的原因,而进程所在的内存区域(私有)信息则保存在LDT中,这就是为什么叫做Local。

具备这些能力的x86 CPU就被称为保护模式,Inter处理器从80286开始引入保护模式,可以看到与x86早前的实模式相比,保护模式开始有了质的飞跃。

从实模式到保护模式

我们在之前的文章中说过,x86是一个有着顽强生命力的物种,其它大部分类型的CPU在计算机不长的历史中逐渐消失了,而x86则历久弥新,也因此x86历史包袱十分沉重,即使是最新款的intel x86处理器也可以运行上世纪编写的古老程序,为做到向后兼容,intel x86程序必须既能运行在实模式下也能运行在保护模式下。

b6bcde51af95a54263d890699f52f105.png

因此x86处理器在加电会首先进入实模式然后切换到保护模式,现代操作系统都运行在保护模式下,正是利用了处理器的一系列特性操作系统才得以保护自己。

到目前为止,我们看到的x86内存管理都是基于段式机制,Segmentation来管理内存的,实际上x86处理器在引入保护模式的同时也开始支持页式内存管理(paging),因此现代x86处理器即支持段式内存管理也支持页式内存管理,只不过对于现代操作系统像Linux等实际上几乎不再使用处理器提供的段式内存管理机制而是基于页式内存管理机制。

从这里我们也能看出来,内存管理机制其实是处理器这种硬件提供的,操作系统(软件)只不过这种机制的使用者而已。

好啦,这篇文章就先到这里,实际上这里还有很多内容没有讲解完,GDT、LDT长什么样子?怎么使用?具体该怎样从实模式切换到保护模式等等,这些内容将在后续章节中介绍。

希望这篇文章对大家理解操作系统的保护机制有所帮助。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/511585.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何快速调度 PTS 的百万并发能力

简介:压测是通过模拟用户行为对业务系统发起请求,测算出系统的承载能力,并对系统做一次全面的体检,压测后可根据压测表现优化系统瓶颈,防止出现线上故障。 作者:灵苒 在实际的业务场景中,压测…

matlab字符串数组里里固定格式的内容,字符串数组和字符数组中的文本

用字符串数组表示文本您可以使用 string 数据类型将任何 1n 字符序列存储为字符串。从 R2017a 开始,您可以用双引号将文本括起来以创建字符串。str "Hello, world"str "Hello, world"虽然文本 "Hello, world" 的长度为 12 个字符&am…

架构师说低代码:走出半生,归来仍是“毒瘤”!

当今世界正在经历数字化与智能化的发展蜕变,其中软件开发技术作为推动数字化转型的必要支撑,成为企业变革的重要推手。云原生作为产业数字化的关键技术之一,催生出低代码等软件开发模式,让开发逐渐走向低门槛、迭代快、可扩展。今…

网关流控利器:结合 AHAS 实现 Ingress/Nginx 流量控制

简介:微服务的稳定性一直是开发者非常关注的话题。随着业务从单体架构向分布式架构演进以及部署方式的变化,服务之间的依赖关系变得越来越复杂,业务系统也面临着巨大的高可用挑战。 作者:涂鸦 微服务的稳定性一直是开发者非常关…

matlab vs2010编译器xml,如何在MATLAB R2010a 中使用Visual C++ 2010编译器

1、安装补丁VS2010MEXSupport.zip参考http://www.mathworks.cn/support/solutions/en/data/1-D5W493/index.html?solution1-D5W493由于MATLAB R2010a 发布的时间要比VS2010早,所以在该版本识别不了VC2010编译器。不过R2010a之后的版本应该不会有这种问题了。解压补…

云钉一体加速,阿里云计算巢与钉钉深度融合、共建应用新生态

简介:云合计划再升级,“云钉一体”战略按下加速键。 1月13日,阿里云宣布云合计划再升级,计算巢与钉钉深度融合互通,实现一键上云、一键上钉钉,助力软件厂商更快地完成技术升级,更好地专注于自身…

DevStream 进入 CNCF 沙箱,探索云原生时代的高效 DevOps 实践

2022 年 6 月 15 日,云原生计算基金会 (CNCF) 宣布 DevStream 正式成为 CNCF 沙箱(Sandbox)项目。 DevStream 是一个开源的 DevOps 工具链管理器,可以通过一个简单的配置文件,将软件研发生命周期中各环节的 DevOps 工…

阿里云何万青:南坡VS北坡,阿里云高性能计算行业实践

简介:北坡模式:借助于云上大计算性能突破来提供HPC服务,切入的重点更加聚焦于云服务。 随着数字化转型的深入,行业应用对算力提出更高要求。为满足不同行业灵活的业务形态与计算需求,以云计算技术为服务模式创新的技术…

理论塔板数 matlab,matlab作图法计算精馏理论板数

MATLAB图解法计算精馏塔理论板数中文摘要:双组份精馏是化工生产中重要的单元操作,运用matlab【1】的强大功能,通过绘制精馏段和提留段操作线方程以及进料q线方程,运用图解法计算理论板数和进料板位置,使得求解精确、简…

双引擎驱动Quick BI十亿数据0.3秒分析,首屏展示时间缩短30%

简介:在规划中,Quick BI制定了产品竞争力建设的三大方向,包括Quick(快)能力、移动端能力和集成能力。针对其中的产品“报表查看打开慢”“报表开发数据同步慢”等性问题开展专项战役——Quick战役,以实现展…

Quick BI产品核心功能大图(六)开放集成

简介:企业想要拥有领先的数据分析能力,自研往往需要投入巨大的人力和财力。 Quick BI作为唯一一个连续两年入选Gartner魔力象限的中国BI产品,具备强大的全链路开放集成能力,可以轻松的与企业原有系统匹配融合,帮助企业…

php error_get_last(),PHP捕获异常register_shutdown_function和error_get_last的使用

register_shutdown_function注册一个会在php中止时执行的函数,注册一个 callback ,它会在脚本执行完成或者 exit() 后被调用。error_get_last获取最后发生的错误,包含type(错误类型),message(错误消息),file(发生错误所…

低复杂度 - 服务网格的下一站

作者 | Addo Zhang来源 | 云原生指北译者:作为一个曾经在新造车公司的基础架构团队任职,为支持公司的“互联网基因”和“数字化转型”落地了云原生基础设施平台,并在尝试采用服务网格未成的我来说,看到这篇文章深有感触。尤其是文…

ADBPGGreenplum成本优化之磁盘水位管理

简介:本文我们将通过一个实际的磁盘空间优化案例来说明,如何帮助客户做成本优化。 作者 | 玉翮 来源 | 阿里技术公众号 一 背景描述 目前,企业的核心数据一般都以二维表的方式存储在数据库中。在核心技术自主可控的大环境下,政企…

阿里云图数据库GDB V3引擎发布,加速开启“图智”未来

简介:无论是学术界还是产业界,都对图数据库有比较高的预期。Gartner发布的《2021年十大数据和分析技术趋势》中提到:“到2025年图技术在数据和分析创新中的占比将从2021年的10%上升到80%。”应用需求推动着技术的发展,在GDB V3的引…

url 函数 php,php中url处理函数总结

在php中url处理函数有很多,如有:http_build_query,compact,urldecode、urlencode,parse_url,rawurldecode等等函数。http_build_query(PHP 5) http_build_query — 生成 URL-encode 之后的请求字符串 代码如下复制代码$data array(foo>bar,baz>boom,cow>m…

阿里云EMR Remote Shuffle Service在小米的实践

简介:阿里云EMR自2020年推出Remote Shuffle Service(RSS)以来,帮助了诸多客户解决Spark作业的性能、稳定性问题,并使得存算分离架构得以实施,与此同时RSS也在跟合作方小米的共建下不断演进。本文将介绍RSS的最新架构,在…

Spring Boot Serverless 实战系列 | 性能调优

简介:Spring Boot Serverless 实战系列第四篇来啦,本文将向大家介绍如何对 Serverless 应用进行性能调优。 SpringBoot 是基于 Java Spring 框架的套件,它预装了 Spring 的一系列组件,让开发者只需要很少的配置就可以创建独立运行…

Kafka操作指令笔记

查堆积用命令查: ./kafka-consumer-groups.sh --bootstrap-server {kafka集群地址} --describe --group {消费组名称}bin/kafka-consumer-groups.sh --bootstrap-server localhost:9092 --describe --all-groups #查看所有组别的积压情况可以通过grep、awk或其他文…

java俄罗斯方块旋转,俄罗斯方块旋转算法研究

发表于 2012-12-05 06:36:24 by 月小升最近在阅读俄罗斯方块的A1 A2 A3 A4B1 B2 B3 B4C1 C2 C3 C4D1 D2 D3 D4旋转以B2 B3 C2 C3 中间的点为旋转轴。旋转以后矩阵变为D1?C1?B1?A1D2?C2?B2?A2?D3?C3?B3?A3D4?C4?B4 A41,1数据变成了 1,4问题:旋…