FPGA開発日記

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

xv6が最初のプロセスを立ち上げるまでの勉強(プロセスのロードから立ち上が、、、らなかった)

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

さて、switchuvmの中で何が起きているのか見ていこう。

    <FunctionCall 59150135: switchuvm(0x8010a994)>
      <FunctionCall 59150142: pushcli(0x80107538)>
        <FunctionCall 59150149: disableinterrupt(0x8010717c)>
          <FunctionCall 59150155: is_interruptible(0x8010713c)>
            <FunctionCall 59150161: read_cop0_status(0x801070e0)>
            <Return 59150174: read_cop0_status>
          <Return 59150184: is_interruptible>
        <Return 59150193: disableinterrupt>
      <Return 59150208: pushcli>
      <FunctionCall 59150226: tlbwi(0x8010a324)>
      <Return 59150254: tlbwi>
      <FunctionCall 59150258: popcli(0x801075a4)>
        <FunctionCall 59150264: is_interruptible(0x8010713c)>
          <FunctionCall 59150270: read_cop0_status(0x801070e0)>
          <Return 59150283: read_cop0_status>
        <Return 59150293: is_interruptible>
      <Return 59150315: popcli>
    <Return 59150322: switchuvm>
    <FunctionCall 59150361: finalizefork(0x80106bc8)>

switchuvmが終わって、次にFinalizeforkという関数が呼ばれているが、これは何だろう?少なくとも、この関数はC言語中には存在していなかった。 実際には、schedulerで最初のプロセスが選ばれて、finalizeforkという関数が呼ばれている。

finalizeforkは、forkretという関数から呼ばれているものだ。

p->context->eip = (uint)forkret;

MIPS用の実装では、以下のようになっている:

forkret:
  jal  finalizefork
  nop
  j    trapret
  nop

従って、switchuvmから戻ってくると、まずはforkret --> finalizeforkとなる。 finalizeforkでは、最初のプロセス起動である場合、initlogでログファイルの初期化を始める。

finalizefork(void)
{
  static int first = 1;
  // Still holding ptable.lock from scheduler.
  release(&ptable.lock);

  if (first) {
    // Some initialization functions must be run in the context
    // of a regular process (e.g., they call sleep), and thus cannot
    // be run from main().
    first = 0;
    initlog();
  }
}

// log.c
initlog(void)
{
  if (sizeof(struct logheader) >= BSIZE)
    panic("initlog: too big logheader");

  struct superblock sb;
  initlock(&log.lock, "log");
  readsb(ROOTDEV, &sb);
  log.start = sb.size - sb.nlog;
  log.size = sb.nlog;
  log.dev = ROOTDEV;
  recover_from_log();
}

readsbで、ルートデバイスのスーパブロックを読みにいっているらしい。

// fs.c
// Read the super block.
void
readsb(int dev, struct superblock *sb)
{
  struct buf *bp;

  bp = bread(dev, 1);
  memmove(sb, bp->data, sizeof(*sb));
  brelse(bp);
}
// bio.c
struct buf*
bread(uint dev, uint sector)
{
  struct buf *b;

  b = bget(dev, sector);
  if(!(b->flags & B_VALID))
    iderw(b);
  return b;
}
// ide.c
void
iderw(struct buf *b)
{
  struct buf **pp;

  if(!(b->flags & B_BUSY))
    panic("iderw: buf not busy");
  if((b->flags & (B_VALID|B_DIRTY)) == B_VALID)
    panic("iderw: nothing to do");
  if(b->dev != 0 && !havedisk1)
    panic("iderw: ide disk 1 not present");

  acquire(&idelock);  //DOC:acquire-lock

  // Append b to idequeue.
  b->qnext = 0;
  for(pp=&idequeue; *pp; pp=&(*pp)->qnext)  //DOC:insert-queue
    ;
  *pp = b;

  // Start disk if necessary.
  if(idequeue == b)
    idestart(b);

  // Wait for request to finish.
  while((b->flags & (B_VALID|B_DIRTY)) != B_VALID){
    sleep(b, &idelock);
  }

  release(&idelock);
}

ここで、デッドロックとなる。よく考えてみれば、IEDからの読み込み作業を開始して、IDEの読み込み完了の通知は割り込みを通じて来るんだった。 それがシミュレータに実装されていない。だからデッドロックしたのか。 では、それを実装していく。