最初からオペレーティングシステム。 レベル2(古い半分)

ファイルシステムを作成します。 ファイルシステムはそれ自体を書き込みません。 ラボのこの半分では、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を使用したディスクパーティションの物理レイアウトを示しています。







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が最も可能性が高いです。









これら2つのレコードに加えて、他のすべてのレコードはデータ領域の特定のクラスターに対応しています。 FATレコードはフルサイズで32ビットですが、使用されるのは28ビットのみです。 上位4ビットは無視されます。 また、値は次のとおりです。









クラスターチェーン



クラスターはクラスターのチェーンを形成します。 これは、本質的にクラスターのリンクリストです。 クラスターがデータに使用される場合、その値には次のクラスターへのリンクが含まれるか、チェーンの終わりを示す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/



からコードを読むことをお勧めします。









キャッシュデバイス









ドライブに直接アクセスするのは非常に高価な操作です。 したがって、すべてのアクセスはキャッシュされたセクターで実行されます。 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/



ディレクトリが見つかります:









ファイルシステムを実装するときは、必要なコードでこれをすべて補完する必要があります。 これらの構造のいずれかを、必要と思われる方法で補足することを恐れないでください。 ただし、既存のメソッドに対して提供されている特性または署名の定義を変更しないでください。







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



以外の名前のファイルにテストを追加することをお勧めします。







次のルールに従うこともお勧めします。









あなたが望む順序ですべてを行うことができます。 ただし、次の順序をお勧めします。







  1. mbr.rs



    解析MBRを実装します。
    おそらく実装には、 unsafe



    unsafe



    使用する必要があります。 ただし、1行で十分です。 最も可能性の高いスライス:: from_raw_parts_mut()またはmem :: transmute()mem::transmute()



    非常に強力なツールです。 できるだけ長く使用しないでください。 それ以外の場合は、何をしているかを完全に理解する必要があります。 Debug



    を実装するには、 Formatter



    debug_struct()



    を使用します。 CachedDevice



    Debug



    .
  2. EBPB ebpb.rs



    .
    MBR, unsafe



    .
  3. MBR EBPB. . BlockDevice



    Cursor<&mut [u8]>



    . :



     println!("{:#?}", x);
          
          



  4. CachedDevice



    vfat/cached.rs



    .
  5. VFat::from()



    vfat/vfat.rs



    .
    MasterBootRecord



    , BiosParameterBlock



    CachedDevice



    . MBR EBPB.
  6. FatEntry



    vfat/fat.rs



    .
  7. VFat::fat_entry



    , VFat::read_cluster VFat::read_chain



    .
    Cluster



    . . . . VFat::fat_entry



    .
  8. vfat/metadata.rs



    .
    Date



    , Time



    Attributes



    , . FAT . Timestamp



    Metadata



    , Entry



    , File



    Dir



    .
  9. 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-16 Vec<u16>



    .

    Dir::find()







    Dir::find()



    traits::Dir



    Dir



    . , Dir::find()



    . . eq_ignore_ascii_case() .
  10. File



    vfat/file.rs



    .
    , Cluster



    Shared<VFat>



    . traits::File



    File



    . entries()



    Dir



    .
  11. 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- ARM unsigned int



u32



Rust.





? [foreign-sync]



SD- ( sd_err



) . . ? , Rust unsafe



-. ? ?

: ! ( , ) ?






. 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



. :









. . . — , . , .







実装



os/kernel/src/shell.rs



. PathBuf



, . PathBuf



cd



. . , .







, , — . おめでとうございます!







必ずビンアロケーターを使用してください!



ほとんどの場合、ファイルシステムの実装はメモリを非常に集中的に使用します。メモリ不足を回避するには、必ずビンアロケーターを使用してください。割り当てるだけでなく、メモリを解放することもできます。





ヒント:既存のメソッドPathBuf



使用Path



し、目的に合わせて使用​​します。





ヒント:あなたは、具体的に注意を引く必要があります..



し、.



実装時にcd



そのようなこと。








All Articles