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に書き込むよう変更しなければならないですね…