ToDo:
Fortran I/O側でのバッファ管理の形態は、OPEN文が実行された時点での fileの種類に依存し、実際のI/O時にfile descriptorがどんな 実体と結びついているかは考慮されていない。 つまり、外部から FNUM + dup2(2)で上書きするという使い方は、 保証外の使用法である。(当たり前と言えば、当たり前ですよね)
バッファの管理形態としては、次の3種に分類できる。
種別 | 条件 | read(2)の呼び出しパターン |
unbuffered | isatty(3)が真 | 要求された読み込み量だけ read(2)を発行する |
special file | S_ISREGが偽 | バッファを埋めるために、read(2)を一度だけ呼び出す |
regular file | S_ISREGが真 | バッファが埋まるまで、read(2)を繰り返し呼び出す |
つまり、SADInspectで起きてる問題は、special file型としてOPEN された Logical Unit Number(LUN)へ socketを結びつけてネットワークから uni-byte streamを受け取る際に、SelectUnit[](ioctl(FIONREAD))で kernel buffer側に溜まっているデータ量を確認し Read[, {n*Byte}]で 溜まっているデータだけを受け取ろうとするが、ioctl(FIONREAD)発行後 read(2)システムコール呼び出しまでに新たに受信されたデータが、 Fortran I/O側のバッファにコピーされ、Read[]で指定した以上の データが kernel bufferから取り出されるために、適切な受信が 出来ない現象であると説明できます。
Fortran I/Oを使う限り、Fortran I/O側のバッファに溜まっている データ量を拾う手段が無いことには解決しません。 また、Fortran I/Oの具体的な実装はコンパイラ依存なので、 言語仕様にそうした手段が組み込まれない限りコードの移植性は望めません。
従って、これを解決するには、unbufferedにするしかありません。 具体的には、itopenbuf_内で仮想端末を確保してOPEN文に渡せばよい。 ただし、FGETCを用いた現状の uni-byte stream読み込みの実装では 著しいパフォーマンス劣化が予測されます。
仮想端末デバイスを一時ファイルにした itopenbuf_を実装。
Revision 1482は、USE_NEW_FORTRAN_OPEN_SIM=YESな状態でコンパイルした itopenbuf_はposix_openpt(3),grantpt(3),ptsname(3),revoke(2)を使った 仮想端末デバイスの確保を行いOPEN文でptyを開いたのち、 そのfile descriptorを/dev/nullで置き換えて、 仮想端末をクローズする。
以前のmkfifo(2)ベースの動作に戻すには、 COPTへ-DUSE_ITOPENBUF_MKFIFOを追加こと。
仮想端末版 itopenbuf()で作った Logical Unit Numberへ結びつけた pipe(2)での動作を検証してみました。
コンパイラ | Pipe[]へのRead[]の動作 |
g95 0.91 20080220 | unbuffered |
g77 3.4.6 | buffered |
gfortran 4.2.4 20080305(prerelease) | unbuffered |
gfortran 4.3.1 20080306(prerelease) | unbuffered |
Intel Fortran 7.1 | unbuffered |
Intel Fortran 8.1 | buffered |
Intel Fortranに関しては、最新の Version 10は持ってないので判断を 保留しますが...g77に関しては「窓から投げ捨てろ」ということですね
カテゴリー: Admin | Emacs | EPICS | Fortran | FreeBSD | GCC | hgsubversion | IPv6 | KEKB | LHC | Lisp | LLVM | MADX | Ryzen | SAD | samba | tDiary | unix | WWW | YaSAI | お仕事 | イベント | 出張 | 宴会 | 数学 | 艦これ | 買いもの | 追記 | 雑記