首先要想学习好驱动就的了解很多关于驱动的事,因为驱动程序都是加载在windows 的内核模式下,他与windows系统的其他组件进行密切交互,其实微软让windows尽可能的在不同的硬件平台上运行,增加可移植性,必须根据市场的需要相对容易的移植到新的体系结构上,下面是微软为了解决在操作系统上出现的几大难题的具体办法,有句话叫“知己知彼、百战不殆”,呵呵
微软把windows 设计成软件分层的体系结构,和硬件紧密联系的只有硬件抽象层(HAL ,hardware abstraction layer)而操作系统的其他重要的组件几乎和硬件没有关系,就使操作系统的大部分不依赖特定硬件,当需要windows移植到别的平台时,只需要修改相关的硬件抽象层,还提出面向对象编程的思想
要想一定更可执行程序可以运行在不同的windows版本上,直接制定了统一的可执行文件格式,并且保持一致的API接口(虽然内部实现不同),保证二进制级的兼容性。还引入了环境子系统,不同的环境子系统提供相应的API支持(小菜认为是虚拟机的前身)
windows的健壮性和可靠性主要是源于用户模式和内核模式的划分,应用程序运行在特权级别低的用户模式,在用户模式下,所有的错误操作都会被早做系统侦测到。并给与提示,而操作系统的大部分核心代码,运行于特权级别比较高的内核模式下,操作系统是经历严格测试的,可以保证正确性,对于任何涉及操作硬件的操作、应用程序都无法在用户模式下完成,必须通过对内核模式中的系统调用来完成,in a word ,就是类似我们的中央集权制度
windows的健壮性同样来自自身的分层设计,而且每一层的特权是不同的,处在最上层的应用程序,对于操作系统的造作就就仅限于对API的操作,API
是操作系统给用户层的唯一接口,当应用程序想访问硬件设备的时候,必须向操作系统发出请求,操作系统会检测到应用程序通过API提出的请求,并校检
请求的参数,当参数非法时,会返回一个错误,这大大提高系统的健壮性和可靠性
可扩展性是指操作系统方便增加新的功能和支撑新的硬件,并且对已有的代码影响达到最小,早在win2000中,操作系统就实现了一个跨越性的改变就是在把内核在从执行体组件中分离出来,操作系统只是负责关于线程的调度工作,线程运行在自己的线程上下文中,线程上下文就是指CPU寄存器的状态,比如堆栈寄存器、指令寄存器、还包括线程ID、线程的优先级、线程的本地存储等相关信息。
内核的主要作用是实现调度线程活动,其他的操作系统组件如内存管理组件、进程管理组件等作为独立的内核组件,统称为执行程序组件,执行程序组件暗战模块化的方法设计,在需要改进的时候可以修正或者增加这行程序组件
windows 的I/O操作是基于异步设计的,也就是在线程在发起一个I/O请求的时候。可以不等待这个I/O 操作完成,就发起下一个I/O请求,这样CPU就不会浪费时间花在等待I/O操作完成上
windows是基于多进程和多线程,并且尽可能使多个任务并行执行,在内核调度线程的时候,应该是尽可能多让各个线程看上去是在同事运行的,而不是都在等待状态,这样会最小化处理器的等待时间,这样就解决了windows的CPU的高度并行化
windows是完全支持异步操作,当用户提出一个I/O 请求的时候,不用等到I/O 操作结束就可以立即返回,接着去执行其他的操作,这样的就是说CPU不用花时间浪费在等待上,而是去做更有意义的事情。这样大大提高和了操作系统对I/O 的吞吐量,在我们编写驱动程序的时候应该让程序尽可能支持异步操作,提高I/O 吞吐能力
上面就是为了学习在windows这个庞然大物上编程索要做的科普,其实上面的一些知识点也不过是操作系统的特点,学过《操作系统原理》的小伙伴都知道哈,简单的概括就四条:1.实时性,2.异步性,3.可扩展性。4.共享性
总体上windows是分为内核模式(kernel mode ) 和 用户模式(user mode),说到操作系统的内核模式和用户模式,一般是和CPU的特权层联系在一起,intel 的386 CPU 就有四个特权层,分别是第0环(Ring 0)、第1环(Ring 1)、第2环(Ring 2)、第3环(Ring 3)。 ring 0 是最高特权层,就是可以执行任意代码,而在ring 3层就是最低特权级别,执行有限代码,其他的CPU也是也是有类似的特权级别windows的核心代码都是运行在内核模式下,而非内核代码是运行在用户模式下,运行在内核模式下的windows核心组件是安全的,切不会受到恶意攻击,所以这些核心组件可以进行所有权限的操作,而在用户模式下的应用程序是不安全的容易受到攻击,所以用户模式下的应用操作是受限的,如果应用程序想尽心一些敏感造作,如访问物理内存,物理端口,应用程序是需要向内核模式下的组件提出请求
操作系统与应用程序是相互隔离的,操作系统的核心代码是运行在特权模式下的,即内核模式,而应用程序是在非特权模式下的,即用户模式,操作系统和应用程序的关系类似于服务器(server)和客户端(client)的关系,一个服务端对应多个客户端(其实一个驱动对象对应多个设备对象)
这个就不细讲了,都是赘述。。。。
windows即可以运行在单CPU的PC上,也可以运行在对称的多CPU的PC(AMD负载平分),也可以运行在主从多CPU的PC上(intel 386 多核,负载主要在主CPU上)win32是windows为了兼容其他版本的windows程序打造的其中一个最主要子系统
如vc提供的 MFC,就是在win32和应用程序之间加了一层封装而已,windows API 主要分为3类,USER函数,GDI函数,和KERNEL函数
USER函数:这类函数管理窗口、菜单、对话框和控件
GDI函数:这类函数在物理设备上执行绘图操作
KERNEL函数:这类函数管理非GDI资源,如进程、线程、文件和同步服务,
操作系统在加载应用程序时,还会将user32.dll 、gdi32.dll、和kernel32.dll加载到内存,其他的不管需要加载啥DLL都离不开这三个DLL ,在win2k的之前,三个函数完全担任windows的重担,(就像USA的三权制衡一样),到win2k上三个系统的功能被完全抽象进了内核模式,这三个DLL就只剩下了“躯壳”了,之所以要直接把user32和GDI模块实现移进内核是为了提高运行速度,减少在层次切换带来的花销,
除了win32子系统外,windows还有OS/2 子系统、POXSIX子系统。WOW子系统、VDM子系统,这些系统各自为应用程序提供API,但是都是通过win32子系统
来实现,这类似虚拟机的概念,只是说一说 WOW(windows on windows)子系统吧,此版本是为了兼容在16位的windows,当然现在win7 上的 SystemWOW64
目录就可想而知了,就是虚拟的64位的子系统
在Win32下面还有一层原语API,就在win32 API 加上 Nt 的那些函数 类似的 win32 API 有 CreateFile() 对应的原语API就是NtCreateFile(),其实我
们如果只做win32的开发,就直接不需要Native API了,就是是win32 API调用了Native API ,Native API 是从用户层进入内核模式的大门,她是通过软件
中断的方式进入到内核模式,并调用内核的系统服务,微软是不鼓励程序猿使用Native API 的,因为这会根据不同的版本在改变,只有多数的win32 API是
不变的,然而根据效率和一些诡异的要求你可以直接调用Native API
Native API 从用户模式进入内核模式,调用系统服务,那个软中段在不同的版本有所不同,但是XP上是sysenter 指令完成的,在软中断会Native API
的参数和系统服务号传递进内核模式,在系统服务组件中,有一个系统服务描述符表(system service descriptor table),根据这个系统服务号为索引,
从表中可以查出对应的系统服务函数的函数地址(诡异的调用可以在这里开始了,直接修改系统服务函数描述表,一般的系统服务函数和Native API 函数)
有相同的函数名,例如NtCreateFile(),他会调用具有相同名字的系统调用,两个函数的名称是一样的,在系统调用中会进行参数的合法性,这也是操作系统
的最后一道屏障,所以修改系统服务函数描述符表的函数地址就可以执行自己任意的代码了,当然特权级别是ring 0 级别的奥!!! you can do anything
you wanna,但是执行失败的后果不仅仅是应用程序退出这莫简单了,或许直接就蓝给你看。。。。
windows 执行体组件位于Ntoskrnl.exe 的上层,而内核位于其上层,在win2k后执行体组件是内核模式下的一组服务函数,他们位于ntskrnl.exe 中,
下面说一说执行体组件组件的若个组成部分
windows操作系统提供的所有服务几乎都是以对象的形式存在的,windows 对象类似于面向对象(OOP)语言的概念,这实际就是把所有的服务和设备
直接抽象成一个对象,那么操作对象就等于修改他的成员变量,还有一些成员变量是不允许直接访问的,就像对象概念中的私有变量,要通过windows提供
一些特定例程来访问(这些例程就像是共有成员函数)
面向对象编程被公认是一种十分有效的软件设计技术,可以提供对代码全面组织和封装,通过对象隐藏了实现细节,提高软件的可靠性健壮性(所要学
习面向对象编程技术)。对象的管理就是创建、管理、回收这些对象组件、在驱动程序开发中,涉及很多对象,如驱动对象(Driver Object) 和设备
对象(Device Object)etc.
在win2k中,每一个进程都有自己专门地址和安全身份(进程是系统分配资源的最小单位),但不是运行的最小执行单位,而线程才是最小执行单位,
进程相当于是线程的容器,进程管理负责就是进程创建、进程调度.终止进程等,这些管理组件是相互联系的(进程管理和对象管理组件等)
windows引入了虚拟内存,这样就必须实现物理内存和虚拟内存之间的映射关系,并且每一个进程有独立的4G 虚拟地址空间,每个进程看到的虚拟
内存是完全不同的,因为每个进程的内存映射是完全不同的,这样为了保证每个进程之间是不会相互干扰的,进程之间如果需要访问对方的虚拟内存,
必须通过专门的几种内存通信机制
windows 规定,将4GB的虚拟内存分成两半,虚拟内存的0 ~ 0x7FFFFFFF规定为用户模式的地址,在用户模式下的程序只能访问这几段虚拟地址,
在0x80000000 ~0xFFFFFFFF 规定为内核模式的地址规定为内核模式的地址,这段程序只允许被内核模式的程序访问(终于明白在OD等调试软件的起始
都是在0 ~ 0x7fffffff 直接呐,因为OD是标准的 用户模式(ring 3)调试神器)
还有就是在内核模式下,所有进程的在指向内核地址是完全相同的(映射方式完全相同)这样在每个进程中顶端2GB的内核模式地址的数据是完全一直
的,虚拟内存管理程序是负责对虚拟内存管理的模块,对虚拟内存的申请、回收等操作都是该模块实现的(知道每一个进程的内核部分都是公共的,我们
就又高兴了,就是说在A程序里面出现设备地址(内核设备) 我可以在B程序里面继续使用(传递同样的内核服务请求,同样的控制码,窃喜中。。。))
I/O管理器负责发起I/O请求,并且管理这些请求,它甴一系列的内核模式下的列程所组成,这些例程为用户提供了统计接口,I/O管理器的目标使
来自用户模式的I/O请求独立于设备
无论是对于端口的读写、对键盘的访问,还是对磁盘文件的操作都统一为IRP(I/O request packages)请求形式,其中IRP包含了对设备操作的重要
数据,例如是读操作还是写操作,度多少字节,写多少字节,是直接读到进程中还是先读到缓冲区,再读到进程中等
IRP被传递到具体设备的驱动程序中,驱动程序负责“完成”这些IRP,并将完成的状态按照原路返回到用户模式下的应用程序中,实际上,I/O管理器
器担当着用户模式代码和设备驱动程序之间的接口
在windows中,配置管理程序记录所有计算机软件、硬件配置信息,它使用一个被称为注册表(Registry)的数据库保存数据,驱动设备就是根据
注册表中的这些信息进行加载
另外,驱动程序从注册表中读取相应的参数,这样可以提高驱动程序的灵活性,例如,设备操作的延时时间,可以作为参数写进注册表,驱动程序
加载的时候读取该值,而不是在编程的时候将其写成定值,这这大大调高驱动程序的灵活性
原文地址:https://blog.csdn.net/l_f0rm4t3d/article/details/11174567