使用 mmap 實現文件複製,關鍵步驟:
fstat 獲取源文件大小 st_size。lseek(dst, st_size-1, SEEK_SET) 定位到目標文件末尾前 1 字節。write(dst, "a", 1) 寫入佔位符 'a',擴展目標文件至源文件大小。mmap 映射源文件 (PROT_READ) 和目標文件 (PROT_WRITE | MAP_SHARED)。memcpy(dst_map, src_map, st_size) 複製內存區域。為什麼
write(dst, "a", 1)寫入的'a'在最終複製的文件中找不到?
mmap 無法擴展文件大小,必須先通過 lseek + write 將目標文件「撐大」到源文件大小。寫入的 'a' 只是佔位,位於文件偏移量 st_size - 1。memcpy(dm, sm, st_size) 將源文件全部內容(包含最後一字節)複製到目標映射區。目標映射區的最後一字節正是之前寫入 'a' 的位置,因此被源文件的最後一字節覆蓋。'a' 不復存在。源文件 (大小 100 字節): [S0, S1, ..., S98, S99]
目標文件擴展後: [?, ?, ..., ?, 'a'] // lseek 99 寫入 'a'
mmap 映射後:
src_map -> 源文件內容
dst_map -> 目標文件內存映射 (最後一字節為 'a')
memcpy(dst_map, src_map, 100) 執行後:
dst_map -> [S0, S1, ..., S98, S99] // 'a' 被 S99 覆蓋
msync/文件關閉後,磁盤文件即為源文件完整副本
| 步驟 | 作用 | 關鍵參數 |
|---|---|---|
lseek(dst, st_size-1, SEEK_SET) | 定位文件末尾前 1 字節 | 偏移量 = 文件大小 - 1 |
write(dst, "a", 1) | 寫入 1 字節擴展文件 | 內容無關緊要,常用 '\0' 或 'a' |
mmap(..., PROT_READ, MAP_PRIVATE, ...) | 源文件只讀映射 | MAP_PRIVATE 修改不回寫磁盤 |
mmap(..., PROT_WRITE, MAP_SHARED, ...) | 目標文件可寫共享映射 | MAP_SHARED 修改同步回磁盤 |
memcpy(dm, sm, st_size) | 內存級高速複製 | 長度 = 源文件大小 |
memcpy 是完全覆蓋,佔位符只是為了讓 mmap 映射成功的臨時手段。msync 導致數據丟失:雖然 MAP_SHARED 通常自動回寫,但調用 msync(dm, st_size, MS_SYNC) 更保險。lseek(-1) 出錯:需特殊處理空文件情況。write 寫入的 'a' 是擴展文件大小的佔位符,後續 memcpy 將源文件完整內容複製到映射區時直接覆蓋了該位置,因此最終文件中不存在。這是 mmap 複製文件的標準模式:先撐大,再映射,再複製。