核心原理大白话 默认内核预读逻辑你读第1页 → 内核偷偷把第2、3、4页也读进来缓存顺序读爽歪歪 随机读时预读是纯浪费你跳到第999999页 → 内核还在帮你读1000000、1000001页 → 全是废操作白占 page cache、白烧I/OMADV_RANDOM就一句话告诉内核我要随机跳别帮我预读了要哪页给哪页---代码PHP没有现成的 mmap 扩展FFI直调 libc 是最简方式。?php// PHP 7.4 FFI直接调 libc$ffiFFI::cdef(Cvoid*mmap(void*addr,size_t len,intprot,intflags,intfd,long offset);intmunmap(void*addr,size_t len);intmadvise(void*addr,size_t len,intadvice);intopen(constchar*path,intoflag);intclose(intfd);C,libc.so.6);$file/data/bigfile.bin;$sizefilesize($file);// 只读打开文件拿到 fd$fd$ffi-open($file,0);// O_RDONLY 0// 把整个文件映射进虚拟内存物理上按需缺页中断加载// PROT_READ1 MAP_SHARED1$map$ffi-mmap(null,$size,1,1,$fd,0);$ffi-close($fd);// fd 关了没事map 还活着// ★ 关键告诉内核随机访问模式禁掉预读// MADV_RANDOM 1$ffi-madvise($map,$size,1);// 随机读任意字节像访问数组一样$bytesFFI::cast(uint8_t *,$map);$offsetrandom_int(0,$size-1);$val$bytes[$offset];echooffset$offsetbyte0x.sprintf(%02x,$val).PHP_EOL;// 收工$ffi-munmap($map,$size);---几个要注意的点 ┌────────────┬────────────────────────────────────────────────────────────────────┐ │ 问题 │ 说明 │ ├────────────┼────────────────────────────────────────────────────────────────────┤ │64位 │10GB 文件必须跑在64位系统64位PHP32位地址空间最多4GB │ ├────────────┼────────────────────────────────────────────────────────────────────┤ │MAP_FAILED│ 生产环境加判断$mapFFI::cast(void*,-1)时报错 │ ├────────────┼────────────────────────────────────────────────────────────────────┤ │ libc.so.6│ Alpine/musl 是 libc.musl-x86_64.so.1macOS 是 libc.dylib │ ├────────────┼────────────────────────────────────────────────────────────────────┤ │ php.ini │ 需要 ffi.enabletrue│ ├────────────┼────────────────────────────────────────────────────────────────────┤ │ 分段映射 │ 如果内存吃紧可以只 mmap 一个窗口比如1GB用 offset 参数滑动 │ └────────────┴────────────────────────────────────────────────────────────────────┘---MADV_RANDOM效果对比10GB 文件随机跳10000次 不加 madvise内核每次还帮你预读~128KB → 白读~1.28GB 垃圾数据 加了MADV_RANDOM每次只读4KB一页→ 实际I/O降到~40MB