通过IRQL看NT内核

简介:

linux强调的是进程自主性,windows则是对象自主性,其中线程本身也是一个对象,进程也是,所以一个进程可以操作另外一个进程的地址空间也就不足为奇了,windows的通信实际上是对象间通信,而linux因为一切围着进程转,最新的内核中断也被线程化了,因此通信就是进程间通信,linux 中进程作为超级容器的意义要比windows的更大些,windows中进程是一个容器,也是一个对象,某种意义上它作为容器的意义是容纳别的对象。 windows的模块化思想更加鲜明。

windows中将缺页,调度等概念从线程,进程中分离,专门安排一个irql级别来处理之,而linux下相应的概念则永远和进程相绑定,看看缺页中断的处理代码,考虑的一直都是进程的概念,在windows中,考虑的就是对象的概念了,比如文件对象等等,但是缺页本质上真的是和进程相关的,所以在 windows中没有进程/线程上下文的的执行绪当然就不能缺页了,那么谁没有进程/线程上下文呢(也就是ms说的“任意上下文”)?答案是 dispatch_level之上(包括dispatch)的没有进程/线程上下文,所以,相应级别的执行中就不能访问分页内存(可能被换出而导致缺页的内存)了。windows下进程/线程是和其他对象并列的概念,没有什么特殊之处,但是在linux(unix)中,它们却是终极重要的概念,贯穿整个系 统。windows为了实现完全异步和为了配合可移植性,将一切抽象成了“中断”,所以我们没有必要特别说什么上下文的概念,上下文只是一个进程/线程的 容器,没有又如何,代码照样执行,之所以强调什么时候要有上下文什么时候没有必要完全是为了配合进程/线程的,为了使进程/线程好管理。

我比较喜欢将一大堆的结果归结为一个原因,就像一切宏观运动都是牛顿定律的结果,一切微观运动都是量子力学的结果,一切宇观运动都是相对论的结果一样,从这一点上看,研究windows是一个好的选择。

我们看一下两个操作系统的缺页,先说linux。

在linux中,发生缺页后执行缺页中断处理,当时的上下文还是引起缺页的进程线程上下文,除非当前在中断或软中断中,然后内核挂起当前线程,执行磁盘 io,将页面取回或者分配一个匿名页面,实际上这里挂起当前线程的意思是执行的缺页中断处理并不属于当前线程和在磁盘io或分配匿名页面的过程中可能要等 待,等待意味着线程切换,一直到线程实际切换,上下文一直是当前线程上下文。 在前面说的中断或软中断中出现缺页的话就不能随便进行线程挂起了,因为此时是linux唯一可能在任意上下文的情况,即使在内核发生缺页也没有关系,缺页 处理只认上下文,不管在什么态(因为内核数据结构常常在中断中被访问,所以,一般还是别在内核用分页内存了,所以linux又规定,内核数据结构从 slab或类似的连续区分配分配,并且常驻内存,你完全可以修改内核,在内核分配一个页面,映射到内核空间或用户空间,然后修改缺页处理程序,使得可以处理映射到内核导致的缺页,最后保证你永远不在中断中访问这个地址,很麻烦,但可能!),所以linux硬性规定:中断不能引起缺页(原因仅仅一个,上下文不确定)。(附:本质意义上,内核空间并不属于任何进程的地址空间,只是一个所有进程和资源的管理空间,所以内核空间大家公用,所以linux很是巧妙的将物理内存线性映射到内核地址空间,内核数据结构大部分在此分配,一旦满足不了要求,可以从vmalloc区域分配,并且只更新0号进程页表,由缺页中断 来解决别的进程页表和0号进程页表同步问题,所以linux内核空间地址的中断并不需要真正分配内存页面,只需要修改个页表就行。linux内核空间地址不许缺页并没有实质性的规定,只是为了内核开发的方便,所以linux仅仅解决用户空间的缺页和内核vmalloc页表缺页。)回到前面正常情况,在有上 下文的情况下进行磁盘io或分配匿名页以及可能引起的当前线程睡眠切换有问题吗?一点问题也没有,因为linux/unix尽量使所有执行绪都有特定上下 文,不允许的操作作为稀有的特例已经被硬性明文禁止了,这样的弊端在于,留给程序员的只有好好把这个原理研究透或者把那些规定背熟了。在windows中 的情况呢?上下文没有那么特殊了,一切都是异步的中断,很多执行绪没有特定上下文,那么发生缺页后呢?可能缺页发生在用户线程,而用户线程运行在被动中断请求级别,这是没有任何问题的,所有别的级别的执行绪都可以中断它,最重要的,io开始例程在dispatch级别运行,缺页处理运行在 dispatch,调度也运行在dispatch。首先,为何调度在dispatch运行呢?因为在dispatch或之上的执行绪就是任意上下文环境 了,它们是不允许发生线程切换的,这怎么保证呢?如果在dirql级别的要切换,那么就要将irql提升到dispatch,因为当前dirql比 dispatch高,于是蓝乎,如果在dispatch的dpc要调度,那么它要提升irql到dispatch,因为它已经在了,于是亦蓝乎,这就是原 因,调度运行在dispatch实际上是为了保证没有上下文的执行绪不能发生线程切换(类比linux,linux中也是没有上下文的不能切换);那么 io开始为何也在dispatch呢?这是因为windows是异步的,一个线程发起一个io不一定在当前上下文就可以执行,可能在一个硬件中断发生后有 了执行的条件(考虑irp排队),然后此中断派发一个dpc开始io动作,中断没有确定上下文,它派发的dpc同样没有上下文,所以io开始只能在没有上 下文的dispatch执行;io分派例程比如read,write之类的必须在有上下文的被动级别运行,因为它们可能要睡眠,而且不能打扰io开始和完成例程;现在考虑缺页,看看它能在哪里执行,咱们从低irql到高考虑,我们假设被动irql也参与到中断模拟,如果缺页处理运行在最低的被动irql, 那么就完了,试想一个同样在被动irql的线程发生缺页,缺页运行前必先提升irql到被动级别,但是不巧,已经是被动级别了,出错,返回,蓝!但是事实上,被动级别并不参加中断模拟,这说明缺页处理运行在被动级别是可以的,但是考虑apc级别,它可使参与中断模拟了,apc比被动级别高,于是如果缺页处 理在被动级别,那么apc就不能缺页了,这说明apc不能用分页内存,这很影响大家伙的信心,windows怎么能这么限制程序员呢;那么我们就将缺页处理运行级别往上提高一级,到apc级别,还是不行,apc还是不能缺页,因为它就是在apc级别的,虚拟中断只能被irql比它高的中断掉;于是再往上 走,到dispatch吧,这下好了,apc可以使用分页内存了,但是提升到这个级别仅仅为了让apc使用分页内存,如果又引起别的问题,那么还是别让 apc使用分页内存了。考虑缺页需要哪些操作,无非就是分配内存页面,或者从磁盘读取页文件,考虑后者,肯定要开始一个磁盘io过程,而磁盘io就是在 dispatch进行的,这可以,考虑前者,分配页面可能导致线程睡眠,现在缺页正在dispatch运行,想挂起一个线程也没有问题,正好调度也是在这个级别运行,仍然没有问题,现在我们可以说缺页处理可以在这个级别了,但是,人是贪心的,一些限制总是使人能有所创新,只可惜,这次的创新在 windows完美主义下失败了,我们不甘心dispatch以及之上的内存不能使用分页内存,于是再往上提一级,提到哪级姑且不考虑,如果 dispatch这种没有上下文的级别产生缺页就有可能睡眠或请页,但是io开始和调度都是确定性的在下面的级别运行,于是不能再往上了,就这样了,缺页中断处理程序只能在dispatch级别运行。

这下有意思吧,windows的大框架已经定死,不需要硬性规定,一两条原则就可以决定这么多事情:1.中断虚拟化的irql机制使windows成为完全异步os内核;2.对象化模块化使windows不过分依赖于进程/线程的概念,使得1的实现简单化。网上很多人比如赫赫有名的毛德操教授说 windows的进程可以操作别的进程空间是windows不安全的原因之一,我认为这句话没错,但是毛教授似乎没有理解windows为何这么做,它不安全是因为细节没有把握好,而大的框架我很欣赏,也很坚信,没有错!linux的软中断和任务队列就是windows的借鉴,只可惜,linux是善变 的,发现工作队列后毅然抛弃了任务队列。

linux更像是一件艺术作品而windows更像一件工业产品。举个例子:一个高大的全钢架结构建筑矗立在广场,看似永远都没有完过工,巨大的钢架抬头 可见,耗资巨大,周围只有钢架,没有封顶,人在里面难免风吹雨淋,人们会在底楼租一间盖好的屋子,但难免还会被漏下来的建筑石料将屋顶砸破,头破血流,目前开发商大势已去,真不知能否完工,但是框架结构绝对一流,缺的不是技术,而是激情;在郊外的草地上,几个满怀激情的人在自己修建一座别墅,没有开发商, 没有人观望,只有他们自己,别墅异常豪华,没有什么框架,因为不需要,他们有技术,有激情,缺的是大家的支持。前者是windows,后者是linux, 而unix可能就是金字塔吧。

现在的硬件也在提供更多的功能,从而使一些软件策略更加容易实现,比如apic就实现了软中断功能,这就更加使得windows的irql如鱼得水了,我认为linux马上也将用最新的apic软中断功能来实现softirq,这一向是linux的作风:绝不落伍!

感叹:现在的硬件也在越来越快的发展了,快赶超软件发展速度了,看,什么都在封装,以前用引脚传递硬件信息,现在用消息了(看看pci和pcie吧),硬 件和软件都在两极分化,一帮人搞的东西越来越简单,越傻瓜,另一帮人却越来越高深,越深不见底!呜呼,我,何去何从?!



 本文转自 dog250 51CTO博客,原文链接:http://blog.51cto.com/dog250/1273468

相关文章
|
9月前
|
Linux API 开发工具
Windows NT 驱动程序的编译、安装、调试
Windows 驱动分为两类,一类是从 Windows NT 遗留下来的驱动模型称为传统的 Windows NT 驱动程序模型,另一类是 Windows 添加了电源管理后的 KMDF (WDM)驱动程序。本文这里首先以最简单的 Windows NT 驱动模型为例介绍 Windows 驱动的简单编写、编译、安装及调试。
161 0
|
Linux 开发工具
|
存储 Linux 程序员
Linux 内核 vs Windows 内核
Linux 内核和 Windows 内核有什么区别?
Linux 内核 vs Windows 内核
|
Linux Windows
win10下WSL2更新内核
win10下WSL2更新内核
4666 0
win10下WSL2更新内核
|
监控 安全 Unix
关于LR监视Windows和linux的说明
一、监控windows系统: 1、监视连接前的准备工作 1)进入被监视windows系统,开启以下二个服务Remote Procedure Call(RPC) 和Remote Registry Service (开始—)运行 中输入services.msc,开启对应服务即可)。
1161 0