fs.c int writei(struct inode *ip, char *src, uint off, uint n)

トップページ
jupiteroak.hatenablog.com


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

int writei(struct inode *ip, char *src, 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].write)
      return -1;
    return devsw[ip->major].write(ip, src, n);
  }

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

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

  if(n > 0 && off > ip->size){
    ip->size = off;
    iupdate(ip);
  }
  return n;
}

writei関数は、引数ipで指定されたiノードが所有するデータブロックの範囲において、引数offで指定された位置から、先頭アドレスが引数dst・サイズが引数nであるメモリ領域に保存されているデータを書き出します。

引数 struct inode *ip,
書き出し対象となるデータブロックを所有しているiノード(inode構造体)のアドレスです。

引数 char *src,
書き出す内容が保存されているメモリ領域の先頭アドレスです。

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

引数 uint n
書き出される予定のデータサイズ(バイト単位)です。

戻り値 uint n
writei関数が実際に書き出したデータサイズ(バイト単位)です。


処理の内容

?????

if(ip->type == T_DEV){
    if(ip->major < 0 || ip->major >= NDEV || !devsw[ip->major].write)
      return -1;
    return devsw[ip->major].write(ip, src, 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として処理を終了します。

書き出されるデータがiノードが所有するデータの範囲をこえる場合

  if(off + n > MAXFILE*BSIZE)
    return -1;

off + n > MAXFILE*BSIZEが真となる場合→offの書き出し開始位置からnバイトサイズのデータを書き出す時に、iノードが所有するデータサイズ MAXFILE*BSIZE(iノードが所有する全てのデータブロックのデータサイズの総計)の範囲内に、書き出されるデータサイズnが収まらない場合は、-1を戻り値として処理を終了します。

nバイトサイズのデータを書き出す

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

ループごとに、ハードディスクへデータを書き出していきます。

iノードが所有するデータブロックに対応しているバッファを取得する

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

bread関数を呼び出して、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は、writei関数が書き出す必要があるデータサイズの総計です。
writei関数が書き出す必要がある残りのデータサイズ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(bp->data + off%BSIZE, src, m);

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

データブロック(セクタ)をロギングにおけるトランザクションの管理対象として登録する

log_write(bp);

log_write関数を呼び出して、バッファbpに対応しているデータブロック(セクタ)を、ロギングにおけるトランザクションの管理対象として登録します。

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

brelse(bp);

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

必要であればip->sizeを更新する

if(n > 0 && off > ip->size){
    ip->size = off;
    iupdate(ip);
  }

n > 0 が真、かつ、off > ip->sizeが真となる場合→書き出し処理が行われ、かつ、書き出し処理(for文の処理)が終わった後のoffの値が ip->sizeを越えている場合は、ip->sizeをoffの値で更新し、iupdate関数を呼び出してiノードの内容をハードディスク上にあるiノードに反映させます。

書き出したデータサイズの値を戻り値としてリターンする

return n;

最後に、書き出したデータサイズの値を戻り値としてリターンします。