int ftruncate(int fd, off_t length);
int msync(void *addr, size_t len, int flags);
ftruncate 擴展文件不補零?man 手冊稱:若 length 大於文件當前大小,文件會被擴展並填充 \\0。但在 arm-linux-gcc 3.4 環境下實測不生效——擴展後的區域內容不確定(非零)。
// 手動寫入零塊確保擴展區為零
off_t cur_size = lseek(fd, 0, SEEK_END);
if (target_size > cur_size) {
char *zero_buf = calloc(1, target_size - cur_size);
write(fd, zero_buf, target_size - cur_size);
free(zero_buf);
}
ftruncate(fd, target_size); // 最後再 truncate 確保大小精確
msync 起始地址必須頁對齊msync(addr, len, flags) 要求 addr 為頁大小整數倍(通常 4KB)。若直接傳入 mmap 返回的任意地址會失敗(返回 EINVAL)。
size_t pagesize = sysconf(_SC_PAGESIZE); // 獲取系統頁大小
void *aligned_addr = (void *)((uintptr_t)mmap_addr / pagesize * pagesize);
size_t aligned_len = len + (mmap_addr - aligned_addr);
int sync_mmap_region(void *mmap_addr, size_t len) {
size_t pagesize = sysconf(_SC_PAGESIZE);
uintptr_t addr = (uintptr_t)mmap_addr;
void *aligned_addr = (void *)(addr & ~(pagesize - 1)); // 向下對齊
size_t offset = addr - (uintptr_t)aligned_addr;
size_t aligned_len = len + offset;
if (msync(aligned_addr, aligned_len, MS_SYNC) == -1) {
perror("msync failed");
return -1;
}
return 0;
}
| 操作 | 注意事項 |
|---|---|
ftruncate 擴展文件 | 不可依賴自動零填充,需手動 write 零块 |
mmap 映射 | 映射長度建議頁對齊,便於後續 msync/munmap |
msync 同步 | 地址、長度均需頁對齊;MS_SYNC 同步寫回,MS_ASYNC 異步 |
| 錯誤處理 | 檢查返回值,errno 區分 EINVAL(對齊錯誤) ENOMEM(內存不足) 等 |
嵌入式 Linux 開發中,ftruncate 與 msync 的行為強依賴內核版本、文件系統、C 庫實現。切勿盲目信任 man 手冊描述,務必在目標平台實測並加入防御性代碼(手動零填充、地址對齊),才能保證跨平台穩定運行。