设备即文件
在本实验中,为了统一地访问设备(device),我们可以把一个设备看成一个文件,通过访问文件的接口来访问设备。目前实现了 stdin 设备文件文件、stdout 设备文件、disk0 设备。stdin 设备就是键盘,stdout 设备就是控制台终端的文本显示,而 disk0 设备是承载 SFS 文件系统的磁盘设备。下面看看 ucore 是如何让用户把设备看成文件来访问。
设备的定义
为了表示一个设备,需要有对应的数据结构,ucore 为此定义了 struct device,如下:
可以认为struct device是一个比较抽象的“设备”的定义。一个具体设备,只要实现了d_open()打开设备, d_close()关闭设备,d_io()(读写该设备,write参数是true/false决定是读还是写),d_ioctl()(input/output control)四个函数接口,就可以被文件系统使用了。
// kern/fs/devs/dev.h
/*
* Filesystem-namespace-accessible device.
* d_io is for both reads and writes; the iobuf will indicates the direction.
*/
struct device {
size_t d_blocks;
size_t d_blocksize;
int (*d_open)(struct device *dev, uint32_t open_flags);
int (*d_close)(struct device *dev);
int (*d_io)(struct device *dev, struct iobuf *iob, bool write);
int (*d_ioctl)(struct device *dev, int op, void *data);
};
#define dop_open(dev, open_flags) ((dev)->d_open(dev, open_flags))
#define dop_close(dev) ((dev)->d_close(dev))
#define dop_io(dev, iob, write) ((dev)->d_io(dev, iob, write))
#define dop_ioctl(dev, op, data) ((dev)->d_ioctl(dev, op, data))这个数据结构能够支持对块设备(比如磁盘)、字符设备(比如键盘)的表示,完成对设备的基本操作。
但这个设备描述没有与文件系统以及表示一个文件的 inode 数据结构建立关系,为此,还需要另外一个数据结构把 device 和 inode 联通起来,这就是 vfs_dev_t 数据结构。
利用 vfs_dev_t 数据结构,就可以让文件系统通过一个链接 vfs_dev_t 结构的双向链表找到 device 对应的 inode 数据结构,一个 inode 节点的成员变量 in_type 的值是 0x1234,则此 inode 的成员变量 in_info 将成为一个 device 结构。这样 inode 就和一个设备建立了联系,这个 inode 就是一个设备文件。
ucore 虚拟文件系统为了把这些设备链接在一起,还定义了一个设备链表,即双向链表 vdev_list,这样通过访问此链表,可以找到 ucore 能够访问的所有设备文件。
注意这里的vdev_list对应一个vdev_list_sem。在文件系统中,互斥访问非常重要,所以我们将看到很多的semaphore。
我们使用iobuf结构体传递一个IO请求(要写入设备的数据当前所在内存的位置和长度/从设备读取的数据需要存储到的位置)
注意设备文件的inode也有一个inode_ops成员, 提供设备文件应具备的接口。
stdin设备
trap.c改变了对stdin的处理, 将stdin作为一个设备(也是一个文件), 通过sys_read()接口读取标准输入的数据。
注意,既然我们把stdin, stdout看作文件, 那么也需要先打开文件,才能进行读写。在执行用户程序之前,我们先执行了umain.c建立一个运行时环境,这里主要做的工作,就是让程序能够使用stdin, stdout。
这里我们需要把命令行的输入转换成一个文件,于是需要一个缓冲区:把已经在命令行输入,但还没有被读取的数据放在缓冲区里。这里遇到一个问题:每当控制台输入一个字符,我们都要及时把它放到stdin的缓冲区里。一般来说,应当有键盘的外设中断来提醒我们。但是我们在QEMU里收不到这个中断,于是采取一个措施:借助时钟中断,每次时钟中断检查是否有新的字符,这效率比较低,不过也还可以接受。
我们来看stdin设备的实现:
disk0设备
封装了一下ramdisk的接口,每次读取或者写入若干个block。
这些设备的实现看起来比较复杂,实际上属于比较麻烦的设备驱动的部分。
最后更新于
这有帮助吗?