FPGA開発日記

カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages , English Version https://fpgadevdiary.hatenadiary.com/

xv6が最初のプロセスを立ち上げるまでの勉強(mpmainの中を追いかける)

※ この記事はまだ勉強中のため、いろいろ間違いがあるかもしれません。

さて、mpmain()の中で何が起きているのか、階層トレースを見てみながら、追い掛けていこう。

まずは、schedulerが呼ばれて、enableinterrupt()により割り込みが有効になる。

  <FunctionCall 59147183: mpmain(0x8010530c)>
  <Return: mpmain>
  <FunctionCall 59147195: cprintf(0x801007fc) ...>
  <Return: cprintf>
  <FunctionCall 59149641: atomic_swap(0x80105204)>
  <Return: atomic_swap>
  <FunctionCall 59149660: scheduler(0x80106964)>
    <FunctionCall 59149666: enableinterrupt(0x80105e8c)>
      <FunctionCall 59149672: is_interruptible(0x80105e4c)>
        <FunctionCall 59149678: read_cop0_status(0x80105df0)>
        <Return: read_cop0_status>
      <Return: is_interruptible>
      <FunctionCall 59149703: read_cop0_status(0x80105df0)>
      <Return: read_cop0_status>
      <FunctionCall 59149721: write_cop0_status(0x80105e1c)>
      <Return: write_cop0_status>
    <Return: enableinterrupt>
    <FunctionCall 59149744: acquire(0x801072c4)>
      <FunctionCall 59149751: pushcli(0x80107538)>
        <FunctionCall 59149758: disableinterrupt(0x8010717c)>
          <FunctionCall 59149764: is_interruptible(0x8010713c)>
            <FunctionCall 59149770: read_cop0_status(0x801070e0)>
            <Return: read_cop0_status>
          <Return: is_interruptible>
        <Return: disableinterrupt>
        <FunctionCall 59149811: is_interruptible(0x8010713c)>
          <FunctionCall 59149817: read_cop0_status(0x801070e0)>
          <Return: read_cop0_status>
        <Return: is_interruptible>
      <Return: pushcli>
      <FunctionCall 59149851: holding(0x801074dc)>
      <Return: holding>
      <FunctionCall 59149873: atomic_swap(0x80107238)>
      <Return: atomic_swap>
      <FunctionCall 59149902: getcallerpcs(0x801073ec)>
      <Return: getcallerpcs>
    <Return: acquire>
    <FunctionCall 59150135: switchuvm(0x8010a994)>
      <FunctionCall 59150142: pushcli(0x80107538)>
        <FunctionCall 59150149: disableinterrupt(0x8010717c)>
          <FunctionCall 59150155: is_interruptible(0x8010713c)>
            <FunctionCall 59150161: read_cop0_status(0x801070e0)>
            <Return: read_cop0_status>
          <Return: is_interruptible>
        <Return: disableinterrupt>
      <Return: pushcli>
      <FunctionCall 59150226: tlbwi(0x8010a324)>
      <Return: tlbwi>
      <FunctionCall 59150258: popcli(0x801075a4)>
        <FunctionCall 59150264: is_interruptible(0x8010713c)>
          <FunctionCall 59150270: read_cop0_status(0x801070e0)>
          <Return: read_cop0_status>
        <Return: is_interruptible>
      <Return: popcli>
    <Return: switchuvm>

scheduler()は無限ループになっており、実行可能なプロセスを探しては実行状態にしていく構造のようだ。

void
scheduler(void)
{
  struct proc *p;

  for(;;){
    // Enable interrupts on this processor.
    enableinterrupt();

    // Loop over process table looking for process to run.
    acquire(&ptable.lock);
    for(p = ptable.proc; p < &ptable.proc[NPROC]; p++){
      if(p->state != RUNNABLE)
        continue;

      // Switch to chosen process.  It is the process's job
      // to release ptable.lock and then reacquire it
      // before jumping back to us.
      proc = p;
      switchuvm(p);
      p->state = RUNNING;
      swtch(&cpu->scheduler, proc->context);
      switchkvm();

      // Process is done running for now.
      // It should have changed its p->state before coming back.
      proc = 0;
    }
    release(&ptable.lock);

  }
}

switchuvmとswitchkvmとは何だろう?x86の実装では、以下のようになっていた。TSSとは、Task State Segmentというものらしい。

void
scheduler(void)
{
  struct proc *p;

  for(;;){
    // Enable interrupts on this processor.
    enableinterrupt();

    // Loop over process table looking for process to run.
    acquire(&ptable.lock);
    for(p = ptable.proc; p < &ptable.proc[NPROC]; p++){
      if(p->state != RUNNABLE)
        continue;

      // Switch to chosen process.  It is the process's job
      // to release ptable.lock and then reacquire it
      // before jumping back to us.
      proc = p;
      switchuvm(p);
      p->state = RUNNING;
      swtch(&cpu->scheduler, proc->context);
      switchkvm();

      // Process is done running for now.
      // It should have changed its p->state before coming back.
      proc = 0;
    }
    release(&ptable.lock);

  }
}

タスク・ステート・セグメント - OS Project Wiki

マルチタスクを実現するための、メモリ上のデータ領域。
タスクの状態(主にレジスタの値)を保存しておくためのセグメントで、システム・セグメントの一種。
一つのタスクに対して、一つのTSSが与えられる。

あー、なるほど、バックアップ領域ね。バックアップした後に、lcr3によりカーネルページテーブルをcr3にロードする。

pushcliとpopcliは、cli(clear IFlag), sti(set IFlag)をラップし、さらにpush、popのように階層化する。