(完整word版)驱动程序原理

(完整word版)驱动程序原理
(完整word版)驱动程序原理

知识体系结构

应用程序:是一段可以执行的代码,由操作系统管理。

编译原理,链接器,装载器:是对操作系统依赖的一个工具,将用户的代码变成可执行的机器码,编译器仅仅检查和翻译用户的语言逻辑,但并不装配成符合操作系统要求的可执行文件格式,如windows要求的EXE文件为PE格式(EXE文件并不仅仅是一个可执行的代码段,而且包含了很多其他的内容,如数据段)。

操作系统接口API:是一个可以被用户程序调用的系统功能接口,可以说,我们编写程序,除了计算和流程控制这些只需要用到CPU指令和CPU寄存器的代码外,其余要访问其他(硬件)资源(包括内存,外设)的代码,均是通过调用OS的API来操作除CPU外的资源的,如向屏幕写一个字母,对于程序来说简单得很,print(“A”); 但是其编译后执行的过程是复杂的,编译后的程序会调用操作系统的API,将当前应用程序的状态(上下文,如光标的位置)以及字母传递给显示器的驱动程序去显示。

操作系统管理与调度:操作系统要实现一般通用的资源管理,也要实现资源使用的协调,包含CPU,内存,磁盘,外设。

首先要确定为什么需要操作系统,操作系统设计的目标是什么?

1.我们总是不能等做完一件事情才去做另外一件,因为有些事情做的过程需要等待,有时候也需要暂停一下当前的任务,先去处理更急的事情,等我回来

时又需要以前的任务保持当时的状态,所以需要计算机也要具备这样的能

力,那怎么实现呢?

2.CPU和内存是计算机的最需要的资源,就如我们的人脑一样,一般很难在同一时间做两件事情。需要处理好一件事情再处理另一件,如果处理得越快就

越好,但是不能前一件事情要等待,你就休息了,后面一件也做不了,计算

机的办法就是你不用CPU了,那好你等待下,我先处理下一个事情。

3.我们写程序,不可能对每个应用,我们重新去写那些驱动程序,也不可能按照自己的想法去处理这些通常的资源管理。否则很多人各自写的应用软件就

没法在一个电脑上运行。

操作系统目标:

1.实现代码重用,对于硬件的访问,对于CPU和内存的充分利用,使不同的应用不需要重新去写这些代码。

2.实现各个任务(不同应用程序)的协调使用,使用户可以实现暂停、重新启用某个任务。

3.实现数据的安全管理,实现良好的人机界面的管理。

4.实现一个开放的体系结构,提供系统调用使用户可以快速编写自己的应用,并提供编译器、链接器、装载器来让用户编写的程序变成可以与操作系统接口的

可执行软件。

操作系统的功能分层:

CPU管理是操作系统的核心:操作系统与用户程序其实可以看成是一个程序,与以前的单任务系统和单片机程序没有本质的区别。

我们来看整个PC机运行过程:

1.系统上电。

2.主板上CPU的CS值设置为0Fx000,IP值设置为0xFFF0,这样CS:IP就指向0xFFFF0位置,这个是程序的开始地址,而硬件上在总线上挂接在0xFFFF0地址

的是主板的BIOS芯片,BIOS开始运行,BIOS是Basic Input Output System简写,

意思即基本的输入输出系统,如果学过单片机就很好理解,其实就是一个程序,由主

板设计的公司的程序员编写的,通过一定的方法(如编程器)写入到芯片内,这段程序会一上电就开始运行。

3.BIOS会检查所有的主板资源,并初始化主板的硬件资源,如总线控制器、显示卡、内存等,并将主板的固有资源和接插件的信息放入固定的内存区域,以便操作系统可以从中获取得到当前的主板上有些什么设备资源。

4.BIOS会在内存地址的最低位0x000000构建起中断向量表,共1K内存(一个向量CS:IP各两个字节,共256个向量),接着是1/4K(256byte)的内存放BIOS数据,接下来在0x0E2CE(56K处)加载了8K左右的与中断向量表相应的若干中断服务程序。

5.当BIOS程序检测到主板上的设备符合启动系统的条件,就读取硬磁盘的引导扇区(第一扇区,这里也是一个程序,从哪个磁盘加载由CMOS设置确定),BIOS 系统将这段程序bootloader读入内存,并将控制权交给引导程序。

6.BIOS具有驱动硬盘等硬件的驱动程序,并且,具有基本的硬件驱动服务程序。这些都由主板硬件厂商提供。Bootloader会调用BIOS的驱动程序和已有的中断服务来从硬盘读取操作系统的核心到内存,并将CPU控制交给操作系统。(操作系统就如一段数据被映射到内存,然后程序通过修改CS:IP跳转到操作系统的入口。)7.操作系统会通过IGDT重新构建中断向量表。每个硬件产生的中断,其编号在硬件设计之初就已经设计好,外部硬件中断/CPU内部异常中断/程序调用中断,其中断号和程序是预先设置好的,当有外部中断时,中断寄存器IPR会暂时存储,并与中断屏蔽寄存器IMR进行AND位运算,然后就可以确定是否处理当前中断,这个是硬件电路实现的,运算后的结果经过中断处理(中断译码)进入CPU的中断寄存器IDTR,CPU执行完当前指令,会自动处理(检查)中断,并将CS:IP指向中断地址,这个地址是什么呢?在实模式下(16位模式),中断译码根据中断号N x 4(每个中断4个字节)直接设置IP,也就是中断的程序地址没法更改的,然后在中断向量的位置就是一个跳转指令,跳转到服务程序处。在保护模式下呢,中断向量的原理与组织与实模式基本一样,也是256个中断处理程序,但是其中断服务程序不在固定的位置,中断向量表也不在固定的位置,CPU的IDTR寄存器由操作系统在系统初始化之初就装入了中断向量表(中断门表)在内存中的寻址位置信息,保护模式下,硬件将IDTR和中断号N译码找到中断表的该中断描述项,而描述项说这个服务程序在GDT或LDT表中的第X项描述的段中,以及偏移多少可以找到程序,然后找到这个段的基址+32位的地址偏移量。

8.操作系统从实模式转保护模式时,最重要的一个是构建内存映射表和各种描述符

表。CPU访问内存时,是通过MMU进行了译码的,MMU的作用就是把CPU指令中的虚拟地址(是程序员编写的地址,一般高级语言编写的程序不直接写地址,但是程序装载到内存后,所有访问地址的指令都发生了地址修改,这是由装载器设置的。)变成实际的物理地址。

9.操作系统为什么需要MMU,计算机从主板启动后,是在ROM中运行的,速度相对于RAM来说是非常慢,我们就想到把程序复制到RAM中运行,但是计算机体系的执行是有固定的地址的,如CPU上电首先从0xFFFF0地址加载第一条指令,发生错误则会自动将IP指向0x0000开始的中断向量表,如果我们将程序COPY 到RAM运行,但是硬件一中断,还是会跑到硬件地址的0x0000处的向量表去运行,还是在ROM中,一样缓慢,为了解决这个问题,我们想到了地址转换,当将程序COPY到RAM中后,构建MMU的页表,然后启动MMU的地址转换功能,然后程序开始从我们设定的地址开始运行(如0xFFFA0),而CPU也为了与操作系统配合,也定义了保护模式,其运行规则也相应发生了变化,其IGTR中的值由实模式时的0x000变成了由操作系统定义的中断门表的地址。保护模式时的中断程序的寻址比实模式复杂,因其中断处理器硬件也复杂。而对于BIOS的ROM访问地址,也被映射到了新的虚拟地址空间,不再是0xFFFF0,所以对于ROM中的中断程序的访问,是通过IGTR+中断描述项定位到该内存地址。所有的外设的内存映射地址均被操作系统重新映射和管理。

(系统上电时,处理器的程序指针从0x0(或者是由0Xffff_0000处高端启动)处启动,顺序执行程序,在程序指针(PC)启动地址,属于非易失性存储器空间范围,如ROM、FLASH等。然而与上百兆的嵌入式处理器相比,FLASH、ROM 等存储器响应速度慢,已成为提高系统性能的一个瓶颈。而SDRAM具有很高的响应速度,为何不使用SDRAM来执行程序呢?为了提高系统整体速度,可以这样设想,利用FLASH、ROM对系统进行配置,把真正的应用程序下载到SDRAM 中运行,这样就可以提高系统的性能。然而这种想法又遇到了另外一个问题,当ARM处理器响应异常事件时,程序指针将要跳转到一个确定的位置,假设发生了IRQ中断,PC将指向0x18(如果为高端启动,则相应指向0vxffff_0018处),而此时0x18处仍为非易失性存储器所占据的位置,则程序的执行还是有一部分要在FLASH或者ROM中来执行的。那么我们可不可以使程序完全都SDRAM中运行那?答案是肯定的,这就引入了MMU,利用MMU,可把SDRAM的地址完全映射到0x0起始的一片连续地址空间,而把原来占据这片空间的FLASH或者ROM 映射到其它不相冲突的存储空间位置。例如,FLASH的地址从0x0000_0000-0x00ff_ffff,而SDRAM的地址范围是0x3000_0000-0x31ff_ffff,则可把SDRAM 地址映射为0x0000_0000-0x1fff_ffff而FLASH的地址可以映射到0x9000_0000-0x90ff_ffff(此处地址空间为空闲,未被占用)。映射完成后,如果处理器发生异常,假设依然为IRQ中断,PC指针指向0x18处的地址,而这个时候PC实际上是从位于物理地址的0x3000_0018处读取指令。通过MMU的映射,则可实现程序完全运行在SDRAM之中)

10.内存映射表:是为MMU构建的一块内存区域,其内容为一个表,或者叫一个数组,每个元素标记了[虚拟地址:物理地址],页表一般分两极,第一级为1M一个项,第二级则是1M内的索引。那会有多少呢,4G=4K项,然后每个1M=256项,

所以页表大小为1M,如果每个项为32bit,那么至少需要256K个指令周期才能把内存的页表装入MMU,也就是0.4ms, windows的CPU分时片为20ms。那么进程

切换的时间不少于1ms。

11.CPU中有是否启用MMU的寄存器设置,如果不启用,则CPU发出读写地址线信号,同时MMU不会做出反应,使能信号直接被MMU输出,RAM检测到指令,直接将数据放到总线上,并通知CPU数据准备好。如果启用MMU,CPU发出读写指令,MMU同时从总线获取地址数据经过硬件运算,仅仅一个硬件周期,然后修改总线的地址,并通知内存或北桥芯片地址准备好。

12.对于操作系统来说,只要构建每个进程的内存映射表,并在进程切换时将内存映射表复制到MMU的缓冲中。

13.而对于应用程序来说,是不能操作MMU的,所以,当企图访问没有映射的虚拟内存时,MMU会向CPU会产生缺页中断,然后在中断服务程序中,操作系统会根据是否是被换出内存还是没有初始化,来决定是从硬盘装载内存,或抛出异常错误。

14.操作系统的内存管理,是分段式管理,并在内存中构建了一个段描述符表,这个表是用来告诉大家(CPU,所有程序)每个段内放了什么东西,是可执行代码,或是数据,或者是一个只读存储器,或者这个部分是不能访问的。为什么要这样,因为系统的4G内存空间,其实是不连续的,所有的硬件都是挂接在总线的,CPU 只有通过32位总线来访问所有的非CPU的外设,包括MMU,RAM,北桥,硬件上CPU就仅与上面的器件通过32位总线连接,对于键盘、鼠标、显示器、PCI 卡、USB、电池芯片、声卡、网卡等等的访问,都是通过写地址总线,然后读数据总线(其实就是地址线,冯。诺伊曼结构的总线复用),来获取或写入一个数据。

而硬件的挂接地址是不连续的,我们访问内存地址时,就要清楚每个实际地址的用途,为了安全,在不清楚的情况下,即使发出错误指令,系统也会自动根据段的属性来检查是否处于保护中的地址,以免系统马上崩溃。不是说过Win32环境下不用“段”了吗?是的,这些“段”实际上并不是DOS汇编中那种意义的段,而是内存的“分段”。上一个段的结束就是下一个段的开始,所有的“分段”合起来,包括系统使用的地址空间,就组成了整个可以寻址的4 GB空间。由于Win32环境的内存管理使用了80386处理器的分页机制,每个页(4 KB大小)可以自由指定属性,所以上一个4 KB可能是代码,属性是可执行但不可写,下一个4 KB 就有可能是既可读也可写但不可执行的数据,再下面呢?有可能是可读不可写也不可执行的数据。Win32汇编源程序中“分段”的概念实际上是把不同类型的数据或代码归类,再放到不同属性的内存页(也就是不同的“分段”)中,这中间不涉及使用不同的段选择器。虽然使用和DOS汇编同样的.code和.data语句来定义,意思可是完全不同了!为了简单起见,在本书中还是简称“段”,读者应该注意到其中不同的含义。在程序中如果不小心用了对 .const段中的数据做写操作的指令,会引起保护错误。在程序中不必定义堆栈段,系统会自动分配堆栈空间。

惟一值得一提的是,堆栈段的内存属性是可读写并且是可执行的,这样靠动态修改代码的反跟踪模块可以拷贝到堆栈中去边修改边执行。一些病毒或者黑客工具用到的缓冲区溢出技术也用到了这个特征,有兴趣了解的读者可以查阅相关的资料。

15.4K, 是windows管理内存的最小单位,每个4K的虚拟地址对应整块的4K实际地址,而每个实际的4K地址为一个段,每个段又有自己的存储属性。当程序访问某个虚拟地址时,MMU先获得虚拟地址与实地址的对应的页表描述项,描述项说地址我不知道,但是我知道他在操作系统的段表中,要么在GDT要么在LDT中的第N项,然后在GDT中的N项说这个段是不能被访问的,MMU就报告出错了,

如果可以访问,那么就根据这个段的起始地址+ (虚拟地址/ 4K的余数就是段

内偏移地址。)

16.Windows的内存分配机制,当应用程序调用操作系统的内核程序申请一个内存区域,内核程序会根据当前的应用程序的内存使用链表来来决定是否需要申请新

的页(段),如果在当前已经申请的4K内存页还有符合申请的大小块就直接重建

链表,分配一个地址给当前进程,可见当前的进程的内存的分配也是操作系统管

理的。

17.DMA控制器,用于管理两个外设之间的数据流传输,如控制从磁盘读取数据进入内存,DMA控制器是一个连续地址信号发生器,同时也有读写的方向时序控

制,数据不经过CPU,由DMA控制直接在总线上传输。

设备驱动程序:

操作系统的驱动程序结构,首先明白计算机硬件与CPU及操作系统的关系,操作系统的代码仅仅在CPU中运行,所以只要CPU支持该操作系统的指令代码,就可以启动操作系统,而至于访问硬件的过程,就是读取内存地址的过程(MOV eax, &0xFFF330),对于i386其IO地址是独立的地址空间0x0000-0xFFFF共64K所以CPU的地址空间对于i386有两个,访问的指令也有两种,而对于ARM这些来说IO地址是被编址到32位内存地址空间的,所有外设也被挂接到内存总线上的。这些硬件的物理地址在主板制造好时就已经定了,无法更改,而BIOS的功能之一也就是收集这些设备与IO的地址对应信息,并交给操作系统。然后操作系统根据BIOS提供的设备信息安装相应的设备驱动程序给操作系统内核使用。如果BIOS不提供这些设备的IO信息,那么操作系统怎么办呢,那操作系统不可能搜索整个IO 地址空间来确定有哪些设备,所以也就没法知道有哪些外设IO。外设的IO分配是主板设计者决定的,每个设备均有连续的三部分寄存器IO地址,1。控制寄存器,2。数据寄存器,3。状态寄存器。通过对寄存器的读写,也就是执行指令IN/OUT 0xFFFF,来读写寄存器实现对于外设的操作,可以看到,这个与单片机对于单个引脚的控制方法不同。PC机的目标是数据交换和处理,而单片机的目标主要是控制每个引脚的电平实现硬件的控制。

操作系统会自带一些驱动程序,那么这些自带的驱动程序怎么与实际的硬件关联起来呢。

下面我们来看USB鼠标的驱动,在windows的设备管理器中我们可以发现,鼠标项,其中可以找到HID鼠标,这个是一个符合USB设备类HID类的串行设备,我们发现这个设备没有硬件资源,也就是说没有对应的IO地址和中断号。所以这个不是一个真实的独立的设备,它是一个windows的高层设备,也可以叫这个设备是一个虚拟的接口设备,我们可以采用windows的一般鼠标驱动程序来操作这个设备。而实际上对于它的操作,会被该设备关联的驱动程序所接管,而其关联的驱动程序是什么能,是windows自带的USB HID类驱动程序,对于USB的HID类设备,其操作协议有固定的规范,所以可以直接驱动。那么USB HID驱动程序又怎么驱动实际的硬件呢,我们可以在windows的设备管理器中找到该鼠标对应的USB控制器,而该控制器有IO地址1840-185F共32个字节空间以及中断号18,那么这个地址和中断号怎么来的呢,这个就是BIOS告诉操作系统的,地址和中断号是主板设计时就已经定了的。所以在不同的PC上装的windows,其IO地址和中断也不同。

bios启动,可以访问VGA,也可以设定从磁盘或USB设备启动,说明BIOS已经可以驱动所有设备,其实上BIOS已经构建起中断向量表,对于挂接到中断总线的所有设备产生的硬件中断已经具有响应能力,BIOS启动后,从CDROM或硬盘或USB设备装载引导程序,最开始没有安装操作系统时,引导程序是一个引导安装系统的程序,安装程序启动运行会根据BIOS的设置来配置操作系统,在windows中明显的就是改变注册表,注册表是系统能用的所有硬软件资源的描述。安装完成后,系统就进入正式的系统。在没有安装更多主板驱动程序之前,所有的底层硬件驱动就是通过中断服务程序提供的,如USB的控制驱动程序,也可以说是总线驱动程序,响应硬件中断和软件中断,提供硬件访问服务,怎么在中断服务(USB总线驱动)和操作系统的通用驱动程序接口之间建立对应的关系呢,对于即插即用硬件来说,这个映射过程是动态的,当没有鼠标接入USB端口时,内存中就存在对于中断18响应的USB控制器驱动程序,但是什么也不做。当有鼠标插入USB端口,首先鼠标的USB芯片得电运行,同时USB的数据线会拉底主机的USB控制器接口的数据线接口的电平,硬件就会知道有设备接入,直接发送USB控制数据包,读取设备的配置信息,知道了设备的描述信息后进一步配置设备信息,同时通过挂接在中断总线上的引脚产生中断信号18,中断服务程序进一步与鼠标芯片交流,确定为HID设备后,中断服务程序会搜索其成员对象中能处理HID类别的驱动程序来与这个设备通信。而这个HID驱动程序在哪呢,什么时候又与中断服务关联了呢,这个就是BIOS与操作系统的交互过程,操作系统启动后,会将自己的三类USB驱动程序挂接到中断服务中,怎么挂接?操作系统直接调用BIOS的函数表中的函数将驱动程序的指针加到服务程序的驱动程序列表,然后所有的来自鼠标的中断均被转发到HID驱动来处理,而HID做了什么,HID会检查设备的类型和设备供应商及ID,并从本驱动的附属驱动中找到最合适的驱动并交给它处理,如果没有找到驱动,则调用操作系统的硬件驱动程序的安装流程,驱动程序安装好后,驱动程序初始化会向HID驱动登记自己,并在操作系统中注册一个鼠标类别的设备,并将标准的鼠标驱动放到本驱动中,当鼠

标移动,首先鼠标的移动数据被保存到鼠标的USB芯片的寄存器中,并产生USB传输数据申请,USB控制器读取寄存器的数据到主机的USB寄存器中,如0x1850处,然后申请中断处理,中断程序获取到寄存器数据,并传递给USB鼠标器驱动程序,USB鼠标驱动程序调用操作系统的标准鼠标处理程序(标准驱动程序)来处理命令,一般就是鼠标驱动会通知windows的窗口管理进程重画整个界面(鼠标位置数据已变更)。

当我们要访问USB端口时,我们不能直接读写USB端口,因为USB端口是由控制器管理的,当没有挂接设备的时候,windows中也没有对应的设备项目,因此也就没法通过readFile/writeFile来操作没有对象的设备了。

当有一个可以读写的USB设备别挂接时,如USB转串口芯片,系统开始找不到驱动就安装,安装好后,驱动程序会向系统注册一个COM端口,而该设备的驱动程序指向芯片特有的驱动程序,芯片驱动程序会调用USB总线驱动将数据传输到芯片端,而这个数据格式是驱动程序根据芯片定制的,当收到数据时,芯片驱动程序会将接收到得数据传递给通用COM驱动给其处理。

对于U盘操作,那些通用的磁盘驱动程序,Scsi的函数都只是在准备一些缓冲区、数据结构等,并没有对硬件进行操作,真正要操作硬件设备的还是由驱动程序来完成的,可见,设备驱动程序是有着很强层次结构的,下层是专门针对物理设备的,上层是针对操作系统的抽象设备的,下层是U盘等物理实体,上层是文件夹,二者通过一定的通信或调用机制完成了设备在操作系统下的正常工作。U盘插入,系统调用安装设备驱动程序,U盘驱动初始化设备,当第一次加入内存,U盘驱动程序修改了总线驱动的设备驱动程序列表,将自己挂接在总线上进行监控,同时修改了系统的设备清单,应用程序读写这个磁盘时不是通常的ReadFile和writeFile,而是通过DeviceIOControl来传递命令控制设备的,首先调用的U盘驱动程序,然后由设备驱动程序调用通用的DISK处理程序来处理,处理完成后,最后调用设备驱动程序的实际硬件操作。

总线驱动:

CPU与主板的配合

外设接口原理

相关主题
相关文档
最新文档