おもちゃのOS、32ビットはついにシンプルなインタラクティブなシェルを実装します
to-doリスト:
合計2つのハードディスクがあり、システム自体がメインディスクにインストールされ、MBRのブートモードが採用され、MBR->ブートローダー - >カーンネルのプロセスが使用されます。
MBR LBA 0号扇区始まる1セクターにあります
Boot LoaderディスクのLBA 2号扇区から始まる4つのセクターにあります
Kernel 、ディスクLBA 9号扇区から始まる200セクター内にあります
ファイルシステムはスレーブディスクに実装されています。これはあまり合理的ではないかもしれません。ファイルシステムが商用システムのロジックに基づいている場合、今すぐ実装する必要があり、オペレーティングシステムが対応するパーティションにインストールされます。
メモリページング、1ページは4kbです
メモリ管理はビットマップ管理を採用し、メモリを割り当てるときにサイズによって区別されます。 1024バイトを超える場合、ページごとに直接割り当てられます。
1024バイト未満の場合、ページごとにアリーナの割り当てに基づいて、アリーナのアイドルブロックチェーンを使用して、割り当てと制御を使用してください。
便利なため、ページングメカニズムは有効になりますが、メモリページとディスクの切り替え関数は実装されていません。

PCBは1ページのサイズです
スレッドスケジューリングのコアエッセンスは、クロック割り込みによるESPポインタースイッチングを制御することにより、PCBを切り替えることです。優先度は、各スレッドの実行時間スライスの長さに反映されます。
プロセスの実装はスレッドに基づいており、TSSの選択はLinuxに基づいており、単一のTSSをバックアップレベル0スタックとレベル0スタックポインターに使用します。スレッドとの最大の違いは、プロセスにPCBにページテーブルアドレスがあることです。これは、プロセスとスレッドの最大の違いです。このプロセスには、独自の独立した仮想メモリスペースがあります。
スケジューリングに効率的なアルゴリズムはありません。キューループスケジューリングを使用するだけです
アイドルスレッドの実装
アイドルスレッドの実装は非常に簡単です。初めてスケジュールを取得したら、それをブロックしてCPUから削除します。スケジューラが再びディスパッチを実行すると、準備が整ったスレッドまたはプロセスがReadyキューにない場合、アイドルスレッドは目覚めます。この時点で、アイドルスレッドはhltを介してCPUを吊り下げます。時間スライスが使用され、CPUが準備完了プロセスやスレッドが見つからない場合は、アイドルスレッドをCPUに置き換え続けます。この時点で、アイドルはそれ自体をブロックし続け、上記のスケジューリングプロセスの繰り返しを開始します。
// 空载任务
static void idle ( void * arg ) {
while ( 1 ) {
thread_block ( TASK_BLOCKED );
asm volatile ( "sti; hlt" : : : "memory" );
}
}プロセスフォーク
プロセスのフォークは、最初に現在のプロセスのPCBをコピーし、現在のプロセスの仮想プールビットマップを介して新しいページテーブルを作成します。仮想アドレスの対応は、元のプロセスとまったく同じです。最後に、割り込みサイトが偽造され、子プロセスがスケジューリングキューに追加され、スケジューリングが実行されるのを待ちます。鍛造割り込み部位では、子プロセスのPCBのEAXは0に変更されます。つまり、フォークの返品値は新しいプロセスで0です。一方、親プロセスのPCBのEAXは変更されておらず、子プロセスのPIDを表します。親プロセスはシステムコールの終わりから戻り、子プロセスは割り込み終了機能を介して直接戻ります。
プロセスexec
Execの実装は、最初にELFファイルをディスクからメモリにロードし、次に現在のプロセスのPCBのプロセス名を変更し、プロセスに必要なパラメーターを合意されたレジスタに実行し、ELFのエントリポイント、割り込みサイトの偽造のエントリポイントにEIPを変更し、exit関数を直接呼び出すことにより、新しいプロセスを直接実行します。
その中で、ELFのエントリポイントは、非常にシンプルなCRT自体を実装することによって実装されています。これにより、_Startエントリが与えられ、合意されたパラメーターレジスタが3レベルのスタックにプッシュされ、外部コマンドの主な関数を呼び出してパラメーターの渡しを実現します。
[bits 32]
extern main
extern exit
; 这是一个简易版的CRT
; 如果链接时候ld不指定-e main的话,那ld默认会使用_start来充当入口
; 这里的_start的简陋实现,充当了exec调用的进程从伪造的中断中返回时的入口地址
; 通过这个_start, 压入了在execv中存放用户进程参数的两个寄存器。然后call 用户进程main来实现了向用户进程传递参数
section .text
global _start
_start:
;下面这两个要和 execv 中 load 之后指定的寄存器一致
push ebx ;压入 argv
push ecx ;压入 argc
call main
; 压入main的返回值
push eax
call exit ; 不再返回,直接调度别的进程了,这个进程直接被回收了プロセス待機
フォークしてローカルコマンドを実行した後、表示しないように、親プロセスはローカル待機中の子プロセスで終了する必要があります。
ここでの実装は、sys_waitシステムコールを入力し、プロセスキュー全体を通過し、親プロセスが独自の中断状態であるプロセスを見つけて、PCBで戻り値を取得し、PCBとページディレクトリテーブルをリサイクルし、スケジューリングキューから削除することです。横断後に保留中の子どものプロセスが見つからない場合は、それ自体をブロックして、子プロセスが目覚めるのを待ちます。
プロセス出口
実行中、外部コマンドは実際に自分で作成された単純なCRTによってラップされます。 Simple CRTコール外部コマンドのメインは、最後に戻り値を取得し、それを渡して終了してから出口を呼び出します
ここでの実装は、主に3つのことを行います。
外部コマンドをロードして実行するプロセス全体
まず、外部コマンドはint main(int argc, char **argv)関数を提供する必要があります。リンクするときは、自家製の単純なCRT start.oを持ち込み、最後にコンパイルされた外部コマンドをファイルシステムに書き込む必要があります。
外部コマンドが実行される場合、現在のプロセスはプロセスを分岐し、新しいプロセスでexecvを使用し、現在のプロセスは待機を実行し、子プロセスの返品値を受け入れるためにアドレスを渡し、新しいプロセスが戻るまでブロックして待機します。新しいプロセスは、ファイルシステムからExevcのメモリに外部コマンドをロードし、PCBの関連コンテンツを外部コマンドの情報に変更し、PCB割り込みスタックのintr_exit CRTの_Startエントリに変更します(現時点では、新しいプロセスは外部コマンドプロセスに完全に置き換えられました)。外部コマンドメインの実行が終了して返されると、CRTはメインリターン値を介して出口を呼び出し、 sys_exitのメインリターン値をPCBの対応する位置に入れ、PCBとページディレクトリテーブルを除くすべてのリソースをリサイクルし、親プロセスを覚醒させて子プロセスをブロックします。親プロセスが覚醒した後、システムコールsys_wait 、PCBのPCBのPCBのPCBのPCBのPCBのPCBのPCBのPCBのPCBのPCBのPCBのPCBのPCBのPCBのPCBのPCBのPCBのPCBの返品値を取得し、PCBのPCBのPCBのPCBの値を取得します。この時点で、子プロセスが実行され、完全にリサイクルされています。
ファイルシステムの実装は、UNIXのようなシステムの巻きを模倣します
パーティションは、INODES 4096の数を制限します。CPUはブロックのサイズ(クラスター)に応じてハードディスクを動作させ、512バイトのセクターに1つのブロックが設定されます。
INODEは、12の直接ブロックと1つの第1レベルの間接テーブルをサポートします。 1つのブロックはセクターの512バイトであるため、単一のファイルは最大140 * 512バイトをサポートします。
イノード構造

ファイルシステムレイアウト

ファイル記述子とinodeの間の対応

パイプラインの実装は、ファイルシステムのファイル構造に依存します。その本質は、ファイル構造が元々カーネル空間のリングバッファースペースに対応する必要があるというイノードを置き換えることです。
// 因为管道也是当作文件来对待,因此file结构体在针对真实文件和管道是有不同的意义
struct file {
// 文件操作的偏移指针, 当是管道是表示管道打开的次数
uint32_t fd_pos ;
// 文件的操作标志,当是管道是一个固定值0xFFFF
uint32_t fd_flag ;
// 对应的inode指针,当是管道时指向管道的环形缓冲区
struct inode * fd_inode ;
};カーネルスペースが共有されているため、パイプラインの読み取りと書き込みを通じて、異なるプロセス間の通信を実現できます。パイプラインの読み取りと書き込みは、 sys_writeとsys_readにカプセル化されているため、パイプラインの操作と通常のファイルの操作に違いはありません。
リダイレクトの本質は、PCBファイル記述子テーブルの対応するグローバル記述子テーブルのアドレスを変更し、対応するファイル記述子の読み取りと書き込みの操作が新しいファイルを指していることです。
パイプライン文字の実装|シェル内は、標準入力と標準出力をパイプラインにリダイレクトすることで達成されます。
int32_t sys_read ( int32_t fd , void * buf , uint32_t count ) {
if ( fd == stdin_no ) {
if ( is_pipe ( fd )) {
// 从已经重定向好管道中读
ret = pipe_read ( fd , buf , count );
} else {
// 从键盘获取输入
}
} else if ( is_pipe ( fd )) {
// 读管道
ret = pipe_read ( fd , buf , count );
} else {
// 读取普通文件
}
return ret ;
}
int32_t sys_write ( int32_t fd , const void * buf , uint32_t count ) {
if ( fd == stdout_no ) {
if ( is_pipe ( fd )) {
// 向已经重定向好管道中写入
return pipe_write ( fd , buf , count );
} else {
// 向控制台输出内容
}
} else if ( is_pipe ( fd )) {
// 写管道
return pipe_write ( fd , buf , count );
} else {
// 向普通文件写入
}
}