dofilewrite and vn_write

540 views

Published on

NetBSD-6.0.1のコードリーディング。

0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total views
540
On SlideShare
0
From Embeds
0
Number of Embeds
3
Actions
Shares
0
Downloads
3
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

dofilewrite and vn_write

  1. 1. dofilewrite と vn_writeについて#start_printfクサバ @kusabanachi
  2. 2. このスライドの概要など● printf 動作を理解するために、その経路中の dofilewrite と vn_write を調べました● 対象は NetBSD-6.0.1
  3. 3. 全体地図ココ#start_printf wiki よ り
  4. 4. dofilewrite
  5. 5. intdofilewrite(int fd, struct file *fp, const void *buf,size_t nbyte, off_t *offset, int flags, register_t *retval){struct iovec aiov;struct uio auio;size_t cnt;int error;aiov.iov_base = __UNCONST(buf); /* XXXUNCONST kills const */aiov.iov_len = nbyte;auio.uio_iov = &aiov;auio.uio_iovcnt = 1;auio.uio_resid = nbyte;auio.uio_rw = UIO_WRITE;auio.uio_vmspace = curproc->p_vmspace;/** Writes return ssize_t because -1 is returned on error. Therefore* we must restrict the length to SSIZE_MAX to avoid garbage return* values.*/if (auio.uio_resid > SSIZE_MAX) {error = EINVAL;goto out;}cnt = auio.uio_resid;error = (*fp->f_ops->fo_write)(fp, offset, &auio, fp->f_cred, flags);if (error) {if (auio.uio_resid != cnt && (error == ERESTART ||error == EINTR || error == EWOULDBLOCK))error = 0;if (error == EPIPE && !(fp->f_flag & FNOSIGPIPE)) {mutex_enter(proc_lock);psignal(curproc, SIGPIPE);mutex_exit(proc_lock);}}cnt -= auio.uio_resid;ktrgenio(fd, UIO_WRITE, buf, cnt, error);*retval = cnt;out:fd_putfile(fd);return (error);}
  6. 6. dofilewrite
  7. 7. dofilewrite ( man の説明)DOFILEREAD(9) – high level file operationsdofilewrite() は buf のバッファから ,ファイルエントリ fp のオブジェクトに ,nbyte のデータの write をする・・・*offset はファイル操作のオフセットで、操作後のオフセットも *offset に入って戻るFOF_UPDATE_OFFSET フラグが flags に設定されていると、fp のファイルオフセットが更新されるfd は ktrace 以外ではほぼ使っていない成功すると転送されたバイト数が *retval で戻る
  8. 8. dofilewrite ( iovec と uio 構造体)
  9. 9. dofilewrite ( uio の man )UIOMOVE(9) – uio 構造体で記述されるデータ移動uio 構造体は通常移動中のデータを表すstruct uio {struct iovec *uio_iov; :処理される I/O ベクタ s へのポインタint uio_iovcnt; : uio_iov 配列の I/O ベクタの数off_t uio_offset; :対応するオブジェクトのオフセットsize_t uio_resid; :残り転送データ量enum uio_rw uio_rw; :リードかライトを示すフラグstruct vmspace *uio_vmspace; :転送データのアドレススペース};struct iovec {void *iov_base; :メモリのアドレスsize_t iov_len; :バイト数};
  10. 10. dofilewrite ( uio 構造体への代入)auio に、ファイルに書き込む buf とサイズ、WRITE フラグとアドレススペースを設定
  11. 11. Dofilewrite ( サイズの制限 )/* Writes はエラー時に -1(signed) を返すから戻り値は ssize_t* 従って、長さ(転送量)は SSIZE_MAX までに制限します*/src/sys/sys/errno.h#define EINVAL 22 /* Invalid argument */
  12. 12. dofilewrite ( write 関数のコール)データの転送量を退避して、ファイルエントリに割り当てられている write 関数をコール(関数ポインタがどうやって割り当てられたかについては調べれていません・・・)引数はファイルエントリ、オフセット、 uio 構造体、ファイルエントリの f_cred 、 dofilewrite の引数の flags
  13. 13. vn_write
  14. 14. /** File table vnode write routine.*/static intvn_write(file_t *fp, off_t *offset, struct uio *uio, kauth_cred_t cred,int flags){struct vnode *vp = (struct vnode *)fp->f_data;int count, error, ioflag, fflag;ioflag = IO_ADV_ENCODE(fp->f_advice) | IO_UNIT;fflag = fp->f_flag;if (vp->v_type == VREG && (fflag & O_APPEND))ioflag |= IO_APPEND;if (fflag & FNONBLOCK)ioflag |= IO_NDELAY;if (fflag & FFSYNC ||(vp->v_mount && (vp->v_mount->mnt_flag & MNT_SYNCHRONOUS)))ioflag |= IO_SYNC;else if (fflag & FDSYNC)ioflag |= IO_DSYNC;if (fflag & FALTIO)ioflag |= IO_ALTSEMANTICS;if (fflag & FDIRECT)ioflag |= IO_DIRECT;vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);uio->uio_offset = *offset;count = uio->uio_resid;if ((error = enforce_rlimit_fsize(vp, uio, ioflag)) != 0)goto out;error = VOP_WRITE(vp, uio, ioflag, cred);if (flags & FOF_UPDATE_OFFSET) {if (ioflag & IO_APPEND) {/** SUSv3 describes behaviour for count = 0 as following:* "Before any action ... is taken, and if nbyte is zero* and the file is a regular file, the write() function* ... in the absence of errors ... shall return zero* and have no other results."*/if (count)*offset = uio->uio_offset;} else*offset += count - uio->uio_resid;}out:VOP_UNLOCK(vp);return (error);}
  15. 15. vn_write
  16. 16. vn_write ( man の説明)VNFILEOPS(9) – vnode file descriptor operationvn_write(fp, offset, uio, cred, flags)fp は file 構造体、offset はファイル内のオフセットuio は読み出すメモリを表す uio 構造体cred は呼び出し元の資格flags は FOR_UPDATE_OFFSET が定義されているときは、ファイル内の読み出し位置を更新する成功するとゼロを返し、そうでなければエラーを返す
  17. 17. vn_write ( vnode の取り出し)FILE(9) – operations on file entriesf_data は下層のオブジェクトの実体の情報を持つsrc/sys/sys/file.hstruct file {...void *f_data; /* descriptor data, e.g. vnode/socket */
  18. 18. vn_write (フラグの再設定)各種フラグを再設定( fp->f_flag から ioflag 変数へ)
  19. 19. vn_write ( f_advice のエンコード)src/sys/sys/vnode.h#define IO_UNIT 0x00010 /* do I/O as atomic unit */#define IO_ADV_MASK 0x00003 /* access pattern hint */#define IO_ADV_SHIFT 0#define IO_ADV_ENCODE(adv) (((adv) << IO_ADV_SHIFT) & IO_ADV_MASK)#define IO_ADV_DECODE(ioflag) (((ioflag) & IO_ADV_MASK) >> IO_ADV_SHIFT)src/sys/sys/file.hu_int f_advice; /* access pattern hint; UVM_ADV_* */
  20. 20. vn_write ( APPEND と NONBLOCK フラグ)src/sys/sys/vnode.henum vtype { VNON, VREG, VDIR, VBLK, VCHR, VLNK, VSOCK,VFIFO, VBAD };struct vnode {enum vtype v_type; /* :: vnode type */
  21. 21. vn_write ( FSYNC と DSYNC フラグ)src/sys/sys/fcntl.h#define FFSYNC O_SYNC /* kernel */#define FDSYNC O_DSYNC /* kernel */OPEN(2) – open or create a file for reading or writingO_SYNC は WRITE 命令がファイルデータとファイルステータスの両方がストレージに格納されるのを待つ。 I/O ファイルの同期O_DSYNC は WRITE 命令がファイルデータがストレージに格納されるのを待つ。 I/O データの同期
  22. 22. vn_write (ファイルシステムの同期設定)src/sys/sys/vnode.hstruct vnode {struct mount *v_mount; /* ptr to vfs we are in */src/sys/sys/fstypes.h#define MNT_SYNCHRONOUS 0x00000002 /* file system writtensynchronously */
  23. 23. vn_write ( ALTIO と DIRECT フラグ)OPEN(2) – open or create a file for reading or writingO_ALT_IO は代わりの I/O セグメントが使われる。代わりの I/O セグメントは下層で定義され、ほとんどの場合は代替の効果はないO_DIRECT は通常ファイルの場合、カーネルのキャッシュにバッファリングされずに、下層のデバイスドライバと直接転送が行おうとする
  24. 24. vn_write ( vn_lock )VNSUBR(9)– high-level convenience functions for vnode operationsvn_lock(vp, flags)vnode のロックを得るための共通コード。フラグは以下LK_SHARED 共有ロックLK_EXCLUSIVE 排他ロックLK_NOWAIT ロックのために寝ないLK_RETRY ロックできるまで、ロックやり直す
  25. 25. vn_write ( enforce_rlimit_fsize )rlimit_fsize を強制する?(直訳)
  26. 26. vn_write --->enforce_rlimit_fsize ( curlwp )CURPROC(9) – current process, processor, and LWPStruct lwp *curlwp(void);curlwp マクロは現在の LWP の情報を持つ、 lwp 構造体を返すNetBSD ドキュメンテーション : カーネルhttp://www.jp.netbsd.org/ja/docs/kernel/LWP すなわち軽量プロセスは、一つのプロセス、またはカーネル内で実行されているプロセスの一つのスレッドに対応します
  27. 27. vn_write --->enforce_rlimit_fsize ( VOP_ISLOCKED )VNODEOPS(9) – vnode operationsVOP_ISLOCKED(vp)Vp がロックされているか調べる。排他ロックなら LK_EXCLUSIVE が、共有ロックなら LK_SHARED 、ロック無しなら 0 が返る
  28. 28. vn_write --->enforce_rlimit_fsize ( offset 位置)追加書込みフラグなら、ファイルの終端からそうでなければ、 uio に格納されたオフセットから
  29. 29. vn_write --->enforce_rlimit_fsize ( RLIMIT _ FSIZE )src/sys/sys/lwp.hstruct lwp {struct proc *l_proc; /* parent process */src/sys/sys/proc.h#define p_rlimit p_limit->pl_rlimitstruct proc {struct plimit *p_limit; /* Process limits */src/sys/sys/resourcevar.hstruct plimit {struct rlimit pl_rlimit[RLIM_NLIMITS];#define RLIMIT_FSIZE 1 /* maximum file size */
  30. 30. vn_write --->enforce_rlimit_fsize ( mutex )MUTEX(9) – mutual exclusion primitivesMutex は LWP と割込みハンドラの相互排他のためにカーネルで使われるmutex_enter(mtx)mutex を獲得するすでに取られていた場合、呼出し元は獲得できるまでブロックされて戻ってこない。mutex_exit(mtx)mutex を解放する
  31. 31. vn_write --->enforce_rlimit_fsize ( psignal )SIGNAL(9) – software signal facilitiespsignal(p, signum)kpsignal のラッパー関数。kpsignal(p, ks, data)ks->ksi_signo のシグナルがプロセス p に配達されるようにスケジュールする。
  32. 32. vn_write --->enforce_rlimit_fsize ( SIGXFSZ と EFBIG )src/sys/sys/signal.h#define SIGXFSZ 25 /* exceeded file size limit */src/sys/sys/errno.h#define EFBIG 27 /* File too large */
  33. 33. vn_write ( VOP_WRITE のコール)vnode, uio 構造体 , ioflag, 資格情報を引数に VOP_WRITE を呼ぶ
  34. 34. vn_write ( IO_APPEND のコメント)SuSv3:Single UNIX Specification Verision 3なんらかのアクションが行われる前に、 nbyte がゼロで、ファイルが通常ファイルで、エラーが無い場合、write 関数はゼロを返して他に結果を持たない。
  35. 35. vn_write ( FOF_UPDATE_OFFSET 処理)追加書き込みモード時はuio->uio_offset に更新後のファイルオフセットがあるようだそうでない場合は、転送データ残量の差分をオフセットに加算している
  36. 36. vn_write ( VOP_UNLOCK )VNODEOPS(9) – vnode operationsVOP_UNLOCK(vp)ロック状態のプロセスを起こすvp はアンロックされるファイルの vnode
  37. 37. dofilewrite ( vn_write の戻り値)src/sys/sys/errno.h#define ERESTART -3 /* restart syscall */#define EINTR 4 /* Interrupted system call */#define EWOULDBLOCK EAGAIN /* Operation would block */#define EPIPE 32 /* Broken pipe */
  38. 38. dofilewrite (エラー処理)残り転送データ量の変化があれば、ERESTART 、 EINTR 、 EWOULDBLOCK のエラーを消す。パイプが壊れた時のシグナル処理
  39. 39. dofilewrite (エラー処理)残り転送データ量の変化があれば、ERESTART 、 EINTR 、 EWOULDBLOCK のエラーを消す。パイプが壊れた時のシグナル処理
  40. 40. dofilewrite ( ktrace と *retval )vn_write コール前後の、残りのデータ転送量の差分から、転送したデータ量を計算して *retval に格納しているKtrgenio は ktrace の入り口の関数。int ktrace_on;が有効なら処理が行われる
  41. 41. dofilewrite ( fd_putfile )/** Release a reference to a file descriptor acquired withfd_getfile().*/voidfd_putfile(unsigned fd)FILEDESC(9) – file descriptor tables and operationsfd_getfile(fdp, fd)ファイルディスクリプタ fd に対応する、ファイルディスクリプタテーブル fdp にあるファイルエントリを得る
  42. 42. dofilewrite まとめ● Uio 構造体に write する情報等を入れる● サイズを SSIZE_MAX に制限する● ファイルエントリに登録されているwrite 関数 (vn_write) をコール● 戻り値がエラーの場合、必要な処理をする● ktrace に続く関数をコール● 転送したサイズを *retval にいれる● ファイルディスクリプタの参照を解放する
  43. 43. vn_write まとめ● 引数のファイル構造体から vnode を取り出す● 引数のフラグを元にフラグを再設定する● vn_lock で vnode をロックする● 通常ファイルの場合、ファイルサイズの上限に収まるかチェック● VOP_WRITE をコールする● FOF_UPDATE_OFFSET フラグが指定されてれば新しいオフセットを計算して更新する● VOP_UNLOCK で vnode のロックを解除する

×