procAのstack領域の構成ここから、procAのstack領域の構成です。2の下にtrapret(コレはtrapasm.Sで定義された関数のaddress)を設定する。(ここで、forkret()からtrapret()に飛べるような細工をしている。contextを配置する。contextの配置を実現していることがポイントです。2の下にtrapret(コレはtrapasm.Sで定義された関数のaddress)を設定する。ここで、forkret()からtrapret()に飛べるような細工をしている。contextを配置する。p;}細工の部分については、その段になったら説明しますが、2週目にこの記事を読むときはちょっと気を払う感じで。kvmalloc()で登場しているのですが、再度説明します。読み始めるか、適宜ほかのサイトを確認してください。spaceのpagingを設定することです。directoryを作成します。Register(PDBR))を再設定します。setupkvmがswitchkvm();(%cr3にロード)によって、設定されている点に注意してください。directoryの領域(4*1024byte)を確保します。pgdir;}後半のmappages(walkpgdirが本質的な仕事をしている)で、paging設定の作成を行います。0xFFFFFFFF)};以下のような変数です。spaceではprocessごとにpagingの設定が異なります。_binary_initcode_sizeまでのbinaryをmoveしておく。kalloc()で動的に確保した領域で4096byteです。V2P(mem)+4096)に対応しており、1番のmappingの設定と一貫性があります。initcode.Sを見てもらえればわかるのですが、100行以内のassemblyで明らかに4096byteに収まる(実際には44byteでした2)ので、1page分の使用で十分です。procAのconfigurationこれは図とコードを対応させた方が良いかなと思います。registerとは、%eaxとか%ebxとかのことでこの領域はまだ0で初期化されたままです。cpusについては、seginit()で登場しました。adddressなので、物理memoryの0~4096でないことに注意してください。このあとswitching処理で拾ってもらえるようにしてます。排他処理を行う理由ここのトピックはやや難しいです。acquire(&ptable.lock);と言うのが理由です。自明ではないので、次の2点について深掘りして見ます。部分でも触れたように、MultiProcessorに対応しています。BSPという)が選択される。1);を実行し、2-2で待機していたBSPを起こす。RUNNING;まで突入している可能性があります。この場合、以下の競合が起こる可能性があります。RUNNABLE;はatomicでないのを前提とします。RUNNABLE;`を処理し終わり、p->stateにRUNNABLEが代入されるもし、こういうことが起こると、pの状態がRUNNABLEにも関わらず、CPUによってprocessが実行するため、予期せぬ動作をするでしょう。実際には、2と3の間にswitchuvm(p)が挟まれているため、上記のようなことが起こる可能性は限りなく低いのですが、可能性のある限りlockは必要です。atomic.なのか?もし、atomicならば、1と4の2つの部分に処理がまたがることはありませんので、lockは不要ですが、実際にはatomicです。境界でのメモリの書き込み、読み込みが必要3ということで、今回の場合はその保証がないため、アトミックであることが保証できません。atomic.というわけでした。lockが必要となります。)で表現できますが、上の考察で見たようにこのinstruction自体は、atomicではありません。instructionと同等です。cpuをとってきてます。continue;ここは何回か説明した通りですが、pの状態がRUNNABLEのprocessを探し出しています。このprocessを動作させるわけですね。今回は上記でみたinitcodeを動かすprocessを探してこれたとします4。instructionの拡張です。参照すればまぁ良いんじゃないかと。Register(PDBR))に反映させている部分です。$SYS_CALLを呼び出したときに効いてくる部分なので、ここの部分はinterruptの記事に回します!(大事な箇所ではありますが...)準備が終わっていよいよswtch関数ですが、switchingの本質的な関数となります。言ってもここまで丁寧に追ってきたので、switching自体はむずくありません。ここからentry.Sで用意されたstackの挙動を追っていきます。p->context);switching前夜swtch関数は引数ありなので、引数がstackに積まれます。tableへのaddressをstackに積んでいることです。これがあるため、scheduler関数の再開でもちらっとみたように、scheduler関数に戻れるのでした。scheduler関数の再開までの章で解説します。context*なので、橙色で指し示した部分になります。(第二引数の矢印の先に注意。context*のアドレスにちょうど対応しています。いうより、そうなるように、汎用registerをstackにpushしたのでした。registerをprocAのstack領域内のaddressに書き換える。pointerの値を保存しています。pointerのaddressにrestoreします。switching完了後switchingは理解できたでしょうか。registerを復元する(結果的に`%edi=%esi=%ebx=%ebp=0`となり、`0`に初期化される。scheduler関数の再開までの章まで、entry.Sで用意されたstackは使用しないので、ご退場いただきました。終わりの部分に書いたように、%espが指し示している部分(forkretへのaddress)が%eipにpopされ、%eipがそのaddressにjumpするのでした。これでswitching自体の挙動は説明したのですが、ここからの関数へのジャンプの仕方が、ぷよぷよでいう連鎖の本番なので、この後の挙動もみていきましょうということで、forkretへと続きます...switchingした後はどうなるのかswitchingし終わったので、procAのstack領域で作業していくことになります。forkretにjumpした直後はこんな感じの模式図になっています:forkret()forkretはあんまり大したことやっていなくて、inodeの初期化くらいです。systemの文脈ですが)また、%eipにpopして、スタックポインタの%espが引き上がります。実行されることになる。(今まで全てkernel領域で作業していた!!)ここでのポイントおよび難所はiretです。interruptの本格的な説明は次の記事あたりでお話ししたいのですが、iretと関係ある部分だけ説明します:cliでinterruptがOFFでない限り、いつでもinterruptが呼び出されます6。Segment)に関しては本記事では説明していませんが、TSS領域内に%ssや%espが保存されています8。図より引用iretはinterruptでやったことの逆をhardware側で自動的に行います。%ss)の値を復元(pop)します。trapretではretを呼び出さないの?iretのセマンティクスについては理解したところで、なぜretを用いないの?という疑問が起こります。そもそも、interruptは起こっていないので、iretを使うのは一見すると変です。書いたように、必ずしも対になって呼ばれる必要はありません。instructionは必ずしも対になって呼ばれる必要はありません。不思議な感じがしますが、こういう風に解釈できます。%eaxでstackに積む動作を行なっています。hello相当の必要な準備をhello関数の最後のretのためにする必要がある、という解釈ができます。instructionの場合に適用すると、iret呼び出しの前にinterrupt相当の必要な準備をtrapret関数の最後のiretのためにする必要がある、という解釈ができます。気持ち悪い感じはあると思いますが、ニュアンスは伝わったと思います(思いたい..)そうなると、次の論点は、「interrupt相当の必要な準備」って何ですか?ということです。registerを設定します。instructionしか存在しないから9です。registerの再設定が必要ということで、retでなくiretを使用します。$T_SYSCALLの直前で一旦終わりです。$T_SYSCALLを呼び出します。initのprocessが動作するまでを説明しました。ここでは、CPUがprocessからscheduler関数へと戻るまでの流れを説明します。exitなどにより、状態が切り替わるときtimerによるinterruptで強制的に別のprocessに切り替えるとき本章では、前者の挙動をみます。後者はinterruptの記事で取り上げます。intena;}大事なのが、swtchなので、その確認です。swtchで一度でてきたswitchingの部分の一番本質的なコードのあった関数でした。retまたこのコードに行き着きました。%edi第二引数と図との対応に注意しましょう。context*のaddressと解釈できます。processのstack領域内のaddressに書き換える。tableへのaddressにjumpします。}}swtch関数の長旅(!!)が終わりました。directoryを再ロードします。0;で終わり、for文が終わります。(PDBR))に再ロードを行い、swtchに突入した後は、switchingする直前と直後はどうなっているのかの章でみた通りです。格納された関数にジャンプし、関数を実行することになります。長期出張から帰ってきて、また出張に出かけて行く船乗りのようなやつでしたまとめxv6(OS)にて、switchingする前はなにがどうなっているのかswitchingする直前と直後はどうなっているのか(設定内容の保存、復元を行うわけですが..)switchingした後はどうなるのかを説明しました。binaryファイルinitcodeのサイズです。詳しくは、_binary_objfile_startと_binary_objfile_sizeについての節を参照にしてください。addressだった場合はlockがかかるため、atomicとなります。Lockingを参照のこと。schedulerときて、このprocessを獲得している場合があるので、bootasmから続いてきているCPUでこのprocessが見つからずに,他のRUNNBALEのprocessが訪れるまでfor(;;){の中身を繰り返す可能性があります。landからのinterruptを想定している。(IRET).と書かれています。exitの他にも状態が変化する関数はあります。↩fork関数とuserinit(これはboot時のみ)関数だけがprocessを作成(forkの場合は複製)する。forkretと定めているので。いかなる場合でもforkretから実行されることになる。1;はfirst変数が一度のみ初期化されるという意味です。staticキーワードで調べてみるとわかると思います。upLoginユーザーは見つかりませんでした。