如何写一个吃很大内存的程序_C 语言如何调用硬件?

这是之前在知乎上回答的一个答案,现在同步到公众号上。

C/C++调用硬件抽象出来就是读、写硬件。
1.读取硬件的数据/状态。比如:读取硬盘的数据,键盘的按键。
2.向硬件写数据,比如:向显卡写数据绘制图形。

这个读写操作涉及到几个问题:
1.如何定位要访问的硬件,即寻址。
2.如何传递要读写的数据,即:数据传输方式。
这两个问题与硬件的体系结构有关。

一. 硬件寻址

要寻址,硬件就得先要有地址。但硬件本身是个较大的具体概念,它实际上是由内部的多个寄存器构成。我们说硬件寻址,说的就是寻址硬件内的寄存器。

硬件寄存器的编址方式一般有两种:
a.统一编址。
b.独立编址。

这两种编址方式的差异在于硬件寄存器的地址空间与内存地址空间是否为同一个地址空间。统一编址硬件寄存器与内存共用同一个地址空间。独立编址下,硬件寄存器使用单独的64KB的I/O地址空间。
ARM就是统一编址,Intel则是独立编址。但是,Intel架构的计算机现在引入了MMIO机制。允许操作系统将部分硬件寄存器的地址映射到一个内存地址空间中。有权限的代码也能使用内存地址直接访问硬件寄存器了。这种方式用于访问显卡这类有很大存储空间的硬件特别高效。


因为我只开发过Intel架构下的代码,下面的内容就只限于Intel架构

对于Intel架构的计算机,如果没有MMIO,就只能通过寄存器在I/O地址空间中的地址来访问硬件寄存器。I/O地址空间中的寄存器称为I/O端口,其地址称为I/O地址。Intel架构的机器只有一条36位(此处仅举例,实际位宽与CPU型号有关)的地址总线。即使是内存与硬件是独立编址,但访问它们的地址仍然是放在这条地址总线上的。那么,问题来了:如何得知某个时间地址总线上的地址是内存地址还是I/O端口呢?

答案在CPU上。CPU有个引脚称为M/IO引脚。它为高/低电平决定了此时地址总线上的地址是内存地址还是I/O地址。

接下来,还有个问题:既然硬件寄存器有I/O地址,那么我们怎么得知某个硬件各寄存器的地址呢?
这与硬件类型有关:
对于某些硬件设备,它的I/O地址是保留好的。比如:IDE硬盘的占用了0x1F0~0x1F7的端口地址范围, PCI配置空间占用了0x0CF8~0x0CFF。
对于早期接口的硬件设备,硬件的I/O地址与它在主板上接入CPU的位置有关。BIOS的的一个功能就是分配这些硬件设备的I/O地址。
对于PCI设备,它通过配置寄存器、基址寄存器支持操作系统为此设备动态分配寄存器的地址空间。

下图是Windows系统下I/O地址的分配情况。

57097a82c5df723277f7395dc4f17aa1.png

二. 数据传输方式

数据传输方式都是计算机组成原理上的内容了。对于Intel架构的计算机而言,在已知硬件寄存器I/O端口地址的情况下,通过IN/OUT指令即可访问硬件的寄存器来实现读写数据的功能。
在Window系统下,ms用硬件抽象层HAL封装了IN/OUT操作。HAL提供了以下函数。

READ_PORT_UCHAR, 
READ_PORT_SHORT,
READ_PORT_LONG
WRITE_PORT_UCHAR,
WRITE_PORT_SHORT,
WRITE_PORT_LONG

Windows驱动正是使用这些函数来I/O端口的。有了这层抽象,驱动就不用关心下面使用的是IN/OUT,还是内存操作了。

到此,我们就明确了硬件地址的意义,如何访问它通过这个地址访问硬件。那么,要读写的数据如何到达硬件呢?在CPU层面上来说,有两种处理方法:
a.将数据放在CPU寄存器中,然后经由IN/OUT指令到达硬件。如果数据量较大,就将数据拆分成若干份,写个循环慢慢传完。这种方式会占用大量CPU时间。
b.DMA方式。将存放数据的内存地址通过IN/OUT指令通知硬件。硬件在传入的内存读取/写完数据后,再以中断的方式通知CPU。这种方式通过DMA芯片执行数据传输操作,占用CPU时间较短,效率较高。

三.Windows下的驱动管理

最后说一下驱动程序。Windows下将硬件抽象为Device Object。硬件在内核中都通过Device Object来描述。驱动硬件的驱动程序被抽象为Driver Object。驱动程序在被内核的I/O子系统加载后,为它可处理的硬件设备创建Device Object进行管理。驱动则提供了若干个函数响应硬件设备的各种功能。驱动程序处理硬件功能的过程被抽象为DriverObject与DeviceObject之间的消息流动。

驱动与硬件设备匹配的过程十分复杂,根据硬件类型不同各有差异。这里我只介绍下大致的过程。
   硬件设备有两类:
   a. 不可热插拔的设备。如:PS/2键盘、鼠标,IDE/SATA硬盘,显卡等。
   b. 可热插拔的设备。如:USB接口的键盘、鼠标、移动硬盘等。

不可热插拔的设备的识别、匹配过程发生在开机时。开机时,BIOS进行硬件自检,按接口枚举出各个硬件设备。之后,Windows内核开始初始化。Windows I/O子系统根据BIOS传递过来的硬件信息进入识别,并加载匹配的匹配的驱动程序。匹配过程涉及到硬件的设备类、厂商等信息。

对于可热插拔的设备,它们可以系统启动前、启动后插入到接口上,并立即驱动。

实现热插拔依赖于特定的接口,如:USB接口。USB接口由USB bridge设备提供,本身不可热插拔。它也是在系统启动过程中匹配到驱动的。但在系统启动完成后,如果有USB设备上有设备接入,USB接口驱动程序的Driver Object会接收到一个AddDevice消息。它就知道有USB设备接入,根据USB接口协议与新接入的设备通信,识别设备的类、厂商、设备内是否自带驱动程序等信息。据此,USB驱动就能为这个设备加载对应的驱动程序,再由加载的程序程序为这个设备创建Device Object。这样,设备进入可用状态。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值