プロセス1実行編③(ユーザーモード) initcode.S (Xv6を読む~OSコードリーディング~)

前回
jupiteroak.hatenablog.com
トップページ
jupiteroak.hatenablog.com




initcode.S
https://github.com/mit-pdos/xv6-public/blob/master/initcode.S

# Initial process execs /init.
# This code runs in user space.

#include "syscall.h"
#include "traps.h"


# exec(init, argv)
.globl start
start:
  pushl $argv
  pushl $init
  pushl $0  // where caller pc would be
  movl $SYS_exec, %eax
  int $T_SYSCALL

# for(;;) exit();
exit:
  movl $SYS_exit, %eax
  int $T_SYSCALL
  jmp exit

# char init[] = "/init\0";
init:
  .string "/init\0"

# char *argv[] = { init, 0 };
.p2align 2
argv:
  .long init
  .long 0

initcode.Sは、ユーザモードのプロセス1として最も初期に実行されます。
initcode.Sでは、execシステムコールを呼び出して、init.cファイルを実行しています。


処理の内容

exitシステムコールを呼び出す

.globl start
start:
  pushl $argv
  pushl $init
  pushl $0  // where caller pc would be
  movl $SYS_exec, %eax
  int $T_SYSCALL
# char init[] = "/init\0";
init:
  .string "/init\0"

# char *argv[] = { init, 0 };
.p2align 2
argv:
  .long init
  .long 0


.globl start
.globl(globlディレクティブ)を使って、startラベルを外部のファイルから参照できるようにします。


start:
pishl命令が配置されているアドレスにstartラベル(シンボル名、名前)を付けます。


pushl $argv
push命令によって、argvラベルが付けられたアドレス = execシステムコールの第二引数の値 = コマンドライン引数配列の先頭アドレス をスタックに退避させます。
argvラベルが付けられたアドレスには、コマンドライン引数配列が配置されています。1つ目の要素はinitラベルのアドレス値(.long init)、2つ目は0の値(.long 0、配列の終わりを意味する値)です。


pushl $init
push命令によって、initラベルが付けられたアドレス = execシステムコールの第一引数の値 = ファイルパスとなるNULL終端文字列の先頭アドレス をスタックに退避させます。
initラベルが付けられたアドレスには、ファイルパスとなるNULL終端文字列のデータ( .string "/init\0")があります。
今回のexecシステムコールでは、initというファイルをロードして実行します。


pushl $0
push命令によって、リターンアドレスをスタックに退避させる挙動を再現しています。
高級言語による記述でシステムコールを呼び出す場合は、システムコールの引数とリターンアドレス(プログラムカウンタ eipレジスタの値)がスタックに退避されます。
今回は、アセンブリ言語で直接システムコールを呼び出す手続きを記述しているので、システムコールの呼び出し元に戻るために必要なリターンアドレスが存在しません。ここでは、リターンアドレスに相当する値を0としてスタックに退避させています。


movl $SYS_exec, %eax
movl命令によって、execのシステムコール番号7(SYS_exec)をeaxレジスタにセットします。
システムコールを利用する場合は、そのシステムコールに対応するシステムコール番号(システムコールを識別する番号)をeaxレジスタにセットする必要があります。execシステムコールに対応するシステムコール番号は7(SYS_exec)なので、この値をeaxレジスタにセットしています。


int $T_SYSCALL
int $T_SYSCALL命令により、execsシステムコールを呼び出します。
int $T_SYSCALL命令を実行するとソフトウェア割り込みが生じ、割り込みディスクリプタテーブルにあるインデックスT_SYSCALL(0x40→64)のエントリが参照しているハンドラへ処理が移ります(Xv6では、割り込みベクタ番号0x40→64をシステムコールに割り当てています)。
execシステムコールが成功した場合、呼び出し元(initcode.S)に処理は戻りません。

execシステムコールが失敗した場合の処理

exit:
  movl $SYS_exit, %eax
  int $T_SYSCALL
  jmp exit

execシステムコールが失敗した場合は、ループ内で、exitシステムコールを呼び出すようにします。




次回
jupiteroak.hatenablog.com