|
4 | 4 | 本章导读 |
5 | 5 | ----------------------------------------- |
6 | 6 |
|
7 | | -到第七章为止,应用程序仅仅能干输入输出字符,读写在磁盘上的数据这些事情,和我们常见的操作系统比起来,内在的一些重要因素,如进程等,大致都有了,但好像感知与交互的能力还不叫弱。 |
| 7 | +到第七章为止,我们设计实现的操作系统已经具备了传统操作系统中的内在重要因素,如进程、文件、地址空间等,应用程序也能通过操作系统输入输出字符,读写在磁盘上的数据。不过与我们常见的操作系统(如Linux,Windows等)比起来,好像感知与交互的I/O能力还比较弱。 |
8 | 8 |
|
9 | | -终于到了I/O设备管理这一章了。人靠衣裳马靠鞍,如果操作系统不能把计算机的外设功能给发挥出来,那应用程序感知外在环境的能力和展示能力都会大打折扣。比如基于中断机制的高效I/O处理,图形化的显示,这些操作系统新技能将在本章将会展现出来。 |
| 9 | +终于到了I/O设备管理这一章了。人靠衣裳马靠鞍,如果操作系统不能把计算机的外设功能给发挥出来,那应用程序感知外在环境的能力和展示内在计算的能力都会大打折扣。比如基于中断机制的高效I/O处理,图形化的显示,这些操作系统新技能将在本章展现出来。 |
10 | 10 |
|
11 | 11 | 其实早在第一章的时候,就非常简单介绍了QEMU模拟的RISC-V 64计算机中存在的外设:UART、时钟、virtio-net/block/console/gpu等。并且在第一章,我们就已经通过RustSBI间接地接触过串口设备了。但我们写的OS是通过RustSBI提供的一个SBI调用 ``SBI_CONSOLE_PUTCHAR`` 来完成字符输出功能的。 |
12 | 12 |
|
13 | 13 | 在第三章,为了能实现抢占式调度,引入了时钟这个外设,也是通过SBI调用 ``SBI_SET_TIMER`` 来帮助操作系统在固定时间间隔内获得控制权。而到了第五章,我们通过另外一个SBI调用 ``SBI_CONSOLE_GETCHAR`` 来获得输入的字符能力。这时的操作系统就拥有了与使用者进行简单字符交互的能力了。 |
14 | 14 |
|
15 | | -后来在第七章又引入了另外一个外设virtio-block设备,即一个虚拟的磁盘设备。还通过这个存储设备完成了对数据的持久存储,并在其上实现了方便用户访问持久性数据的文件系统。对virtio-block设备的I/O访问没有通过RustSBI来完成,而是直接调用了 ``virtio_driers`` crate中的 ``virtio-blk`` 设备驱动程序来实现的。但我们并没有深入分析这个设备驱动程序的具体实现。 |
| 15 | +后来在第七章又引入了另外一个外设virtio-block设备,即一个虚拟的磁盘设备。还通过这个存储设备完成了对数据的持久存储,并在其上实现了访问持久性数据的文件系统。对virtio-block设备的I/O访问没有通过RustSBI来完成,而是直接调用了 ``virtio_drivers`` crate中的 ``virtio-blk`` 设备驱动程序来实现的。但我们并没有深入分析这个设备驱动程序的具体实现。 |
16 | 16 |
|
17 | | -可以说在操作系统中,I/O设备管理无处不在,且I/O设备的操作系统相关代码在整个操作系统中的比例是最高的(Linux/Windows等都达到了75%以上),也是出错概率最大的地方。虽然前面的操作系统已经涉及了很多I/O设备访问的相关处理,但我们并没有对I/O设备进行比较全面的分析和讲解。这主要是由于各种I/O设备差异性比较大,操作系统很难象进程/地址空间/文件那样,对各种I/O设备建立一个通用和一致的抽象和对应的解决方案。 |
| 17 | +可以说在操作系统中,I/O设备管理无处不在,且与I/O设备相关的操作系统代码--设备驱动程序在整个操作系统中的代码量比例是最高的(Linux/Windows等都达到了75%以上),也是出错概率最大的地方。虽然前面章节的操作系统已经涉及了很多I/O设备访问的相关处理,但我们并没有对I/O设备进行比较全面的分析和讲解。这主要是由于各种I/O设备差异性比较大,操作系统很难象进程/地址空间/文件那样,对各种I/O设备建立一个一致通用的抽象和对应的解决方案。 |
18 | 18 |
|
19 | | -但I/O设备非常重要,各种I/O(输入输出)设备的存在才使得计算机的强大功能能够展现在大众面前。所以能够管理I/O设备的操作系统是让计算机系统在大众中普及的重要因素,比如对于手机而言,大众关注的不是CPU有多快,内存有多大,而是关注显示是否流畅,触摸是否敏捷这些外设带来的人机交互体验。而这些体验在很大程度上取决与操作系统对外设管理的效率。 |
| 19 | +但I/O设备非常重要,由于各种I/O(输入输出)设备的存在才使得计算机的强大功能能够展现在大众面前。所以能够管理各种I/O设备的操作系统是让计算机系统在大众中普及的重要因素,比如对于手机而言,大众关注的不是CPU有多快,内存有多大,而是关注显示是否流畅,触摸是否敏捷这些外设带来的人机交互体验。而这些体验在很大程度上取决于操作系统对外设的管理与访问效率。 |
20 | 20 |
|
21 | | -另外,对I/O设备的管理体现了操作系统最底层的设计机制,如中断,并发,异步,缓冲,同步互斥等。这对上层的进程,地址空间,文件等有着深刻的影响。所以在分析、设计和实现了进程,地址空间,文件这些经典的操作系统抽象概念后,我们需要再重新思考一下,具备I/O设备管理能力的操作系统应该如何设计,特别是是否能给I/O设备也建立一个操作系统抽象。带着这些问题来思考和时间,将会对操作系统有更全面的体会。 |
| 21 | +另外,对I/O设备的管理体现了操作系统最底层的设计机制,如中断,并发,异步,缓冲,同步互斥等。这对上层的进程,地址空间,文件等有着深刻的影响。所以在分析、设计和实现了进程,地址空间,文件这些经典的操作系统抽象概念后,我们需要再重新思考一下,具备I/O设备管理能力的操作系统应该如何设计,特别是是否能给I/O设备也建立一个操作系统抽象。如果同学带着这些问题来思考和实践,将会对操作系统有更全面的体会。 |
22 | 22 |
|
23 | 23 | .. note:: |
24 | 24 |
|
25 | 25 | **UNIX诞生是从磁盘驱动程序开始的** |
26 | 26 |
|
27 | | - 如果翻看UNIX诞生的历史,你会发现一个有趣的故事。Ken Tompson在退出Mulitics开发后,还是想做操作系统。他首先做的就是给PDP-7计算机的磁盘驱动器写了一个包含磁盘调度算法的磁盘驱动程序和来提高计算机的磁盘I/O读写速度。为了测试磁盘访问性能,Ken Tompson花了三周时间写了一个操作系统,这就是Unix的诞生。 |
| 27 | + 回顾UNIX诞生的历史,你会发现一个有趣的故事:贝尔实验室的Ken Tompson在退出Mulitics操作系统开发后,还是想做继续操作系统方面的探索。他先是给一台闲置的PDP-7计算机的磁盘驱动器写了一个包含磁盘调度算法的磁盘驱动程序,希望提高磁盘I/O读写速度。为了测试磁盘访问性能,Ken Tompson花了三周时间写了一个操作系统,这就是Unix的诞生。这说明是磁盘驱动程序促使了UNIX的诞生。 |
28 | 28 |
|
29 | | -本章的目标是深入理解I/O设备管理,并将站在I/O设备管理的角度来分析I/O设备的特征,操作系统与I/O设备的交互方式。接着会进一步通过串口,时钟,键盘鼠标,磁盘,图形显示等各种外设的具体实现来展现操作系统是如何管理I/O设备的,并展现设备驱动与操作系统内核其它重要部分的交互。 |
| 29 | +本章的目标是深入理解I/O设备管理,并将站在I/O设备管理的角度来分析I/O设备的特征,操作系统与I/O设备的交互方式。接着会进一步通过串口,磁盘,图形显示等各种外设的具体实现来展现操作系统是如何管理I/O设备的,并展现设备驱动与操作系统内核其它重要部分的交互。 |
30 | 30 |
|
31 | 31 |
|
32 | 32 | 实践体验 |
|
46 | 46 |
|
47 | 47 | $ make run |
48 | 48 |
|
49 | | -
|
| 49 | +.. image:: virtio-test-example.png |
| 50 | + :align: center |
| 51 | + :name: virtio-test-example |
50 | 52 |
|
51 | 53 | 本章代码树 |
52 | 54 | ----------------------------------------- |
|
92 | 94 | 本章代码导读 |
93 | 95 | ----------------------------------------------------- |
94 | 96 |
|
95 | | -本章涉及的代码主要与设备驱动相关,需要了解硬件,需要多遍阅读和运行测试相关代码。这里简要介绍一下在内核中添加设备驱动的大致开发过程。对于写设备驱动,首先需要大致了解对应设备的硬件规范。在本章中,主要有两类设备,一类是实际的物理设备 -- UART(QEMU模拟了这种NS16550A UART芯片规范);另外一类是一类虚拟的抽象设备 -- virtio deivce,本来用于虚拟机运行环境,目前被很多运行在虚拟环境中的操作系统支持,其virtio规范貌似成为了一种事实上的标准。 |
96 | | - |
| 97 | +本章涉及的代码主要与设备驱动相关,需要了解硬件,需要多遍阅读和运行测试相关代码。这里简要介绍一下在内核中添加设备驱动的大致开发过程。对于写设备驱动,首先需要大致了解对应设备的硬件规范。在本章中,主要有两类设备,一类是实际的物理设备 -- UART(QEMU模拟了这种NS16550A UART芯片规范);另外一类是一类虚拟的设备驱动 |
97 | 98 | 然后需要了解外设是如何与CPU连接的。首先是CPU访问外设的方式,在RISC-V环境中,把外设相关的控制寄存器映射为某特定的内存区域(即MMIO映射方式),然后CPU通过读写这些特殊区域来访问外设(即PIO访问方式)。外设可以通过DMA来读写主机内存中的数据,并可通过中断来通知CPU。外设并不直接连接CPU,这就需要了解RISC-V中的平台级中断控制器(Platform-Level Interrupt Controller,PLIC),它管理并收集各种外设中断信息,并传递给CPU。 |
98 | 99 |
|
99 | 100 | 对于设备驱动程序对外设的具体管理过程,大致会有初始化外设和I/O操作。理解两种操作和对应的关键数据结构,就大致理解外设驱动要完成的功能包含哪些内容。每个设备驱动的关键数据结构和处理过程有共性部分和特定的部分。建议从 ``virtio-drivers`` crate 中的 ``examples/riscv/src/main.rs`` 这个virtio设备的功能测试例子入手来分析。 |
100 | 101 |
|
101 | | -以 ``virtio-blk`` 存储设备为例,可以看到,首先是访问 ``OpenSBI`` (这里没有用RustSBI,用的是QEMU内置的SBI实现)提供的设备树信息,了解QEMU硬件中存在的各种外设,根据外设ID来找到 ``virtio-blk`` 存储设备;找到后,就进行外设的初始化,如果学习了 virtio规范(需要关注的是 virtqueue、virtio-mmio device, virtio-blk device的描述内容),那就可以看出代码实现的初始化过程和virtio规范中的virtio设备初始化步骤基本上是一致的,但也有与具体设备相关的特定初始化内容,比如分配 I/O buffer等。初始化完毕后,设备驱动在收到上层内核发出的读写扇区/磁盘块的请求后,就能通过 ``virtqueue`` 传输通道发出 ``virtio-blk`` 设备能接受的I/O命令和I/O buffer的区域信息; ``virtio-blk`` 设备收到信息后,会通过DMA操作完成磁盘数据的读写,然后通过中断或其他方式让设备驱动知道命令完成或命令执行失败。而 ``virtio-gpu`` 设备驱动程序的设计实现与 ``virtio-blk`` 设备驱动程序类似。 |
| 102 | +以 ``virtio-blk`` 存储设备为例,可以看到,首先是访问 ``OpenSBI`` (这里没有用RustSBI,用的是QEMU内置的SBI实现)提供的设备树信息,了解QEMU硬件中存在的各种外设,根据外设ID来找到 ``virtio-blk`` 存储设备;找到后,就进行外设的初始化,如果学习了 virtio规范(需要关注的是 virtqueue、virtio-mmio device, virtio-blk device的描述内容),那就可以看出代码实现的初始化过程和virtio规范中的virtio设备初始化步骤基本上是一致的,但也有与具体设备相关的特定初始化内容,比如分配 I/O buffer等。初始化完毕后,设备驱动在收到上层内核发出的读写扇区/磁盘块的请求后,就能通过 ``virtqueue`` 传输通道发出 ``virtio-blk`` 设备能接收的I/O命令和I/O buffer的区域信息; ``virtio-blk`` 设备收到信息后,会通过DMA操作完成磁盘数据的读写,然后通过中断或其他方式让设备驱动知道命令完成或命令执行失败。而 ``virtio-gpu`` 设备驱动程序的设计实现与 ``virtio-blk`` 设备驱动程序类似。 |
102 | 103 |
|
103 | | -注:目前还没有提供相关的系统调用来方便应用程序访问这些外设。 |
| 104 | +注:目前还没有提供相关的系统调用来方便应用程序访问virtio-gpu外设。 |
0 commit comments