From 014b24606f09c593521a676ad78bbe58e9b960bd Mon Sep 17 00:00:00 2001 From: Yu Chen Date: Mon, 22 Mar 2021 20:20:14 +0800 Subject: [PATCH] add some comments in ch6/7 --- source/chapter6/1file-descriptor.rst | 6 +++++- source/chapter6/2pipe.rst | 4 ++++ source/chapter7/2fs-implementation.rst | 2 +- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/source/chapter6/1file-descriptor.rst b/source/chapter6/1file-descriptor.rst index 5ccb55b..123b5ce 100644 --- a/source/chapter6/1file-descriptor.rst +++ b/source/chapter6/1file-descriptor.rst @@ -9,6 +9,8 @@ 文件 ------------------------------------------- +.. chyyuu 可以简单介绍一下文件的起源??? + 在类 Unix 操作系统中,”**一切皆文件**“ (Everything is a file) 是一种重要的设计哲学。在这里,所谓的 **文件** (File) 就是指由内核管理并分配给进程让它可以与之交互的部分 I/O 资源,它大致可以分成以下几种: - **普通文件** (Regular File) 指的是储存在磁盘/硬盘等存储介质上的文件系统中的一般意义上的文件,可以被看成一个固定的字节序列; @@ -29,7 +31,7 @@ fn write(&self, buf: UserBuffer) -> usize; } -其中 ``UserBuffer`` 是我们在 ``mm`` 子模块中定义的应用地址空间中的一段缓冲区的抽象。它本质上其实只是一个 ``&[u8]`` ,但是它位于应用地址空间中,在内核中我们无法直接通过这种方式来访问,因此需要进行封装。然而,在理解抽象接口 ``File`` 的各方法时,我们仍可以将 ``UserBuffer`` 看成一个 ``&[u8]`` ,它同时给出了缓冲区的起始地址及长度。 +其中 ``UserBuffer`` 是我们在 ``mm`` 子模块中定义的应用地址空间中的一段缓冲区的抽象。它本质上其实只是一个 ``&[u8]`` ,但是它位于应用地址空间中,在内核中我们无法直接通过这种方式来访问,因此需要进行封装。然而,在理解抽象接口 ``File`` 的各方法时,我们仍可以将 ``UserBuffer`` 看成一个 ``&[u8]`` 切片,它是同时给出了缓冲区的起始地址及长度的一个胖指针。 ``read`` 指的是从文件中读取数据放到缓冲区中,最多将缓冲区填满(即读取缓冲区的长度那么多字节),并返回实际读取的字节数;而 ``write`` 指的是将缓冲区中的数据写入文件,最多将缓冲区中的数据全部写入,并返回直接写入的字节数。至于 ``read`` 和 ``write`` 的实现则与文件具体是哪种类型有关,它决定了数据如何被读取和写入。 @@ -118,6 +120,8 @@ 文件描述符与文件描述符表 -------------------------------------------- +.. chyyuu 可以解释一下文件描述符的起因??? + 每个进程都带有一个线性的 **文件描述符表** (File Descriptor Table) 记录所有它请求内核打开并可以读写的那些文件。而 **文件描述符** (File Descriptor) 则是一个非负整数,表示文件描述符表中一个打开的文件所处的位置。通过文件描述符,进程可以在自身的文件描述符表中找到对应的文件,并进行读写。当打开一个文件的时候,如果顺利,内核会返回给应用刚刚打开的文件的文件描述符;而当应用想关闭一个文件的时候,也需要向内核提供对应的文件描述符。 当一个进程被创建的时候,内核会默认为其打开三个文件: diff --git a/source/chapter6/2pipe.rst b/source/chapter6/2pipe.rst index 5f89d02..2b38c10 100644 --- a/source/chapter6/2pipe.rst +++ b/source/chapter6/2pipe.rst @@ -11,6 +11,8 @@ 首先来介绍什么是 **管道** (Pipe) 。我们可以将管道看成一个有一定缓冲区大小的字节队列,它分为读和写两端,需要通过不同的文件描述符来访问。读端只能用来从管道中读取,而写端只能用来将数据写入管道。由于管道是一个队列,读取的时候会从队头读取并弹出,而写入的时候则会写入到队列的队尾。同时,管道的缓冲区大小是有限的,一旦整个缓冲区都被填满就不能再继续写入,需要等到读端读取并从队列中弹出一些字符之后才能继续写入。当缓冲区为空的时候自然也不能继续从里面读取,需要等到写端写入了一些数据之后才能继续读取。 +.. chyyuu 进一步介绍一下pipe的历史??? + 我们新增一个系统调用来为当前进程打开一个管道: .. code-block:: rust @@ -176,6 +178,8 @@ 从内存管理的角度,每个读端或写端中都保存着所属管道自身的强引用计数,且我们确保这些引用计数只会出现在管道端口 ``Pipe`` 结构体中。于是,一旦一个管道所有的读端和写端均被关闭,便会导致它们所属管道的引用计数变为 0 ,循环队列缓冲区所占用的资源被自动回收。虽然 ``PipeRingBuffer`` 中保存了一个指向写端的引用计数,但是它是一个弱引用,也就不会出现循环引用的情况导致内存泄露。 +.. chyyuu 介绍弱引用??? + 管道创建 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/source/chapter7/2fs-implementation.rst b/source/chapter7/2fs-implementation.rst index 7722fc8..9d0375a 100644 --- a/source/chapter7/2fs-implementation.rst +++ b/source/chapter7/2fs-implementation.rst @@ -141,7 +141,7 @@ easy-fs 被从内核中分离出来,它的实现分成两个不同的 crate - ``addr_of_offset`` 可以得到一个 ``BlockCache`` 内部的缓冲区一个指定偏移量 ``offset`` 的字节地址; - ``get_ref`` 是一个泛型方法,它可以获取缓冲区中的位于偏移量 ``offset`` 的一个类型为 ``T`` 的磁盘上数据结构的不可变引用。该泛型方法的 Trait Bound 限制类型 ``T`` 必须是一个编译时已知大小的类型,我们通过 ``core::mem::size_of::()`` 在编译时获取类型 ``T`` 的大小并确认该数据结构被整个包含在磁盘块及其缓冲区之内。这里编译器会自动进行生命周期标注,约束返回的引用的生命周期不超过 ``BlockCache`` 自身,在使用的时候我们会保证这一点。 -- ``get_mut`` 与 ``get_ref`` 的不同之处在于它会会获取磁盘上数据结构的可变引用,由此可以对数据结构进行修改。由于这些数据结构目前位于内存中的缓冲区中,我们需要将 ``BlockCache`` 的 ``modified`` 标记为 true 表示该缓冲区已经被修改,之后需要将数据写回磁盘块才能真正将修改同步到磁盘。 +- ``get_mut`` 与 ``get_ref`` 的不同之处在于它会获取磁盘上数据结构的可变引用,由此可以对数据结构进行修改。由于这些数据结构目前位于内存中的缓冲区中,我们需要将 ``BlockCache`` 的 ``modified`` 标记为 true 表示该缓冲区已经被修改,之后需要将数据写回磁盘块才能真正将修改同步到磁盘。 ``BlockCache`` 的设计也体现了 RAII 思想, 它管理着一个缓冲区的生命周期。当 ``BlockCache`` 的生命周期结束之后缓冲区也会被从内存中回收,这个时候 ``modified`` 标记将会决定数据是否需要写回磁盘: -- GitLab