ファイルシステムを作成します。 ファイルシステムはそれ自体を書き込みません。 ラボのこの半分では、FAT32ファイルシステムを実装し、SDカードドライバーをそれに固定し、インタラクティブシェルを介して少しやり取りします。
最も若い部分 。 カットの下で続けた。
フェーズ2:32ビット脂質
このフェーズでは、FAT32ファイルシステムを実装します。 現時点では排他的に読み取り専用です。 主な作業は、 2-fs/fat32
ます。
ディスクとファイルシステム
ディスク上のデータは、1つ以上のファイルシステムによって管理されます。 メモリアロケータと同様に、ファイルシステムはメモリの管理、割り当て、および解放を行います。 唯一の違いは、これは高速RAMではなく、低速で不揮発性のメモリであることです。 つまり、すべての変更は将来いつでも保存されます。 コンピューターの再起動後を含む。 多くの異なるファイルシステムがあります。 Linuxには、EXT4があります。 MacOSにはHFS +とAPFSがあります。 WindowsにはNTFSがあります。 一部のファイルシステムは、一度に複数のOSに実装されます。 FAT32はそのようなものです。 Linux、macOS、Windowsを含むすべての主要なオペレーティングシステムに実装されています。 元々は、DOSの後のバージョンとWindowsの前のバージョンで使用されていました。 FAT32の主な利点は、どこにでもあることです。 これは、最もクロスプラットフォームなファイルシステムの1つです。
ディスク上に複数のファイルシステムを配置できるようにするために、この同じディスクをパーティションに分割できます。 各セクションは、異なるファイルシステム用に個別にフォーマットできます。 ディスクをパーティション分割するために、ディスクは、どのセクションが開始するか、ダウンロードされる場所、およびこのセクションが使用するファイルシステムのタイプを特定の場所に書き込みます。 1つの一般的なシステムは、マスターブートレコード、または単にMBRです。 MBRには、セクションを説明する4つのエントリのテーブルが含まれています。 ただし、一部のセクションは使用済みとして宣言されない場合があります。 GPTのような若干新しいパーティションスキームがあります。これは、特に4つ以上のパーティションをサポートします。
この割り当てでは、1つのFAT32パーティションを含むディスクからMBRを読み取るコードを実装します。 この組み合わせは、ラズベリーによって使用されます。MBRとFAT32も使用されます。
ディスクのパーティション分割
次の図は、MBRとFAT32を使用したディスクパーティションの物理レイアウトを示しています。
FAT構造 PDFには、これらの構造のサイズと内容に関する必要な情報がすべて含まれています。 最低限必要な説明とともに。 ファイルシステムを実装するときにドキュメントを使用します。 さらに、対応するウィキペディアの記事を調べることも役立ちます 。
マスターブートレコード
MBRは常にディスクのゼロセクターにあります。 MBRには4つのパーティションエントリが含まれます。 これらの各エントリには、セクションのタイプ、セクター内のセクションのオフセット、およびこのセクションがブート可能かどうかなどのさまざまなフラグが含まれます。 CHS(シリンダー、ヘッド、セクター)などの他のすべてのフィールドは完全に無視できます。 これは、ほとんどの最新の実装が行うことです。 FAT32のパーティションタイプが0xB
または0xB
あることも注目に値します。
拡張BIOSパラメータブロック
FAT32パーティションの最初のセクターには、BIOSパラメーターの拡張ブロックが含まれています。 頭字語EBPB。 このブロック自体は、BIOSまたはBPBパラメーターブロックで始まります。 一緒に、FATファイルシステムに必要なすべてのレイアウトオプションを決定します。
EBPBには、特別な注意を払う価値のある領域が1つあります。 予約済みセクターの数を決定するもの。 これは、FATが見つかるセクターのFAT32セクションの先頭からのオフセットです。 最後のFATの直後に、クラスターのデータを含む領域があります。 次に、FAT、データ領域、クラスターをさらに詳しく見ていきます。それだけです。
クラスター
FATファイルシステムに保存されているすべてのデータはクラスターに分割されます。 EBPBには、クラスター内の各クラスターのセクター数を確認できるフィールドがあります。 クラスターの番号付けは2から始まります。図からわかるように、クラスター2のデータはデータ領域の先頭にあります。 クラスター3のデータは、クラスター2の直後に配置されます。
ファイル割り当てテーブル
FATはファイルアロケーションテーブルの略です。 ファイル配布テーブル。 FATという名前に基づいて、これはFATレコードのテーブル(配列)です。 FAT32では、このような各書き込みのサイズは32ビットです。 このテーブル全体のサイズは、EPBPのFATあたりのセクターとセクターあたりのバイトフィールドによって決まります。 冗長性のために、ファイルシステムに複数のFATが存在する場合があります(聖なるバックアップの名前で!)。 テーブルの数はEPBPでも確認できます。 FATのフィールド番号を参照してください。
0と1の番号が付けられたレコードに加えて、各FATレコードがクラスターの状態を決定します。 注2はクラスター2のステータスを示します。ドリンク3はクラスター3のステータスを決定します。 各クラスターには独自のFATレコードがあります。
エントリ0と1が最も可能性が高いです。
- エントリ0:
0xFFFFFFFN
、これはIDです。 - レコード1:クラスターチェーンの終了(EOC)マーカー。
これら2つのレコードに加えて、他のすべてのレコードはデータ領域の特定のクラスターに対応しています。 FATレコードはフルサイズで32ビットですが、使用されるのは28ビットのみです。 上位4ビットは無視されます。 また、値は次のとおりです。
-
0x?0000000
:未使用のクラスターを空にします。 -
0x?0000001
:予約済み。 -
0x?0000002
0x?FFFFFEF
:データクラスター。 特定の値は、チェーン内の次のクラスターです。 -
0x?FFFFFF0
0x?FFFFFF6
:予約済み。 -
0x?FFFFFF7
:予約済みまたは破損したクラスター。 -
0x?FFFFFF8
0x?FFFFFFF
:チェーンの最後のクラスター。 EOCマーカーでなければなりません。
クラスターチェーン
クラスターはクラスターのチェーンを形成します。 これは、本質的にクラスターのリンクリストです。 クラスターがデータに使用される場合、その値には次のクラスターへのリンクが含まれるか、チェーンの終わりを示すEOCマーカーになります。
例として、8つのFATレコードを含むチャートを考えます。
クラスターは色で色分けされているため、どのチェーンに属しているかを簡単に把握できます。 最初の2つのエントリはIDとEOCです。 レコード2は、対応するクラスターがデータクラスターであり、このチェーン(緑色)が1クラスターのサイズであることを示しています。 レコード3は、クラスター3にデータが含まれており、チェーンの次(青)がクラスター5になり、クラスター6がこのチェーンを切断することを示します。 同様に、クラスター7とクラスター5はチェーンを形成します(赤)。 クラスター番号8は無料で使用されていません。
カタログと記録
クラスターチェーンは、ファイルまたはディレクトリのデータです。 ディレクトリは、ファイル名と他のすべてのメタデータを含む特別なファイルの本質です。 ガジェット内には、カタログエントリの配列があります。 各エントリには、名前、開始クラスタ、およびこのエントリがディレクトリか単なるファイルかが含まれます。
他のディレクトリのエントリに関連付けられていない特別なディレクトリが1つあります。 ルートディレクトリ。 ルートディレクトリの開始クラスターは、EBPBにあります。 これにより、他のすべてのファイルとディレクトリの場所を特定できます。
歴史的な理由により、物理ディレクトリの各エントリは、2つの異なる方法で解釈できます。 属性フィールドは、これらのメソッドのいずれかを指します。 これら2つのバリエーションは次のとおりです。
- 通常のカタログエントリ。 (通常のディレクトリエントリ)
- 長いファイル名のエントリ。 (長いファイル名のエントリ)
11文字より長いファイル名を使用するために、長いファイル名(LFN)がFAT32に追加されます。 エントリの名前が11文字を超える場合、LFNエントリが前に付きます。 ただし、これらのレコードは物理的にソートされていません。 代わりに、シーケンスを決定するためのフィールドが含まれています。 したがって、LFNレコードの物理的な順序は信頼できません。
だから
続行する前に、FAT構造を理解する必要があります。 その後、次の質問に答えてください。
最初のセクターにMBR構造が含まれているかどうかを確認する方法は? [mbr-magic]
ディスクの最初のセクターにMBRが含まれていない場合があります。 MBRが存在するかどうかを確認するにはどうすればよいですか?
FAT32クラスターの最大数はいくつですか? [最大クラスター]
FAT32の設計には、いくつかの制限があります。 FAT32のクラスターの最大数と、これらの制限はどこから来ますか? FAT16を使用する場合、同じ制限または他の制限がありますか?
単一ファイルの最大サイズは? [最大ファイルサイズ]
最大ファイルサイズに制限はありますか? もしそうなら、最大ファイルサイズは何であり、この境界を決定するものは何ですか?
ヒント :ディレクトリ内のエントリの構造を見てください。
LFNレコードがあるかどうかを判断する方法 [lfn-identity]
ディレクトリ内のエントリをよく見ると、LFNが目の前にあるのか、通常のエントリにあるのかを正確に判断するのはどのバイトですか? 具体的には、これらのバイトは何で、どのような値が必要ですか?
/a/b/c.txt
[マニュアル検索] を見つけるにはどうすればよいですか
EBPBを念頭に置いて、/a/b/c.txt
ファイルの初期クラスターを見つけるために実行するすべての手順を説明します。
コード構造
ファイルシステムを書くことは非常に奇妙なことです。 FAT32は、それを読むだけですが、例外ではありません。 2-fs/fat32
提供されるコードは基本的に基本構造を提供しますが、多くの設計上の決定と実装の大部分は完全にあなたのものです。
次に、すでに準備が整っているものについて説明します。 fat32/src
ディレクトリからコードを読み取ります。
ファイルシステムの特性
そこで、 traits
モジュールを見つけることができます。 エントリポイントはtraits/mod.rs
そこには、約7つの特性と1つの構造があります。 ファイルシステムを実装する場合、これをすべて実装します。
ほとんどの特性の架空の実装を提供するDummy
構造が1つあります。 このタイプはスタブとして使用できます。 コードをよく見ると、このスタブはいくつかの場所で使用されています。 たぶんあなたは便利になるでしょう。
次の順序でtraits/
からコードを読むことをお勧めします。
-
traits/block_device.rs
からのBlockDevice
。 ファイルシステムは、ジェネリックによって物理/仮想ストレージから分離されます。 つまり、ファイルシステムはどのデバイスでも動作しますが、このデバイス自体はBlockDevice
実装しBlockDevice
。 実装/テストプロセスでは、通常のファイルの上にBlockDevice
実装を使用できます。 もちろん、便利さの名において! しかし、BlockDevice
は、BlockDevice
SDカードドライバーをEMMCコントローラーと一緒にラップします。それだけです。 違いはほとんど目立ちません。 -
traits/fs.rs
からのFile
、Dir
およびEntry
これらの特性は、ファイル、ディレクトリ、またはそれらの一般化がファイルシステムにどのような最小限のプロパティを持つべきかを決定します。 それらが互いに依存していることに注意してください。 たとえば、Entry
プロパティは、関連付けられたFile
タイプを使用しFile
。 -
traits/fs.rs
FileSystem
この特性は、ファイルシステムのプロパティを定義します。 他の特性へのバインディングを通じて含める。 たとえば、このファイルシステムのFile
を実装するタイプが必要です。 これにより、各FileSystem
実装に対して1つのFile
、Dir
およびEntry
実装のみが存在することが保証されFileSystem
。 -
traits/metadata.rs
からのMetadata
とTimestamp
。 各Entry
は、ファイルまたはディレクトリに関する情報を取得できるメタデータに関連付ける必要があります。 メタデータはこのメタデータを担当します。 そして、Timestamp
特定の瞬間の一連のプロパティを定義します。 この特性は、ファイル作成時間などに使用されます。
キャッシュデバイス
ドライブに直接アクセスするのは非常に高価な操作です。 したがって、すべてのアクセスはキャッシュされたセクターで実行されます。 CachedDevice
構造は、 vfat/cache.rs
。 セクターキャッシュへの透過的かつ明示的なアクセスを提供します。 本質的に、これは内部的にHashMap
をストレージとして使用するBlockDevice
ラッパーです。 HashMap
のキーは、セクター番号になります。 CachedDevice
を実装すると、 BlockDevice
キャッシュバージョンとして透過的に使用できます。 さらに、 get()
およびget_mut()
メソッドが提供され、キャッシュされたセクターを直接参照できます。
さらに、 CachedDevice
定義されている論理セクターと物理セクター間の対応を監視するには、 CachedDevice
構造がCachedDevice
。 このためにvirtual_to_physical()
メソッドが提供されvirtual_to_physical()
ます。 これと同じ方法を使用して、特定の論理セクターについて読み取る必要がある物理セクターの数を決定する必要があります。
有用性
util.rs
ファイルには、スライス( &[T]
)および動的配列( Vec<T>
)の1つの有用な特性とその実装が含まれています。 これを使用して、特定の条件を維持しながら相互に転送できます。 たとえば、 &[u32]
を&[u8]
にキャストするには&[u8]
次を使用できます。
use util::SliceExt; let x: &[u32] = &[1, 2, 3, 4]; assert_eq!(x.len(), 4); let y: &[u8] = unsafe { x.cast() }; assert_eq!(y.len(), 16);
MBRおよびEBPB
mbr.rs
構造は、 mbr.rs
ファイルにあります。 彼女はBlockDevice
からMBRを読み取り、分析する責任があります。 同様に、 BiosParameterBlock
構造体を使用できます。 ファイルvfat/ebpb.rs
にあります。 彼女は、FAT32のBPBおよびEBPBセクションの読み取りと分析を担当しています。
共有
vfat/shared.rs
のShared<T>
構造は、タイプT
へのセキュアな可変アクセスに使用できますT
ファイルシステムの実装に役立ちます。 特に、コードのさまざまな部分からFSへのアクセスを共有する機能が必要な場合。 先に進む前に、 Shared<T>
使用する方法と理由を理解してください。
ファイルシステム
ファイルシステム自体のカーネルは、 vfat/vfat.rs
ファイルにあります。 明らかにこれはVFat
構造です。 ご覧のとおり、構造にはCachedDevice
が含まれていCachedDevice
。 実装は、提供されたBlockDevice
をCachedDevice
ラップする必要がCachedDevice
ます。
VFATとは何ですか?
VFATは、FAT32の前身であるMicrosoftの別のファイルシステムです。 さまざまな歴史的理由から、これはFAT32と同義語になりました。 常に正しい名前とは限らず、この愚かな伝統を続けます。
タイプ&Shared<VFat>
のFileSystem
プロパティの部分的な実装&Shared<VFat>
すでに存在します。 さらに、 from()
メソッドがShared<VFat>
返すことに気付くかもしれません。 主なタスクは、 from()
メソッドの実装、および&Shared<VFat>
必要なFileSystem
プロパティを完了することです。 これは、ファイルシステムプロパティの必要な部分を実装する他の構造の実装につながります。
別のvfat/
ディレクトリが見つかります:
-
error.rs
FAT32
初期化中に発生する可能性のあるError
含むError
列挙が含まれます。 -
file.rs
File
構造のプレハブが含まれています。これは、traits::File
traitを実装する必要があります。 -
dir.rs
file.rs
似ていfile.rs
さらに、構造体がディスクに書き込まれるときに、構造体の空白が含まれます。 -
entry.rs
Entry
構造のEntry
が含まれます。これは、traits::Entry
traitを実装する必要があります。 -
metadata.rs
生ファイルのプロパティを操作するためのData
、Time
、Attributes
構造が含まれています。 また、Timestamp
の不完全な構造、Metadata
、traits
モジュールからの対応する特性を実装する必要があります。 -
fat.rs
FatEntry
構造が含まれます。 この非常に構造はFATレコードをラップし、対応するFATレコードを簡単かつ簡単に読み取るために使用できます。 -
cluster.rs
物理クラスター番号をラップし、論理クラスター番号の読み取りに使用できるクラスター構造が含まれています。
ファイルシステムを実装するときは、必要なコードでこれをすべて補完する必要があります。 これらの構造のいずれかを、必要と思われる方法で補足することを恐れないでください。 ただし、既存のメソッドに対して提供されている特性または署名の定義を変更しないでください。
vfat.rs
から始まるすべてのコードを読んで、そこで何が起こっているのかを理解してください。
実装
これで、FAT32ファイルシステムを実装するために必要なものがすべて揃いました。 最適な順序で実装を行うことができます。
提供されているすべてのブランクを必ず更新してください!
すべてのリポジトリのコピーが最新であることを確認してください。git pull
を使用して2-fs
とos
の最新バージョンを入手し、必要なものをすべて修正します。
実装をテストするための厳密なテストセットを提供します。 テストを実行make clean && make fetch
前に、 2-fs
ディレクトリでmake clean && make fetch
実行します。 複数のファイルを2-fs/files/resources/
アップロードします。 これらのファイルは単体テストで使用されます。 このディレクトリには、MBR、EBPB、FAT32を含むイメージと、実装のさまざまな部分をテストするために使用されるハッシュがあります。 Linuxの* BlessやmacOSのHex Fiendなどの16進エディターを使用して画像を分析すると便利な場合があります。
テストは、 cargo test
を使用して実行できます。 デバッグメッセージを表示するには、 cargo test -- --nocapture
実行します。 これにより、 stdout
とstderr
インターセプトが防止されます。 さらに、必要と思われる量のテストを自由に追加できます。 マージの競合を防ぐため、 tests.rs
以外の名前のファイルにテストを追加することをお勧めします。
次のルールに従うこともお勧めします。
- 可能な限り意味のあるタイプを使用してください。 たとえば、時間フィールドに
u16
を使用する代わりに、Time
構造を使用できます -
unsafe
ものはできるだけ避けてください。 実装では、unsafe
ではunsafe
合計4つの非union
文字列と3つの文字列を使用してunion
を処理します。 実装はこれに従うようにしてください。 - あらゆる種類のヘルパーメソッドを使用して重複を避けます。 一般的な動作をヘルパーメソッドに入れると便利な場合がよくあります。 それが理にかなっているときにそれをしてみてください。
- 実装がクラスターまたはセクターサイズに依存しないことを確認してください。 セクターサイズまたはクラスターサイズの特定の値をハードコアにしないでください。 実装は、EBPBから取得した512の倍数のクラスターおよびセクターサイズで動作する必要があります。
- 不必要に2回バッファリングしないでください。 セクターが既にキャッシュにある場合は、メモリーにセクターを読み込まないでください。 狂信なくメモリを使用してみてください。
あなたが望む順序ですべてを行うことができます。 ただし、次の順序をお勧めします。
-
mbr.rs
解析MBRを実装します。 おそらく実装には、unsafe
でunsafe
使用する必要があります。 ただし、1行で十分です。 最も可能性の高いスライス:: from_raw_parts_mut()またはmem :: transmute() 。mem::transmute()
非常に強力なツールです。 できるだけ長く使用しないでください。 それ以外の場合は、何をしているかを完全に理解する必要があります。Debug
を実装するには、Formatter
debug_struct()
を使用します。CachedDevice
Debug
. - EBPB
ebpb.rs
. MBR,unsafe
. - MBR EBPB. .
BlockDevice
Cursor<&mut [u8]>
. :
println!("{:#?}", x);
-
CachedDevice
vfat/cached.rs
. -
VFat::from()
vfat/vfat.rs
.MasterBootRecord
,BiosParameterBlock
CachedDevice
. MBR EBPB. -
FatEntry
vfat/fat.rs
. -
VFat::fat_entry
, VFat::read_clusterVFat::read_chain
.Cluster
. . . .VFat::fat_entry
. -
vfat/metadata.rs
.Date
,Time
Attributes
, . FAT .Timestamp
Metadata
,Entry
,File
Dir
. -
Dir
vfat/dir.rs
Entry
vfat/entry.rs
.
Dir
,Cluster
Shared<VFat>
.File
vfat/file.rs
. ,Iterator<Item=Entry>
entries()
.entries()
unsafe
.VecExt
SliceExt
. FAT — ,Dir
.
Entry
LFN, ,union
.VFatDirEntry
. Rust . .
. , , .union
. .
.
, LFN, , ..
.
UTF-16 LFN.decode_utf16()
. UTF-16Vec<u16>
.
Dir::find()
Dir::find()
traits::Dir
Dir
. ,Dir::find()
. . eq_ignore_ascii_case() . -
File
vfat/file.rs
. ,Cluster
Shared<VFat>
.traits::File
File
.entries()
Dir
. -
VFat::open()
vfat/vfat.rs
. components() ,Path
. , ,std
, , .read_dir()
,is_file()
,is_dir()
.
Dir::find()
.VFat::open()
. 17 .Dir
.
, - , , . ! , , .
SD-
SD- Raspbrerry Pi 3, Foreign function interface FFI . FFI Rust 19.1 Rust . . os/kernel/src/fs
.
Foregin Function Interface
FFI Rust , . , Rust, extern
:
extern { static outside_global: u32; fn outside_function(param: i16) -> i32; }
outside_function
outside_global
. :
unsafe { let y = outside_function(10); let global = outside_global; }
, unsafe
. Rust , . . , , Rust , . outside_function
outside_global
. .
Rust , ( ) . Rust (mangles) , . . , , . #[no_mangle]
:
#[no_mangle] fn call_me_maybe(ptr: *mut u8) { .. }
() :
void call_me_maybe(unsigned char *); call_me_maybe(...);
Rust ? [foreign-safety]
, Rust , . , Rust , Rust , , Rust.
Rust ? [mangling]
. C++ Rust . , ? , , Rust .
SD-
SD- os/kernel/ext/libsd.a
. . つまり . os/kernel/src/sd.rs
, .
wait_micros
, . . . :
/* * Sleep for `us` microseconds. */ void wait_micros(unsigned int us);
— API Rust-. Sd
, SD- new()
. BlockDevice
Sd
. unsafe
. , MBR kmain
. , . , , .
: 64- ARMunsigned int
u32
Rust.
? [foreign-sync]
SD- (sd_err
) . . ? , Rustunsafe
-. ? ?
: ! ( , ) ?
. kernel/src/fs/mod.rs
.
, . , . , , static FILE_SYSTEM: FileSystem
kernel/src/kmain.rs
. , .
. . FileSystem
kernel/src/fs/mod.rs
, FAT32 SD-. Sd
( BlockDevice
) initialize()
. FileSystem
, VFat
. , kmain
.
, ( /
) SD-. , — .
4: Mo'sh
cd
, pwd
, ls
cat
. os/kernel/src/shell.rs
.
. ( cwd
current working directory) — , . cwd
/a
, hello
/a/hello
. cwd
/a/b/c
, hello
/a/b/c/hello
. /
, . . /hello
hello
.
cd <dir>
. cd /hello/there
, cwd
/hello/there
. cd you
, cwd
cd /hello/there/you
.
. , cwd
.
チーム
, : cd
, pwd
, ls
cat
. :
-
pwd
(print the working directory). . -
cd <directory>
(change (working) directory).directory
.directory
. -
ls [-a] [directory]
(list the files in a directory). .-a
directory
.-a
, . .directory
, .directory
. .-a
directory
. . , . -
cat <path..>
(concatenate files).path
. . — . UTF-8, .
. . . — , . , .
実装
os/kernel/src/shell.rs
. PathBuf
, . PathBuf
cd
. . , .
, , — . おめでとうございます!
必ずビンアロケーターを使用してください!
ほとんどの場合、ファイルシステムの実装はメモリを非常に集中的に使用します。メモリ不足を回避するには、必ずビンアロケーターを使用してください。割り当てるだけでなく、メモリを解放することもできます。
ヒント:既存のメソッドPathBuf
を使用Path
し、目的に合わせて使用します。
ヒント:あなたは、具体的に注意を引く必要があります..
し、.
実装時にcd
。
そのようなこと。