(Last Updated On: 2018年8月13日)
php-usersのMLに投稿した内容の補足です。コードはPHP 4.4の開発ブランチのソースです。
main/stream.cに_php_stream_passthru関数が定義されています。reafile関数(ファイルの中身を全て出力)、fpassthru関数(ファイルリソースの中身を全て出力)に利用されています。
PHPAPI size_t _php_stream_passthru(php_stream * stream STREAMS_DC TSRMLS_DC) { size_t bcount = 0; int ready = 0; char buf[8192]; #ifdef HAVE_MMAP int fd; #endif #ifdef HAVE_MMAP if (!php_stream_is(stream, PHP_STREAM_IS_SOCKET) && stream->filterhead == NULL && php_stream_tell(stream) == 0 && SUCCESS == php_stream_cast(stream, PHP_STREAM_AS_FD, (void*)&fd, 0)) { struct stat sbuf; off_t off; void *p; size_t len; fstat(fd, &sbuf); if (sbuf.st_size > sizeof(buf)) { off = php_stream_tell(stream); len = sbuf.st_size - off; p = mmap(0, len, PROT_READ, MAP_SHARED, fd, off); if (p != (void *) MAP_FAILED) { BG(mmap_file) = p; BG(mmap_len) = len; PHPWRITE(p, len); BG(mmap_file) = NULL; munmap(p, len); bcount += len; ready = 1; } } } #endif if(!ready) { int b; while ((b = php_stream_read(stream, buf, sizeof(buf))) > 0) { PHPWRITE(buf, b); bcount += b; } } return bcount; }
注目すべきは以下です。
if (p != (void *) MAP_FAILED) { BG(mmap_file) = p; BG(mmap_len) = len; PHPWRITE(p, len); BG(mmap_file) = NULL; munmap(p, len); bcount += len; ready = 1; }
PHPWRITEマクロは最終的に出力バッファに書き出します。PHPからの出力は最終的にWebサーバにバッファされる場合があります。つまり、一度に大量のデータを出力するとメモリを大量に必要とする可能性があります。
Momoery Mapped I/Oの利点がバッファ無しでデータを読める事は分りますが、大きなファイルを送信するには不適切な事は明らかです。データを適切なチャンクサイズに区切ってPHPWRITEに書き込むよう変更しなければならないですね…
Leave a Comment