console.c void cprintf(char *fmt, ...)

トップページ
jupiteroak.hatenablog.com


console.c
https://github.com/mit-pdos/xv6-public/blob/master/console.c#L54

void cprintf(char *fmt, ...)
{
  int i, c, locking;
  uint *argp;
  char *s;

  locking = cons.locking;
  if(locking)
    acquire(&cons.lock);

  if (fmt == 0)
    panic("null fmt");

  argp = (uint*)(void*)(&fmt + 1);
  for(i = 0; (c = fmt[i] & 0xff) != 0; i++){
    if(c != '%'){
      consputc(c);
      continue;
    }
    c = fmt[++i] & 0xff;
    if(c == 0)
      break;
    switch(c){
    case 'd':
      printint(*argp++, 10, 1);
      break;
    case 'x':
    case 'p':
      printint(*argp++, 16, 0);
      break;
    case 's':
      if((s = (char*)*argp++) == 0)
        s = "(null)";
      for(; *s; s++)
        consputc(*s);
      break;
    case '%':
      consputc('%');
      break;
    default:
      // Print unknown % sequence to draw attention.
      consputc('%');
      consputc(c);
      break;
    }
  }

  if(locking)
    release(&cons.lock);
}

cprintf関数は、可変長引数で指定された値を用いて、引数fmtで指定されたフォーマットの文字列を画面に表示します。

引数 char *fmt
引数fmtで指定されたフォーマットの文字列を指定するアドレスです。

引数 ...
フォーマットの文字列内で用いる値は、可変長引数として指定します。

クリティカルセクションの入口を定める

locking = cons.locking;
  if(locking)
    acquire(&cons.lock);

lockingが真となる場合→これからロックを取得することを示すフラグがセットされている場合は、aquire関数を呼び出してロックを取得し、クリティカルセクションの入口とします。

フォーマットが不正の値ではないことを確認する

 if (fmt == 0)
    panic("null fmt");

フォーマットの文字列を指定するアドレスがない場合は、panic関数を呼び出します。

表示に利用する引数を指定するアドレスを取得する

argp = (uint*)(void*)(&fmt + 1);

&fmtはコールスタックに配置しているcprintf関数の第一引数を指定するアドレス値です。
char型へのポインタfmtのサイズは4バイトなので、&fmt + 1はcprintf関数の第二引数を指定するアドレス値(第一引数を指定するアドレス値+1×4バイト)となります。このアドレス値をuint型へのポインタargpに格納します。

フォーマットを画面表示する

for(i = 0; (c = fmt[i] & 0xff) != 0; i++){
    
   ...
  }

fmtが指定するフォーマットの文字列を表示していきます。
char型へのポインタfmtとインデックスiを用いることで、フォーマットの文字列に1バイトずつアクセスすることができます。
さらに、0xffでマスク処理することにより、変数cにfmt[i]の値のみが含まれるようにします。

文字データが%ではない場合

    if(c != '%'){
      consputc(c);
      continue;
    }

c != '%'が真となる場合→文字データが%ではない場合は、consput関数を呼び出して文字データcを画面に表示し、ループを継続します。

文字データが%の場合

%の次の文字データを取得する
c = fmt[++i] & 0xff;
if(c == 0)
   break;

インデックスiを前置インクリメントすることで、%の次の文字データを取得します。
c == 0が真となる場合→cがヌル文字の場合は、ループを脱出します。

%の次の文字データが指定する形式に基づいて引数を表示する
switch(c){
    case 'd':
      printint(*argp++, 10, 1);
      break;
    case 'x':
    case 'p':
      printint(*argp++, 16, 0);
      break;
    case 's':
      if((s = (char*)*argp++) == 0)
        s = "(null)";
      for(; *s; s++)
        consputc(*s);
      break;
    case '%':
      consputc('%');
      break;
    default:
      // Print unknown % sequence to draw attention.
      consputc('%');
      consputc(c);
      break;
    }


%の次の文字データがdの場合
printint関数を呼び出して、引数の値*argpを10進数・符号ありで表示します。
argpは後置インクリメントされるので、この時表示された引数の次にある引数を指定するようになります。
最後に、switch文を脱出し、ループを継続します。


%の次の文字データがxまたはpの場合
printint関数を呼び出して、引数の値*argpを16進数・符号なしで表示します。
argpは後置インクリメントされるので、この時表示された引数の次にある引数を指定するようになります。
最後に、switch文を脱出し、ループを継続します。


%の次の文字データがsの場合
char型へのポインタsを用いて引数の値*argpの先頭1バイトを取得します。
(s = (char*)*argp++) == 0が真となる場合→引数の値*argpの先頭1バイトがヌル文字の場合は、char型へのポインタsに "(null)"を代入します。
その後、for文とconsput関数を用いて、sが指定する文字列を画面に表示します。
最後に、switch文を脱出し、ループを継続します。


%の次の文字データが%の場合
consput関数を呼び出して、%を画面に表示します。
最後に、switch文を脱出し、ループを継続します。


%の次の文字データがその他の値だった場合
consput関数を呼び出して、%と文字データcを画面に表示します。
最後に、switch文を脱出し、ループを継続します。

クリティカルセクションの出口を定める

 if(locking)
    release(&cons.lock);

release関数を呼び出してロックを解放し、クリティカルセクションの出口とします。