micro_os_loaderを使用し、独自のカーネルを作成しましたが、問題なく動作します。カーネルはセクター2にある必要があります。そのようにしたのは問題ありませんが、セクター4でプログラムを作成し、カーネルでサブプログラムとして実行したいと思います。しかし、どうすればそれを行うことができますか?カーネルでは、プログラムをセクター4で実行するにはどうすればよいですか。
1 に答える
これは、カーネルがどのように機能するかに大きく依存します。
プログラムコードをメモリに読み込み、そのプロセスを作成する必要があります。そして...まあ...次のステップはすでにほとんど設計に依存しています。
基本的に、プログラムのエントリポイントを指すにはCS:IPが必要です。
たとえば、マルチタスクを実行している場合は、PCB内のすべてのレジスタを保存し、プロセスキューから別のプロセスを選択するハンドラを使用してタイマー割り込みを設定する可能性があります。次に、そのプロセスに属するPCBからレジスタをロードし、スタック上のリターンアドレスを改ざんして、そのプロセスから実行する次の命令をポイントし、プロセス制御を与えるために問題を発行します。
その間、カーネルはループでアイドリングしています-例:
idle:
hlt
jmp idle
ここから、カーネルには線形の作業はありません。代わりに、プロセスからのシステムコールと、ハードウェアからの割り込みを処理するためにアイドル状態になります。システムコールは通常、割り込みハンドラの形式で実装されるため、システムで発生するすべての種類の非同期イベントを均一に処理できます。
[編集]
追加の例として、プリエンプティブマルチタスクを使用し、スケジューラーをタイマーIRQハンドラーに愚かにハードコーディングしますが、詳細については説明しません。たとえば、プロセスキューの実装はあなた次第です(これはメモリ内のどこかにあるPCBのリストにすぎません)。タイマーは必須ではないため、再プログラムしません(すでに実行され、IRQ 0を生成しています)。ただし、デフォルトの8253/8254セットアップで提供される速度以外の速度でタスクを切り替えることができるように、必要に応じて再プログラムすることができます。また、IRLではFPUの状態やその他のものも保存する必要があることに注意してください...
; cdecl calling convention
; ugly pieces of code appear! DO NOT COPY-PASTE OR OTHERWISE USE THIS! this is just for illustration
proc timerInterruptHandler
cli ; disabling interrupts so they don't mess up anything. no need to worry about re-enabling them as FLAGS is implicitly saved on the stack
push ax ; let's first save our current CPU state on the stack
push cx
push dx
push bx
push bp
push sp
push si
push di
push es
push ss
push ds
push bp
mov bp,sp
call getCurrentProcess ; get the current process instance. let's now assume that the context is simply saved at relative address 0
mov di,ax
lea si,[bp + 2]
mov cx,14
rep movsw ; save the contents of each register to the buffer
call selectNextProcess ; we'll select the next process from the queue (or the first when the current is the last one).
mov si,ax
lea di,[bp + 2]
mov cx,14
rep movsw ; overwrite the saved registers on the stack with the saved state of our new process
; upon returning from the handler (the next steps), the state of the new process will be loaded
mov sp,bp
pop bp
pop ds
pop ss
pop es
pop di
pop si
pop sp
pop bp
pop bx
pop dx
pop cx
call acknowledgeMasterPICInterrupt ; every IRQ must be acknowledged to the corresponding PIC (both if the IRQ is triggered by the salve PIC, i.e. IRQ8+)
pop ax
iret
endp timerInterruptHandler
; void setUpTaskScheduler(void)
proc setUpTaskScheduler ; here we assume that no IRQ remapping is done, thus IRQ 0 (timer) is at INT 8.
pushf
cli
push bx
push ds
mov cx,2 ; multiplying the interrupt number by 4 gives you the address of the IVT record to modify
mov bx,8
shl bx,cx
xor ax,ax
mov ds,ax
lea ax,[cs:timerInterruptHandler] ; write the offset first
mov [word ptr ds:bx + 0],ax
mov ax,cs
mov [word ptr ds:bx + 2],ax ; then the segment
pop ds
pop bx
popf
ret
endp setUpTaskScheduler
; void acknowledgeMasterPICInterrupt(void)
proc acknowledgeMasterPICInterrupt
mov al,20h
out 20h,al
ret
endp acknowledgeMasterPICInterrupt
; void acknowledgeSlavePICInterrupt(void)
proc acknowledgeSlavePICInterrupt
mov al,20h
out 0A0h,al
call acknowledgeMasterPICInterrupt
ret
endp acknowledgeSlavePICInterrupt
_main:
; ...
call setUpTaskScheduler
; Once interrupts are enabled the scheduler will start doing its work
; ...
[/編集]
ここでは、PITを再プログラムするコード例を見つけることができます。