オペレーティングシステムがどのように機能するか第二章
A.サポート技術情報の概要
1. 3台のコンピュータの魔法
- プログラム内蔵方式
- 関数呼び出しスタックメカニズム。スタック:パス、通話記録機能パラメータ記憶領域のC言語ランタイムが使用する必要があります。
- 割り込み
2.スタックレジスタと関連するスタック操作
- ESP(ポインタレジスタをスタック):スタックポインタ(ポインティング・スタックの最上部)
- EBP(ベースポインタレジスタ):(を指すベースポインタスタックの上部は)、現在のレコードのベースアドレスは、C言語の関数呼び出しとして使用されます。
- EAXは:一時的に値の数を格納するために、関数が値格納されているデフォルトのEAXレジスタを返し、呼び出し元の関数aに戻りました。
- EIP:次に実行すべき命令がメモリ内のアドレスであることを示します。(必ず命令アドレスを指します)
- CS(コード・セグメント・レジスタ)
- スタックにプッシュし、スタックアドレスは4つのバイトが減少しています
- スタックをポップ、スタックアドレスは4つのバイトをインクリメントされ
- 関数呼び出し、コールアドレスを呼び出します。現在のCS:EIPは、スタックにCSの中の値:EIPは、呼び出された関数のエントリアドレスを指します。
- RET機能が戻ると、オリジナルのCS保存するここでは、スタックポップの上から:EIP値を、そしてCSを置く:EIPは行きます。
- スタックを作成するために使用される関数を入力
- 機能スタックの失効を残すために
3.インラインアセンブリ
/*
**通过例子来熟悉内嵌汇编的语法规则
*/
#include <stdio.h>
int main(){
unsigned int val1 = 1;
unsigned int val2 = 2;
unsigned int val3 = 0;
printf("val1:%d,val2:%d,val3:%d\n",val1,val2,val3);
asm volatile(
"movl $0,%%eax\n\t" //把EAX清0
"addl %1,%%eax\n\t" //把ECX的值与EAX寄存器求和,然后放到EAX寄存器中去。%eax += val1
"addl %2,%%eax\n\t" //把val2的值加上val2的值再放到EAX中。%eax += val2
"movl %%eax,%0\n\t" //val1+val2的值存储的地方放到内存val3中。val3 = %eax
: "=m" (val3)
: "c" (val1),"d" (val2)
);
printf("val1:%d+val2:%d=val3:%d\n",val1,val2,val3);
return 0;
}
インラインアセンブリ共通修飾子
修飾子 | 説明 |
"A" | EAXへの入力変数 |
"B" | EBXへの入力変数 |
"C" | ECXへの入力変数 |
"D" | EDXに入力変数 |
"S" | ESI入力変数に |
"D" | EDIへの入力変数 |
"R" | 入力変数、すなわちEAX、EBX、ECX、EDX、ESI、EDI一つに汎用レジスタ |
"EAX" | 破壊記述部 |
"M" | メモリ変数 |
"=" | ちょうど書かれた命令のオペランド(出力オペランド) |
"+" | オペランドは、命令タイプに読み取る(入出力操作の数)であります |
概要
- 登録はフロントエスケープ文字で1%以上となり、百分の二があります
- 入力された番号の出力は、それぞれ1%、2%、3%である......
- 各入力または出力の前部が修飾子を追加することができる組み込みアセンブリ
4.仮想のx86 CPUのハードウェア・プラットフォーム
重要な指示
$ cd ~/LinuxKernel/linux-3.9.4
$ rm -rf mykernel
$ patch -p1 < ../mykernel_for_linux3.9.4sc.patch
$ make allnoconfig
$ make
$ qemu -kernel arch/x86/boot/bzImage /*查看搭建起来的内核启动效果*/
カーネルは、次のような効果を構築するために開始します。
ルートディレクトリカーネルのソースコードの下のMYKERNELのLinux-3.9.4ディレクトリを入力し、mymain.c myinterrupt.cにQEMUウィンドウの出力コードを参照してください。
ファンクション見ることができますmy_start_kernel(空)mymain.c機能は、「ここで私my_start_kernel」10万二次出力を行いました。
mymain.cコードは、割り込みハンドラコンテキストながら絶えず実行周期的クロック信号を割り込みを生成し、それはmykernel.c my_timer_hander(ボイド)関数でトリガすることができます。これは、クロック割り込みとx86CPUをシミュレートします。
キーコード解析
プロセス制御ブロックを定義するために使用1.mypcb.hヘッダ。
カーネルのさまざまなコンポーネントを初期化する責任2.mymain.c入場MYKERNELカーネルコード。
asm volatile(
"movl %1,%%esp\n\t" /*将进程原堆栈的栈底的地址存入ESP寄存器中*/
"pushl %1\n\t" /*将当前ESP寄存器的值入栈*/
"pushl %0\n\t" /*将当前进程的EIP寄存器的值入栈*/
"ret\n\t" /*让入栈的进程EIP保存到EIP寄存器中*/
"popl %%ebp\n\t" /*这里不会被执行,只是一种编码习惯,与前面的push结对出现*/
:
: "c" (task[pid].thread.ip),"d" (task[pid].thread.sp) /* input c or d mean %ecx/%edx*/
);
変更内容をスタック:
割り込み処理3.myinterrupt.cクロックおよびプロセススケジューリング
if(next->state == 0)
{
/*进程调度关键代码*/
asm volatile(
"pushl %%ebp\n\t" /*保存当前EBP到堆栈*/
"movl %%esp,%0\n\t" /*保存当前ESP到当前PCB中*/
"movl %2,%%esp\n\t" /*将next进程的堆栈栈顶的值存到ESP寄存器*/
"movl $1f,%1\n\t" /*保存当前进程的EIP值,下次回复进程后将在标号1开始执行*/
"pushl %3\n\t" /*将next进程继续执行的代码位置(标号1)压栈*/
"ret\n\t" /*出栈标号1到eip寄存器*/
"1:\t" /*标号1,也就是next进程开始执行的位置*/
"popl %%ebp\n\t" /*恢复EBP寄存器的值*/
: "=m" (prev->thread.sp),"=m" (prev->thread.ip)
: "m" (next->thread.sp),"m" (next->thread.ip)
);
my_current_task = next;
printk(KERN_NOTICE ">>>switch %d to %d<<<\n",prev->pid,next->pid);
}
else /*next该进程第一次被执行*/
{
next->state = 0;
my_current_task = next;
printk(KERN_NOTICE ">>>switch %d to %d<<<\n",prev->pid,next->pid);
asm volatile(
"pushl %%ebp\n\t" /*保存当前EBP到堆栈*/
"movl %%esp,%0\n\t" /*保存当前ESP到当前PCB中*/
"movl %2,%%esp\n\t" /*载入next进程的栈顶地址到ESP寄存器*/
"movl %2,%ebp\n\t" /*载入next进程的堆栈基地址到EBP寄存器*/
"movl $1f,%1\n\t" /*保存当前EIP寄存器值到PCB,这里$1f是指上面的标号1*/
"pushl %3\n\t" /*把即将执行的进程的代码入口地址入栈*/
"ret\n\t" /*出栈进程的代码入口地址到EIP寄存器*/
: "=m" (prev->thread.sp),"=m" (prev->thread.ip)
: "m" (next->thread.sp),"m" (next->thread.ip)
);
}
システムのみ2つのプロセスがプロセス0と1プロセスであると仮定する。0初期化プロセスの実装処理1を開始する起動、プロセススケジューリング、時カーネルによって実行されます。1プロセスが実行されたことがないので、最初に実行されるので、他のコードの実行でした。下記のスケジュール設定プロセススタック1の変化を分析します。
コードが実行中でなければなりません場合は、この時点では、実装プロセス1の始まりは、スケジューリングのプロセスは、プロセスの実行中に発生した場合、再0のプロセスが、この時点で実行するために予定されています。次のように実行コードは、アセンブリのスタックの変更を埋め込まれている場合: