fs.c int readi(struct inode *ip, char *dst, uint off, uint n)

トップページ
jupiteroak.hatenablog.com


fs.c
https://github.com/mit-pdos/xv6-public/blob/master/fs.c#L452

int readi(struct inode *ip, char *dst, uint off, uint n)
{
  uint tot, m;
  struct buf *bp;

  if(ip->type == T_DEV){
    if(ip->major < 0 || ip->major >= NDEV || !devsw[ip->major].read)
      return -1;
    return devsw[ip->major].read(ip, dst, n);
  }

  if(off > ip->size || off + n < off)
    return -1;
  if(off + n > ip->size)
    n = ip->size - off;

  for(tot=0; tot<n; tot+=m, off+=m, dst+=m){
    bp = bread(ip->dev, bmap(ip, off/BSIZE));
    m = min(n - tot, BSIZE - off%BSIZE);
    memmove(dst, bp->data + off%BSIZE, m);
    brelse(bp);
  }
  return n;
}

readi関数は、引数ipで指定されたiノードが所有するデータの範囲において、引数offで指定された位置から、引数nで指定されたデータサイズを読み込み、その読み込んだ内容を先頭アドレスが引数dstであるメモリ領域に保存します。

引数 struct inode *ip
読み込み対象となるデータを所有しているiノード(inode構造体)のアドレスです。

引数 char *dst
iノードから読み込んだデータが保存されるメモリ領域の先頭アドレスです。

引数 uint off
iノードが所有するデータの範囲内における読み込み開始位置(バイト単位)です。

引数 uint n
読み込まれる予定のデータサイズ(バイト単位)です。

戻り値 uint n
readi関数が実際に読み込んだデータサイズ(バイト単位)です。


処理の内容

?????????

if(ip->type == T_DEV){
    if(ip->major < 0 || ip->major >= NDEV || !devsw[ip->major].read)
      return -1;
    return devsw[ip->major].read(ip, dst, n);
}

、、、

offが不正な値ではないことを確認する

if(off > ip->size || off + n < off)
   return -1;

①off > ip->sizeが真、または、②off + n < offが真となる場合→①iノードが所有するデータサイズ ip->size(iノードが所有する全てのデータブロックのデータサイズの総計)の範囲に読み込み開始位置offが含まれない、または、②詠み込み開始位置offと読み込まれるデータサイズnの和でラップアラウンドが生じる場合は、戻り値を-1として処理を終了します。

読み込むデータサイズの上限nを定める

if(off + n > ip->size)
   n = ip->size - off;

off + n > ip->size が真となる場合→offの読み込み開始位置からnバイトサイズのデータを読み込む時に、iノードが所有するデータサイズ ip->size(iノードが所有する全てのデータブロックのデータサイズの総計)の範囲内に、読み込まれるデータサイズnが収まらない場合は、offの読み込み開始位置から残りのデータサイズip->size - offを読み込まれるデータサイズの上限とします。

nバイトサイズのデータを読み込む

for(tot=0; tot<n; tot+=m, off+=m, dst+=m){
     ...
 }

ループごとに、ハードディスクからデータを読み込んでいきます。

iノードが所有するデータブロック1個の内容を取得する

bp = bread(ip->dev, bmap(ip, off/BSIZE));

bread関数を呼び出して、iノードのデータブロックの内容を取得します(iノードのデータブロックに割り当てられたセクタに対応しているバッファを取得します)。
bread関数でiノードのデータブロックの内容を取得する際は、読み込み開始位置offが含まれるデータブロックに割り当てられたセクタのセクタ番号を指定する必要があります。そのために、bread関数の第二引数にはbmap(ip, off/BSIZE)の戻り値を設定します。
bmap関数は、第一引数で指定されたiノードが所有するデータブロックのうち、第二引数のインデックスで指定されるデータブロックに割り当てられたセクタのセクタ番号を取得します。offをBSIZEで除算した値off/BSIZEは、読み込み開始位置offが含まれているデータブロックを指定するインデックスになります。
bmap関数の第一引数にip、第二引数にoff/BSIZEを指定することで、iノードipが所有するデータブロックのうち、off/BSIZE番目にあるデータブロックに割り当てられているセクタのセクタ番号を取得することができます。

今回のループで読み込むデータサイズを定める

m = min(n - tot, BSIZE - off%BSIZE);

mは、for文における今回のループで読み込むデータサイズです。
totは、for文における今までのループで読み込んだデータサイズの合計です。
nは、readi関数が読み込む必要があるデータサイズの総計です。
readi関数が読み込む必要がある残りのデータサイズn - totが、データブロック間でまたがっている場合とそうでない場合を考慮して、mの値を定めます。
読み込む必要がある残りのデータサイズn - totがデータブロック間でまたがっていない場合は、n - totがfor文における今回のループで読み込むデータサイズmの値となります。
読み込む必要がある残りのデータサイズn - totがデータブロック間でまたがっている場合は、BSIZE - off%BSIZEがfor文における今回のループで読み込むデータサイズmの値となります。
off%BSIZEは、iノードが所有するデータ(iノードが所有する全てのデータブロックのデータサイズの総計)の範囲内における読み込み開始位置offを、データブロック(セクタ1個)に含まれるデータの範囲内おける読み込み開始位置に変換した値です。BSIZE - off%BSIZEは、データブロック(セクタ1個)に含まれるデータの範囲内において、詠み込み開始位置off%BSIZEからデータブロックの終端までの残りのデータサイズです。
bread関数は、1つのデータブロックの内容しか取得できないので、このような処理が必要となります。

読み込んだデータを指定されたメモリ領域に保存する

memmove(dst, bp->data + off%BSIZE, m);

memmove関数を呼び出して、データブロック(先頭アドレス bp->data + off%BSIZE・サイズmのメモリ領域)から、先頭アドレスがdst・サイズがmのメモリ領域へ、データをコピーします。

バッファのスリープロックを解放する

brelse(bp);

brelse関数を呼び出して、バッファbpに関わるスリープロックを解放し、他のプロセスがこのバッファを利用できるようにします。

for文の継続条件について

読み込んだデータサイズmの分だけ、今までのループで読み込んだデータサイズの合計totと、iノードが所有するデータサイズ ip->sizeの範囲内に位置するオフセットoffと、読み込んだデータを保存するメモリ領域の先頭アドレスdstをインクリメントします。

読み込んだデータサイズの値を戻り値としてリターンする

return n;

最後に、読み込んだデータサイズの値を戻り値としてリターンします。