作者:小白一枚 | 来源:互联网 | 2023-06-29 12:36
第一章:概要:你想写windows驱动程序吗?为什么要写windows驱动程序?windows驱动程序可以干什么?相信希望学习windows驱动程序的兄弟,估计都会有这么几个问题?那
第一章:概要:
你想写windows驱动程序吗?为什么要写windows驱动程序?windows驱动程序可以干什么?相信希望学习windows驱动程序的兄弟,估计都会有这么几个问题?那么我们先就这几个问题进行一下简单的探讨。
1. 你是否想写windows驱动程序,这个回答,只能有你自己回答了,作者没有办法回答。
2. 为什么要写驱动程序?首先,驱动程序是操作系统运行核心的一部分,可以执行很多应用层程序无法执行的任务。你在编程中,如果使用了汇编语言,而且使用了IN或者OUT指令,你就会发现,系统报错。为什么内?这是因为很多计算机指令,属于特权指令,在应用层无法执行,想要执行这些指令,只能在内核层执行。当然,只是为了执行CPU的特权指令,就编写驱动程序,似乎理由不是很充分。
为了很好的回答这个问题,我们不得不回到计算机的组成。计算机一般由CPU,内存以及外围设备组成。CPU和内存是一个计算机的核心组件。其他的例如硬板,键盘,鼠标,显示器,网卡等,都可以算作是计算机的外围设备。计算机的功能大概来说,就是对数据进行处理,这个功能由CPU搭配内存来完成。此时,问题就来了,如何将需要处理的数据,发送给CPU和内存呢?我们只能通过计算机的外围设备,将这些数据装入计算机的内存,让计算机去处理。如何处理这些数据,是需要编写数据处理程序的,在这里,我们不对此类程序进行讨论。我们这里要说的是,外围设备,如何将数据发送到计算机的内存,让CPU去进行处理呢?既然是通过外围设备,将数据发送到计算机内存,这个任务由谁来完成呢?答案是:这些外围设备的驱动程序,所做的就是这个工作。这些外围设备的驱动,基本都由设备制造厂家或者微软来编写,我们散客,很少有理由编写这些操作硬件的驱动程序!
那么是不是说,驱动程序就没有必要学习了?作者说,不是,因为驱动程序可以完成应用程序无法完成的很多工作。例如文件的加密,解密,网络数据的检测等。还有,如果你想干点坏事的话,驱动层是最好不过,当然,要防止别人干坏事,也最好在驱动层做手脚,否则只在应用层的话,是很不靠谱的。
有些读者在看到这里就问,你老人家说了半天,我还是糊涂的!!!不要紧,慢慢的你就会明白。首先大概说下,计算机为什么要分为应用层和驱动层,也就是内核层。这是现代操作系统安全的需要。大家还记不记得DOS操作系统,这个操作系统没有内核层和应用层的划分,所有程序都一视同仁,无论哪个程序,都可以对硬件进行直接操作。这样的操作系统现在已经被淘汰,安全性太差,任何程序都可以无视一切屏障,通行无阻。后来为了安全的需要,就把操作系统分了两部分(内核层和应用层,其实硬件支持可以划分4层的,一般现在都只划分2层)。核心部分,在内核层运行,一般程序,在应用程运行,内核层和应用层的转换,由操作系统自己完成,应用程序不允许进行这样的转换。我不知道这个在操作系统中是如何完成保护的,但是,给一个裸机,我想通过编程,可以自由的在两种模式下进行转换,这个猜想不会有错,因为操作系统本身就不断在这两种模式之间进行切换。下面的猜想,你不要认为是正确的,但可以为你提供一种思路。如果是我写操作系统,那么我还要为我这个操作系统制作一个编译器,别人可以使用这个编译器对我的操作系统平台进行二次开发!我对Windows比较熟悉,Windows的著名开发工具就是Visual C,或者C++。在开发这个编译器时,我就把这些特权指令进行屏蔽,使得在编译器上就无法通过。这是一种办法,但这种办法实在不靠谱。为什么?因为我们可以查看微处理器的说明书,找出机器执行的二进制代码,然后直接编辑二进制文件(这个可以做到,虽然麻烦),然后,我再查看操作系统可执行文件的格式(例如windows下的PE格式文件),我自己手动填写二进制代码,旁路掉编译器,直接打造一个可执行文件,实现内核模式和应用模式的转换,是否可行?我没有做过这么无聊的试验,但我想这个试验不会成功,因为比我无聊的人很多!另外,微软肯定不会就这么点手段!有人说,是否有硬件设置,防止自由转换呢?我可以肯定的回答,没有!为什么呢?我们先假设,有这么一个硬件,确保只有操作系统能够执行这种转换。如何保证呢?其实涉及到硬件,有些问题反而简单,无非就是某个寄存器的状态,硬件要提供这种保证,也只能检查一些机器状态,如果满足状态,他就转换,否则,就不转换!这个转换所要求的状态,必然是公开的,为什么,因为CPU的生产厂家,希望自己的CPU不止一家公司应用,可以在很多地方用,那么你让人家使用自己的产品,关键的技术说明,必然提供。而这个内核层和应用层转换的方法,恰恰就是一个关键的,必须的技术,如何能够保密(除非不想让别人用)。上面一段话说明什么,说明,即使设置了硬件保护,最终,保护需要公开,那还保护个什么劲!那么微软如何做到,自由自己能够转换,而我们的程序不能呢?他保证这个的唯一途径,就是检查这个转换指令执行的环境,不是硬件环境,是软件环境!例如,这个指令代码的执行地址!当然,我相信,操作系统执行的检查远远不是这么简单的,可能复杂很多。如果软件环境满足条件,就进行转换,否则,就报错。以上是作者的随想,呵呵。看完后,你知道,操作系统有一个内核层,一个应用层,应用层的程序执行受些限制,而内核层则不受限制,那么我的目的就达到了。
今天就先到这里,有功夫我接着写
上面说了内核层和应用层,下面说下计算机的内存分配。Windows中,将应用程序的内存分成两部分,上半部分归应用层使用,下半部分供内核使用,内核程序可以访问所应用层的内存,而应用层的程序,不能访问内核部分的内存,当然,转换到内核模式下是可以的。这中设置也是安全的要求。32位的CPU,可以控制4G的内存,其中前2G为应用层内存,后2G为内核内存(一般是这样的,还有一种应用层使用前3G内存,内核使用后1G内存)。这不是说,你的计算机上有4G的物理内存,这是说,你的程序可以使用4G的内存。至于你的计算机到底有多少内存,对于你程序的执行,速度上是有形象的,但对于编程来说,我们可以不进行考虑(当然,不是说绝对不考虑,以后进行内核编程的时候,在必要的地方,会体现出来的,现在我们先认为,计算机的实际内存,我们可以先不考虑)。计算机的物理内存不足4G,但我们的程序可以使用4G的内存,这是为什么呢?建议读者认真阅读《Intel微处理器》或者《深入理解Windows操作系统》。这两本书里面都有答案。如果有兴趣,我可以将这部分内容翻译出来,供大家阅读。上面一段话,你只要记住,程序的内存空间分为内核部分和应用部分,内核部分只有在程序运行在内核状态下时才可以使用。另外内存还有分页内存和非分页内存的区别,这个以后再慢慢介绍。
现在我们再回过头来说,为什么要写内核程序。我们看看,计算机给应用程序设置了如此多的障碍,这个不允许做,那个不允许做,从作者的内心来说,感觉很不舒服,应用层程序限制了我的自由,我要进入内核进行编程,我要自由!这就是我想写驱动程序的动机。
Window留给用户的内核程序的接口,除了驱动程序,我还没有发现其他接口,如果哪位大神知道,不妨公布出来,当然,有人把Windows的一部分代码挖掉,替换成自己的代码,这个从理论上来说是可行的,也有人这么干过(例如,Windows操作系统是一个非实时的操作系统,在工业控制上,不能使用,有人就把Windows的内核替换掉,保留Windows的GUI部分,也就是应用程序图形接口部分,在工业控制上使用)。我们作为散客,从理论上讲,这个办法我们也可以用,但是,这个是需要人力和物力的投入的,不是个人所能够完成的,那么留给我们的,恐怕能够实际应用的也就是驱动程序接口了。
好了,这个问题就到这里,不管你满意不满意,反正我说完了。其中穿插了一点知识,都是作者的随想。至于上面介绍的真正技术上的知识,如果有必要,我会详细介绍,现在我们先开个头,不要搞的那么技术,那么正式,另外,以上作者很多词用的也不恰当,但作者希望大家不要抠字眼,主要理解里面的想法就可以了。
现在说第三个问题,驱动程序能干什么?答案是,他什么都可以干(当然,驱动程序不能帮你找女朋友,哈哈)。这个什么都可以干,指的是,计算机可以干什么,我们就可以编写驱动程序,让计算机干什么!这个应用程序是无法做到的!驱动程序,可以使我们回到DOS的自由时代!
现在,我们准备进进入计算机中,专门的“专业”的谈一些技术问题。首先我们来了解下计算机的结构。
第一节:计算机的基本结构和一些重要概念
上图中,虚线我们叫他地址总线,这个线的作用就是,选择内存和外部设备的,比方说,这个虚线状态是1,那么内存此时就处于被使用状态,另外的其他设备,例如外设1,2,3就处于断开状态,也就是CPU此时只和内存连接,其他设备都和CPU没有任何连接。当这个虚线处于状态2时,外设1和CPU连接,其他的都处于断开状态。同理,状态3,状态4,我就不一一解释了。
我们再来看看大虚线,这个我们叫控制总线,控制总线就是告诉外设和内存,你们应该干什么,例如,虚线(也就是地址总线)处于状态1,而大虚线,也处于状态1,此时,大虚线的状态1告诉内存,我要读数据了。当然,控制线的其他状态就表示其他意思。因为地址线在状态1,所以,除了内存外的其他外设对于控制线的状态不做相应(因为此时,这些外设的控制总线,也就是大虚线处于断开状态)。
我们在来看看实线,我们叫他数据总线,刚才不是说虚线,大虚线都处于1状态,表示要读取内存数据,此时,就通过数据总线,也就是实线,将数据从内存传递到CPU。
这个就是计算机三大总线的基本作用,其操作方式也是上述的方式,只不过比较复杂而已,没有我说的那么简单,但原理是一样的。如果想知道详细的,请参考专业书籍。
现在结合我们上面的大概介绍,来看看计算机如何工作,例如现在又下面一条语句:
MOV AX [1]
这条汇编指令的意思是,我要读取存放在地址1上的一个数据(数据的大小隐含在CPU寄存器AX中,也就是寄存器有多大,我们就读取多大的数据),那么此时,CPU将地址总线设置成1,控制总线设置成1,然后读取数据总线上的数据到AX寄存器中,这条指令计算机就算完成了。其他计算机指令执行的原理基本相同。另外,很多计算机的指令并不涉及到外部设备,例如ADD AX,BX 这个只要在CPU内部进行运算就可以了。(正因为如此,这条指令执行的速度非常之快)
下面我们在介绍计算机中,另外一个非常重要的概念,中断!
我们先来说说,计算机为什么要设置中断。
我们现在假设,外设1是一个键盘。那么键盘的作用就是将我们想要输入计算机的信息输入到计算机的CPU,进行处理(例如,我们想将我们的名字输入计算机,让计算机存储在内存中,或者某个寄存器中)。现在我们开始敲击键盘(那么计算机是如何知道我们想要将我们的名字存放在内存中呢?计算机根本不可能知道,因为此时我们还没有和计算机进行通信的手段,也就是俗话所得,你不说,我怎么知道你想要什么。并且,计算机也不可能知道我们什么时候想将自己的名字输入到计算机内存!还是因为我们没有告诉计算机,计算机不可能知道,因为计算机不是神!在这里我再强调一下,计算机不是神,他所知道的,必须是我们告诉他的,我们不告诉他,他不可能知道我们想干,目录什么,这是一条铁律,请大家记住)。现在我们知道,计算机对我们所做的一无所知(在我们敲击键盘的时候)。此时,计算机有2中方式,获取我们的信息,第一种,就是计算机不断检测键盘,看看有没有人敲击键盘,如果有人敲击键盘,立即响应,看看这个人想干什么(通过它输入的信息进行判断)。还有一种方式,就是当我们敲击键盘的时候,键盘先给给CPU发出一个信号,说,CPU,有人敲我,你看怎么办?那么这两种办法,那个好,那个不好,相信大家很好判断。第一种,CPU很忙,要抽出时间检测键盘,第二种,CPU根本不担心什么,只顾干自己的事情,等到收到键盘的信号,再做响应。所以计算机设计中,选择了第二种。他的操作顺序是这样的:CUP很忙,一直在工作,此时某个外部设备发出一个信号(一般是通过控制总线中的一条),来告诉CPU,外部设备中有一个有事情需要CPU处理,CPU然后再给所有的设备发出一个信号,问,谁呀?然后有事情的外部设备再告诉CPU,是我,外设1。然后CPU一看,噢,你小子呀,知道了,什么事情。。。。这样CUP和外设之间就开始处理数据了(这里具体涉及到了三大总线,数据,控制,地址总线中的那些,请参考专业书,我记不住,不好意思)。这种响应模式,其实就是中断,就是外部设备随时可以中断CPU的工作,让CPU和自己进行通信。
这时,有人就问了,那要是内存,外设1,外设2同时有事情,CPU怎么办?好办,我们将这些外部设备排个优先级,例如,外设1最高,外设2次之,外设3再次之,最后是内存。也就是说,当外设1有事情时,其他设备再着急,也只能干看了。这个优先级,就是计算机的中断优先级,这保证了外设和CPU之间通讯的有序性,不至于混乱。
后来,这个中断的概念又进一步发展,因为人们觉得这个方法很好。怎么发展呢?就是CPU所执行的所有任务,都有一个中断等级,也就是给CPU中执行的每一条指令,都赋予了一个中断等级!例如,我在执行MOV AX,BX时,计算机所处的中断等级,就是我执行这条指令的中断等级。我们计算机执行的程序中,百分之90以上的程序,都处于计算机中断等级中的倒数第二级。在这个等级上,我们的程序可能会随时被打断。但是我们是没有感觉的,因为计算机太快了,而且我们程序的执行,离不开这些中断所对应的外部设备的协助(举个例子:你玩网络游戏,那么计算机必然要从网卡上读取游戏服务器发送来的数据,不产生中断,能行吗?)。
又有人问了,那我们干嘛不将自己的程序放在最低的中断等级上?这个,我们总要给自己留一点余地吧,要是还有更不重要的任务,我们再往哪里放呢?这里就还真有这种任务,例如,磁盘碎片的整理。你说,你正在玩游戏,计算机突然中断,说我现在要整理磁盘,结果你人物被打死,装备掉了,你是不是有想砸计算机的冲动?所以,这个磁盘整理程序,中断等级一定要比你游戏程序的中断等级低!
要了解中断的详细概念,请参考专业书籍,这里只提供思路!
中断讲完了,不知道你理解了没有,如果还想有哪方面的补充,请回复一下,我可以补充的,哈哈。
下面说说计算机的磁盘存储器,内存,高速缓存和寄存器的的关系。