1- 设备驱动程序 (上)
1+ 驱动程序 (上)
22=========================================
33
44本节导读
55-----------------------------------------
66
7- 设备驱动程序概述
7+ 驱动程序概述
88----------------------------------------
99
10- 从操作系统架构上看,设备驱动程序与I /O设备靠的更近,离应用程序更远,这使得设备驱动程序需要站在协助所有进程的全局角度来处理各种I /O操作。这也就意味着在设备驱动程序的设计实现中 ,尽量不要与单个进程建立直接的联系,而是在全局角度对I/O设备进行统一处理。
10+ 从操作系统架构上看,驱动程序与I /O设备靠的更近,离应用程序更远,这使得驱动程序需要站在协助所有进程的全局角度来处理各种I /O操作。这也就意味着在驱动程序的设计实现中 ,尽量不要与单个进程建立直接的联系,而是在全局角度对I/O设备进行统一处理。
1111
12- 上面只是介绍了CPU和I/O设备之间的交互手段。如果从操作系统角度来看,我们还需要对特定设备编写设备驱动程序 。它一般需要完成如下一些功能:
12+ 上面只是介绍了CPU和I/O设备之间的交互手段。如果从操作系统角度来看,我们还需要对特定设备编写驱动程序 。它一般需要完成如下一些功能:
1313
14141. 设备初始化,即完成对设备的初始配置,分配I/O操作所需的内存,设置好中断处理例程
15152. 如果设备会产生中断,需要有处理这个设备中断的中断处理例程(Interrupt Handler)
2020
2121异步I/O操作是一个效率更高的执行模式,即应用程序发出I/O请求后,并不会等待此I/O操作完成,而是继续处理应用程序的其它任务(这个任务切换会通过运行时库或操作系统来完成)。调用异步I/O操作的应用程序需要通过某种方式(比如某种异步通知机制)来确定I/O操作何时完成。这部分可以通过协程技术来实现,但目前我们不会就此展开讨论。
2222
23- 编写设备驱动程序代码需要注意规避三方面的潜在风险的技术准备措施 :
23+ 编写驱动程序代码需要注意规避三方面的潜在风险的技术准备措施 :
2424
25251. 了解硬件规范:从而能够正确地与硬件交互,并能处理访问硬件出错的情况;
26- 2. 了解操作系统,由于设备驱动程序与它所管理的设备会同时执行 ,也可能与操作系统其他模块并行访问相关共享资源,所以需要考虑同步互斥的问题(后续会深入讲解操作系统同步互斥机制),并考虑到申请资源失败后的处理;
27- 3. 理解驱动程序执行中所在的可能的上下文环境:如果是在进行中断处理(如在执行 ``trap_handler `` 函数),那是在中断上下文中执行;如果是在代表进程的内核线程中执行后续的I/O操作(如收发TCP包),那是在内核线程上下文执行。这样才能写出正确的设备驱动程序 。
26+ 2. 了解操作系统,由于驱动程序与它所管理的设备会同时执行 ,也可能与操作系统其他模块并行访问相关共享资源,所以需要考虑同步互斥的问题(后续会深入讲解操作系统同步互斥机制),并考虑到申请资源失败后的处理;
27+ 3. 理解驱动程序执行中所在的可能的上下文环境:如果是在进行中断处理(如在执行 ``trap_handler `` 函数),那是在中断上下文中执行;如果是在代表进程的内核线程中执行后续的I/O操作(如收发TCP包),那是在内核线程上下文执行。这样才能写出正确的驱动程序 。
2828
2929
3030硬件系统架构
6666传递设备树信息
6767~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6868
69- 操作系统在启动后需要了解计算机系统中所有接入的设备,这就要有一个读取全部已接入设备信息的能力,而设备信息放在哪里,又是谁帮我们来做的呢?在 RISC-V 中,这个一般是由 bootloader,即 OpenSBI or RustSBI 固件完成的。它来完成对于包括物理内存在内的各外设的探测,将探测结果以 **设备树二进制对象(DTB,Device Tree Blob) ** 的格式保存在物理内存中的某个地方。然后bootloader会启动操作系统,即把放置DTB的物理地址将放在 ``a1 `` 寄存器中,而将会把 HART ID (**HART,Hardware Thread,硬件线程,可以理解为执行的 CPU 核 **)放在 ``a0 `` 寄存器上,然后跳转到操作系统的入口地址处继续执行。例如,我们可以查看 ``virtio_drivers `` crate中的在裸机环境下使用设备驱动程序的例子 。我们只需要给 `rust_main ` 函数增加两个参数(即 ``a0 `` 和 ``a1 `` 寄存器中的值 )即可:
69+ 操作系统在启动后需要了解计算机系统中所有接入的设备,这就要有一个读取全部已接入设备信息的能力,而设备信息放在哪里,又是谁帮我们来做的呢?在 RISC-V 中,这个一般是由 bootloader,即 OpenSBI or RustSBI 固件完成的。它来完成对于包括物理内存在内的各外设的探测,将探测结果以 **设备树二进制对象(DTB,Device Tree Blob) ** 的格式保存在物理内存中的某个地方。然后bootloader会启动操作系统,即把放置DTB的物理地址将放在 ``a1 `` 寄存器中,而将会把 HART ID (**HART,Hardware Thread,硬件线程,可以理解为执行的 CPU 核 **)放在 ``a0 `` 寄存器上,然后跳转到操作系统的入口地址处继续执行。例如,我们可以查看 ``virtio_drivers `` crate中的在裸机环境下使用驱动程序的例子 。我们只需要给 `rust_main ` 函数增加两个参数(即 ``a0 `` 和 ``a1 `` 寄存器中的值 )即可:
7070
7171.. code-block :: Rust
7272
@@ -179,7 +179,7 @@ CPU可以通过MMIO方式来对PLIC进行管理,下面是一下与PLIC相关
179179 };
180180
181181
182- 可以看到串口UART0的中断号是10,virtio设备的中断号是1~8。通过 ``dtc `` Device Tree Compiler工具生成的文本文件,我们也可以发现上述中断信号信息,以及基于MMIO的外设寄存器信息。在后续的设备驱动中 ,这些信息我们可以用到。
182+ 可以看到串口UART0的中断号是10,virtio设备的中断号是1~8。通过 ``dtc `` Device Tree Compiler工具生成的文本文件,我们也可以发现上述中断信号信息,以及基于MMIO的外设寄存器信息。在后续的驱动程序中 ,这些信息我们可以用到。
183183
184184
185185操作系统如要响应外设的中断,需要做两方面的初始化工作。首先是完成第三章讲解的中断初始化过程,并需要把 ``sie `` 寄存器中的 ``seie `` 位设置为1,让CPU能够接收通过PLIC传来的外部设备中断信号。然后还需要通过MMIO方式对PLIC的寄存器进行初始设置,才能让外设产生的中断传到CPU处。其主要操作包括:
@@ -195,10 +195,10 @@ CPU可以通过MMIO方式来对PLIC进行管理,下面是一下与PLIC相关
195195上述操作的具体实现,可以参考 ``plic.rs `` 中的代码。
196196
197197
198- 串口设备驱动程序
198+ 串口驱动程序
199199------------------------------------
200200
201- 完成上述前期准备工作后,我们就可以开始设计实现设备驱动程序程序了 。
201+ 完成上述前期准备工作后,我们就可以开始设计实现驱动程序程序了 。
202202首先我们要管理是物理上存在的串口设备。
203203串口(Universal Asynchronous Receiver-Transmitter,简称UART)是一种在嵌入式系统中常用的用于传输、接收系列数据的外部设备。串行数据传输是逐位(bit)顺序发送数据的过程。
204204
0 commit comments