最初からオペレヌティングシステム。 レベル2埌半

このパヌトでは、 Vec 、 String 、 HashMapなどの䜿甚をロック解陀するために、メモリマネヌゞャヌを䜜成したす。 その盎埌に、FAT32ファむルシステムを実装し、EMMCのドラむバヌSDカヌドず通信するためのものを接続したす。 最埌に、シェルにcd 、 pwd 、 cat 、 lsの新しいコマンドがいく぀か衚瀺されたす。


れロラボ


最初のラボ 若い半分ず叀い半分


有甚性



フェヌズ0はじめに


たず、最初から環境に䜕も倉わっおいないこずを確認したす。



さらに、これがむンストヌルされたす git 、 wget 、 tar 、 screen 、 makeおよび最埌のシリヌズにあったすべおのもの。 私たちは安定性を確信し、前進したす。


必芁なコヌドを取埗する


このリポゞトリを、 cs140eずいう名前のパパたたは、そこにあるものにcs140eしたす。


 git clone https://web.stanford.edu/class/cs140e/assignments/2-fs/skeleton.git 2-fs 

結局のずころ、内郚には次のようなものがあるはずです。


 cs140e ├── 0-blinky ├── 1-shell ├── 2-fs └── os 

ここで、コヌドの䞀郚があるosリポゞトリを、この郚分で䜿甚されるコヌドずマヌゞする必芁がありたす。


 cd os git fetch git checkout 2-fs git merge master 

マヌゞの競合を解決する必芁がある堎合がありたす。 次のようなものが衚瀺される堎合がありたす。


 Auto-merging kernel/src/kmain.rs CONFLICT (content): Merge conflict in kernel/src/kmain.rs Automatic merge failed; fix conflicts and then commit the result. 

自動的に倱敗したした。 手にする必芁がありたす。 これを解決するには、 kmain.rsを手動で倉曎する必芁がありたす。 以前のシリヌズからのすべおの倉曎を必ず保存しおください。 競合を解決するには、 git addおよびgit commitを䜿甚しお固定ファむルを远加する必芁がありたす。 マヌゞの競合の解決に関する䞀般的な情報、および䞀般的なgitに関する情報は、 githowto.comマニュアルにありたす 。 このチュヌトリアルを実行できたす。 䜕でも重宝したす。


ファヌムりェアの曎新


2-fs turnipからmake fetchを䜿甚しおファヌムりェアを再ダりンロヌドする必芁がありたす。 チヌム自䜓がすべおをダりンロヌドしお解凍したす。 files/サブディレクトリからMicroSDカヌドのルヌトにファむルを配眮するだけです。 ぀たり、ファむルfirmware/bootcode.bin 、 firmware/config.txt 、 firmware/fixup.datおよびfirmware/start.elf 。 kernel8.imgずしお前の郚分のブヌトロヌダヌを䜿甚する堎合、この行をconfig.txtに远加する必芁があるこずを忘れないでください


 kernel_address=0x4000000 

ttywriteをむンストヌルする


これで、 os/kernelのMakefile远加のinstallタヌゲットが远加されたした。 カヌネルを収集し、前の郚分のttywriteを䜿甚しおttywriteに盎接送信したす。 したがっお、 kernel8.imgにkernel8.img以前のシリヌズで曞かれたブヌトロヌダヌが含たれおいる堎合、このたさにブヌトロヌダヌはファむルを受け入れおカヌネルにロヌドしたす。 これを実行make installは、パパでカヌネルコヌドを䜿甚しおmake installを実行make install必芁がありたす。


同時に、 Makefileからのこのタヌゲットは、名前だけでttywrite盎接呌び出したす。 ぀たり ttywriteは、 PATH環境倉数が指す堎所のいずれかになければなりたせん。 これらの堎所の1぀にttywriteために、1 1-shell/ttywrite 、 cargo installを実行できたす。 圓瀟のナヌティリティが収集し、適切な堎所にコピヌしたす。 すべおが正しい堎合、 ttywrite --helpを呌び出すずヘルプが衚瀺されたす。


デフォルトでは、 make installはデバむス名に/dev/tty.SLAB_USBtoUARTを䜿甚したす。 Makefileを線集しお、必芁なものを瀺したす。 ぀たり アダプタにはどのデバむスがありたすか PI_TTYずいう倉数がありたす。 異なる意味を䞎えおください。


ALLOCATOR.initialize()はパニックを匕き起こしたす
シェルは匕き続き機胜するはずです。 ただし、 make installタヌゲットをテストしようずするず、シェルが機胜しおいないこずがわかりたす。 ほずんどの堎合、問題はALLOCATOR.initialize()呌び出しにありたす。 メモリアロケヌタヌはありたせん。 譊告なしで実行されるず、単にパニックになるスタブのみ。 少し埌でこの迷惑な事実を修正したす。 ずりあえず、この行をコメントアりトするだけで、倚かれ少なかれそれでも機胜したす。

フェヌズ1メモリラむン



このフェヌズでは、2぀のメモリアロケヌタヌを実装したす。 最も単玔なバンプアロケヌタヌずやや耇雑なビンアロケヌタヌ。 ヒヌプ䞊のメモリ割り圓おが機胜するために必芁なのは、実際にはこれだけです。 ぀たり、 Vec 、 Box 、およびString匕き続き機胜したす。 䜿甚可胜なメモリの量を刀断するために、ARMタグ ATAGS を䜿甚したす。 さらに、 panic!呌び出されたずきに最終的に呌び出されるpanic_fmt関数の内郚を実装する必芁がありpanic! 。


サブフェヌズAパニック


panic_fmtの実装から始めたしょう。 kernel/src/lang_items.rs 。


蚀語アむテム


Rustコンパむラが、オペレヌティングシステムRaspberry Piなどを䜿甚しない目的でプログラムをコンパむルするように構成されおいる堎合、コンパむラはいく぀かの機胜を芁求したす。 私たちは自分の手でそれらを曞く必芁がありたす。 そのようなものは蚀語項目ず呌ばれたす。 これらの芁玠は、特定の条件䞋でコンパむラが呌び出しを眮き換える関数です。 このような関数は、あらゆる基本蚀語に察しお定矩できたす。 これを行うには、そのような関数に#[lang_item]属性で泚釈を付ける必芁がありたす。


Rustでは珟圚、これらの関数のうち2぀だけが必芁です。



これらの関数は䞡方ずもkernel/src/lang_items.rsですでに宣蚀されおいkernel/src/lang_items.rs 。 関数panic_fmtを远加しお、有甚なものを衚瀺する必芁がありたす。


panic_fmt実装


次に、この関数を远加したす。 実装では、送信された情報をコン゜ヌルに出力しおから、無限ルヌプloop 。 ずころで。 fmt::ArgumentsはDisplayトレむトを実装したす。 したがっお、 kprintln!("{}", fmt) を䜿甚できたす。 個人的に奜きなように結論を出したす。 たずえば、Linuxカヌネルのパニックからむンスピレヌションを受けるこずができたす 。


  ( ( ) ) ) ( ( ( ` .-""^"""^""^"""^""-. (//\\//\\//\\//\\//\\//) ~\^^^^^^^^^^^^^^^^^^/~ `================` The pi is overdone. ---------- PANIC ---------- FILE: src/kmain.rs LINE: 40 COL: 5 index out of bounds: the len is 3 but the index is 4 

次に、ある皮のカヌネルパニックでpanic_fmt実装panic_fmtテストしたす。 make installを䜿甚しおコンパむルし、ブヌトロヌダヌを介しお実行できるこずを思い出しおください。 はい。 ALLOCATOR.initialize()がpanic! どこか䞭。 したがっお、実行するず、゚ラヌメッセヌゞも衚瀺されるはずです。


これに加えお、倚くの方法でパニックを匕き起こすようにしおください。 Using panic!() 、他の同様のマクロを䜿甚したす。 そしお䜕か他のもの。 実装が自分で機胜しおいるこずが確実な堎合は、次のフェヌズに進みたす。


サブフェヌズBATAGS


このサブフェヌズでは、raspberryファヌムりェアが提䟛するARMタグATAGSに察しおむテレヌタヌを実行したす。 反埩子を䜿甚しお、䜿甚可胜なメモリの量を決定したす。 䞻な䜜業は、 pi/src/atagsずkernel/src/allocator/mod.rs 。


ARMタグ


ATAGSたたはARMタグは、さたざたな情報をオペレヌティングシステムのカヌネルに転送するために、ARMブヌトロヌダヌずファヌムりェアで䜿甚されるメカニズムです。 Linuxは、ATAGSを䜿甚するように構成するこずもできたす。


Malinkaは、アドレス0x100から始たるATAG構造䜓の配列を配眮したす。 この堎合、各タグは8ビットのヘッダヌで始たりたす。


 struct AtagHeader { dwords: u32, tag: u32, } 

dwordsフィヌルドには、タむトル自䜓を含むタグ党䜓のサむズが含たれたす。 ダブルワヌドダブルワヌド、぀たり32ビットワヌドのサむズ。 最小サむズは2 ヘッダヌのサむズです。 tagフィヌルドには、タむプATAGが含たれたす。 ATAGSヘルプにはこれらの玄12がありたす。 Malinkaはそのうちの4぀だけを䜿甚したす。 圌らは私たちにずっお興味深いです


名タむプ tag 倧きさ説明
コア0x544100015たたは2空のリストの堎合スタヌタヌずしお䜿甚
なし0x000000002終わりを意味する
思い出0x544100024物理メモリの䞀郚を説明したす
CMDLINE0x54410009違うカヌネルに匕数を枡す

タグのタむプによっお、ヘッダヌの埌のデヌタの解釈方法が決たりたす。 名前に関連付けられおいるリンクをクリックするず、詳现を確認できたす。 たずえば、 MEMタグには次のようなものが含たれたす。


 struct Mem { size: u32, start: u32 } 

タグは、それらの間を埋めるこずなく連続しおメモリ内にありたす。 このリストは、 COREタグで始たりたす。 そしお、このリストの最埌のタグはNONEです。 他のすべおは任意の順序にするこずができたす。 次のタグがどこから始たるのかをdwordsは、 dwordsの内容を調べる必芁がありたす。 グラフィカルには、すべお次のようになりたす。


ATAGS


組合ず安党



ATAGタグの生の構造は、 pi/src/atags/raw.rsファむルで宣蚀されおいたす。 同時に、そこでunion䜿甚されたす。 Rustの結合は、Neat Cの結合ずほずんど同じです。 䞀郚のフィヌルドがメモリを共有する構造を定矩したす。


 pub struct Atag { dwords: u32, tag: u32, kind: Kind } pub union Kind { core: Core, mem: Mem, cmd: Cmd } 

぀たり、関連付けにより、正しい遞択を考慮せずに、任意の構造をメモリに曞き蟌むこずができたす。 Rustでは、特定のナニオンフィヌルドぞのアクセスは安党ではないこずがわかりたした。


倚くの堎所に既にunsafeラッパヌがありたす。 少なくずも、自分で協䌚に連絡する方法に぀いお心配する必芁はありたせん。 ただし、ラむブラリの゚ンドナヌザヌに関連付けを枡すこずはお勧めできたせん。 このため、ファむルpi/src/atags/atag.rsには別のAtag構造がありたす。 この構造は、アクセスに察しお完党に安党です。 倖郚コヌドに枡すのは私たちです。 atagモゞュヌルを远加するず、内郚衚珟ずこの構造の間の倉換を蚘述したす。


関連付けを゚ンドナヌザヌに枡すのはなぜ悪い考えですか [゚ンドナヌザヌ安党]

安党でない構造物の安党なむンタヌフェむスを䜜成するために倚くの努力をしたした。 これは、Rustで耇数回衚瀺されたす。 暙準ラむブラリもこの奜䟋です。 安党なレむダヌを䜜成する甚途は䜕ですか このアプロヌチをNeat Cなどの蚀語に翻蚳できたすか

コマンドラむン匕数


CMDLINEタグには特に泚意が必芁です。 このタグは次のように宣蚀されたす。


 struct Cmd { /// The first byte of the command line string. cmd: u8 } 

コメントによるず、 cmdフィヌルドには文字列の最初のバむトが含たれおいたす。 蚀い換えるず、 &cmdはCに䌌た文字列ぞのポむンタヌであり、最終的にはれロバむトで終了したす。 このタグの安党なバヌゞョンはCmd(&'static str)です。 rawバヌゞョンから安党なバヌゞョンに倉換する堎合、この行のサむズを決定する必芁がありたす。 ぀たり nullタヌミネヌタコヌド0文字を芋぀けたす。 その埌、アドレスずサむズを䜿甚しお、これをslice::from_raw_parts()を䜿甚しおスラむスに倉換できたす。 次に、このスラむスは、 str::from_utf8()たたはstr::from_utf8_unchecked()を䜿甚しお文字列に倉換できたす。 既にラボ1で䜿甚しおいたす。


atags実装


たあ。 これで、 pi/src/atagsあるatagsモゞュヌルを実装するために必芁なものがすべおpi/src/atags 。 atags/raw.rsからraw::Atag::next()をatags/raw.rsからatags/raw.rs このメ゜ッドは、珟圚のATAGの次のATAGのアドレスを決定したす。 ここでは、 unsafeコヌドに頌らなければなりunsafe 。 次に、 raw構造から安党な構造に倉換するためのヘルパヌメ゜ッドずプロパティを実装したす。これはatags/atag.rs From<&'a raw::Cmd> for Atagする堎合、少しunsafeでunsafeコヌドを䜿甚するFrom<&'a raw::Cmd> for AtagがFrom<&'a raw::Cmd> for Atagたす。 Atags 、 atags/mod.rsにあるatags/mod.rsのIterator traitを実装しatags/mod.rs ここでは、少しunsafeコヌドも必芁になる堎合がありたす。


ヒント

3行のコヌドでAtags::next()メ゜ッドを実装できたす少なくずも詊しおみおください。

x: &Tから*const u32倉換できたすx as *const T as *const u32です。

x: *const Tから&Tぞの逆倉換は、 &*x実行できたす。

add subたたはoffsetを䜿甚しお、生のポむンタヌで算術挔算を実行できたす。

atagsテスト


ATAGSの独自の実装をテストしたす。 むテレヌタを介しおすべおの倀を取埗し、すべおをコン゜ヌルに出力しおみおください。 kernel/src/kmain.rs盎接移動しkernel/src/kmain.rs 。 NONE以倖の3぀のタグのうち少なくずも1぀が衚瀺されるはずです。 同時に、各ATAGの実装が期埅どおりであるこずを確認しおください。 実装が完了したら、次のサブフェヌズに進みたす。


ヒント 構造のより矎しい出力をコン゜ヌルに出力するには、 `{}を䜿甚したす。



タむプCMDLINEタグには䜕がCMDLINEたすか [atag-cmdline]

これらのタグに倀はありたすか ファヌムりェアからどのような議論がありたしたか どこから来たず思いたすか、どのように䜿甚できたすか



MEMタグに埓っお䜿甚可胜なメモリ量はどれくらいですか [atag-mem]

MEMタグで報告される䜿甚可胜なメモリの正確な開始アドレスずサむズは䜕ですか これはすべお、ラズベリヌが持っおいる1ギガバむトのメモリにどれくらい近いですか

サブフェヌズCりォヌムアップ


このサブフェヌズでは、2぀のメモリアロケヌタの埌続の曞き蟌みに必芁なすべおを準備したす。 アドレスを2のべき乗に揃える関数align_upおよびalign_downを実装したす。 さらに、システムメモリから開始アドレスず終了アドレスを返すmemory_map関数を実装したす。 この関数は䞡方のアロケヌタヌによっお䜿甚され、割り圓おに䜿甚可胜なメモリヌ量を決定したす。


アラむメント


メモリ内のアドレスは、Nで完党に分割されるず、Nバむトでアラむンされたず呌ばれたす N ぀たり、アドレスk k % n == 0が真になりたす。 通垞、蚘憶を調敎するこずを心配する必芁はありたせん。 ただし、珟圚はシステムプログラマです。 このトピックぞの泚目が高たっおいるのは、ハヌドりェア、プロトコル、およびその他すべおが、アラむメントに関しお非垞に特定のプロパティを芏定しおいるずいう事実に関連しおいたす。 たずえば、32ビットARMアヌキテクチャでは、スタックポむンタヌが8バむトにアラむメントされおいる必芁がありたす。 AArch64アヌキテクチャこの堎合では、スタックポむンタヌに16バむトのアラむメントが必芁です。 x86-64でもほが同じこずが必芁です。 メモリのペヌゞのアドレスは、4キロバむトに揃える必芁がありたす。 さらに倚くの異なる䟋がありたすが、それなしではできないこずはすでにわかっおいたす。


Cute Cでは、libCからアロケヌタによっお返されるメモリアドレスは、32ビットシステムでは8バむト、64ビットシステムでは16バむトであるこずが保蚌されたす。 さらに、呌び出し元は、返されたメモリアドレスのアラむメントを明確に制埡するこずはできず、自分で凊理する必芁がありたす。 このために、たずえば、POSIX暙準のposix_memalign関数がありたす。


Neat Cでこのようなアラむメントプロパティが遞択されるのはなぜですか [libc-align]

mallocシステム関数のアラむメントに8バむトたたは16バむトの保蚌を遞択しおも、根拠はありたせん。 暙準Cラむブラリでこのような保蚌が遞択されたのはなぜですか

Cute Cでは、 malloc()およびfree()宣蚀は次のようになりたす。


 void *malloc(size_t size); void free(void *pointer); 

Rustは、䜎安党レベルでallocずdeallocを䜿甚したす。これは次のようになりたす。


 // `layout.size()` is the requested size, `layout.align()` the requested alignment unsafe fn alloc(&mut self, layout: Layout) -> Result<*mut u8, AllocErr>; // `layout` should be the same as was used for the call that returned `ptr` unsafe fn dealloc(&mut self, ptr: *mut u8, layout: Layout); 

呌び出しコヌドは敎列を瀺す堎合があるこずに泚意しおください。 その結果、呌び出し元のコヌドではなくアロケヌタヌが、敎列されたポむンタヌを返すように泚意する必芁がありたす。 次のサブフェヌズでメモリアロケヌタを実装する堎合、戻りアドレスが正しく敎列されおいるこずを確認する必芁がありたす。


さらに、 deallocずは異なりdeallocでは、呌び出し偎がallocずたったく同じLayout枡す必芁があるこずに泚意しおdealloc 。 したがっお、倖郚コヌドは、特定の割り圓おられたメモリのサむズずアラむンメントの栌玍に泚意する必芁がありたす。


このようにRustが責任をアロケヌタヌず呌び出しコヌドずに分けおいるず思うのはなぜですか [onus]

キュヌトなアロケヌタでは、返すこずができるメモリアドレスのアラむメントに関する制限が少なくなりたす。 ただし、同時に、割り圓おられたスペヌスのサむズを保持する必芁がありたす。 Rustでは、たったく逆です。 なぜRustは反察の道を遞んだず思いたすか これはアロケヌタヌず呌び出しコヌドにどのような利点がありたすか

align_up  align_upおよびalign_down


次のサブフェヌズでアロケヌタヌを実装する堎合、次たたは前のアラむメントされたアドレスを刀別するのに圹立ちたす。 ぀たり アドレスu次の>=たたは<= u 、2の环乗で敎列されたす。 align_upおよびalign_downが既にもちろん実珟されおいalign_up ありalign_down 。 これらはkernel/src/allocator/util.rs 。 これらはおおよそ次のように宣蚀されたす


 /// Align `addr` downwards to the nearest multiple of `align`. /// Panics if `align` is not a power of 2. fn align_down(addr: usize, align: usize) -> usize; /// Align `addr` upwards to the nearest multiple of `align`. /// Panics if `align` is not a power of 2. fn align_up(addr: usize, align: usize) -> usize; 

これらの機胜を今すぐリリヌスしおください。 kernelディレクトリからmake testたたはcargo test呌び出すmake testにより、このプロセスの正圓性を確認できkernel 。 テスト自䜓はkernel/src/allocator/tests.rs 。 この郚分のすべおが正しい堎合、 align_utilすべおのテストにalign_utilはずです。


泚意  kprint{ln}!テスト䞭kprint{ln}! print{ln}!呌び出しになりたすprint{ln}! すべおが機胜するはずです。

ヒント

実装は1行たたは2行を占有したす。

align_upずalign_downは互いに非垞に䌌おいたす。

スレッドセヌフ


libCのmalloc()および実装する2぀のメモリアロケヌタはグロヌバルです。 ぀たり い぀でもどこでも呌び出すこずができたす。 任意のストリヌムに含める。 したがっお、アロケヌタヌはスレッドセヌフでなければなりたせん。 Rustはスレッドの安党性を非垞に重芖しおいたす。 このため、システムに䞊行性を凊理するメカニズムスレッドなどがただない堎合でも、アロケヌタヌを実装するこずは困難です。


スレッドセヌフメモリアロケヌタヌのトピックは非垞に広範囲です。 このトピックに぀いおは、倚くの研究が芋぀かりたす。 珟時点では、このトピックに぀いおは觊れたせん。 MutexメモリMutexをラップするだけです。 このラッパヌは既にkernel/src/allocator/mod.rs 今すぐこのコヌドをすべお読んでください。 Alloc特性の実装がそこにあるこずに泚意しおください。 これにより、Rustはこれが非垞に有効なアロケヌタヌであるこずを知るこずができたす。 , #[global_allocator] ( kmain.rs ). #[global_allocator] — , , Rust- Vec , String Box . ぀たり alloc() dealloc() , .



Alloc Allocator kernel/src/allocator/mod.rs imp::Allocator , mutex . imp . . #[path = "bump.rs"] , Rust-, . #[path] . bump- bump.rs . bin- bin.rs .


: memory_map


kernel/src/allocator/mod.rs — memory_map . Allocator::initialize() , kmain() . initialize() imp::Allocator .


memory_map . , . ぀たり , ATAGS. , . binary_end . , _end ( layout.ld ).


memory_map , Atags B binary_end . , . - String::from("Hi!") . . , panic!() bump-. memory_map , bump- — . , .


D: Bump-



. Bump-. kernel/src/allocator/bump.rs .


Bump- . alloc current . . current ( ). , . dealloc .


, , , 1 , 512 . , .


バンプ


, kernel/src/allocator/bump.rs . ぀たり new() , alloc() dealloc() bump::Allocator . align_up align_down , . -. make test cargo test kernel . allocator::bump_* .


. , , !

saturating_add saturating_sub .

- , kmain() . :


 let mut v = vec![]; for i in 0..1000 { v.push(i); kprintln!("{:?}", v); } 

— .


alloc ? [bump-chain]

bump::Allocator::alloc() . ? : , v.push(i) bump::Allocator::alloc() .

E: Bin-



: bin-. kernel/src/allocator/bin.rs .


Bin- , . ( ) , . . — . , . .


, , . k - 2 2^n n , 3 k ( 2^3 , 2^4 
 2^k ). , 2^3 , 2^3 . 2^3 2^4 2^4 :




(intrusive) . kernel/src/allocator/linked_list.rs . kernel/src/allocator/bin.rs LinkedList , .


?

next ( previous ) . . .

LinkedList::new() . push() . ( ), pop() . peek() . :


 let mut list = LinkedList::new(); unsafe { list.push(address_1); list.push(address_2); } assert_eq!(list.peek(), Some(address_2)); assert_eq!(list.pop(), Some(address_2)); assert_eq!(list.pop(), Some(address_1)); assert_eq!(list.pop(), None); 

LinkedList . iter() . . iter_mut() . Node , . value() pop() .


 let mut list = LinkedList::new(); unsafe { list.push(address_1); list.push(address_2); list.push(address_3); } for node in list.iter_mut() { if node.value() == address_2 { node.pop(); } } assert_eq!(list.pop(), Some(address_3)); assert_eq!(list.pop(), Some(address_1)); assert_eq!(list.pop(), None); 

LinkedList . , push() . LinkedList .


? [ll-alloc]

— . , , ?


, , . , . . , . . . .


:



, .


実装


bin- kernel/src/allocator/bin.rs . ( , bin- ) . . . - . make test cargo test . bump.rs bin.rs #[path] kernel/src/allocator/mod.rs . たあ぀たり bump-, bin, .


bin- — .


? [bin-about]

. :
  • () ?
  • ?
  • ?




? [bin-frag]

! , ? ( ) .




UPD :

Source: https://habr.com/ru/post/J352414/


All Articles