在開發過程中有時候需要為某個檔案快速地配置設定固定大小的磁盤空間,為什麼要這樣做呢? (1)可以讓檔案盡可能的占用連續的磁盤扇區,減少後續寫入和讀取檔案時的磁盤尋道開銷; (2)迅速占用磁盤空間,防止使用過程中所需空間不足。 (3) 後面再追加資料的話,不會需要改變檔案大小,是以後面将不涉及metadata的修改。
具體的例子有windows下的Bt下載下傳服務,或者一些基于固定大小檔案塊的存儲系統(如QFS)。 為某個檔案預配置設定磁盤空間必須是實際的占用磁盤空間, 以Linux來說,使用lseek或truncate到一個固定位置生成的“空洞檔案”是不會占據真正的磁盤空間的。 快速的為某個檔案配置設定實際的磁盤空間在Linux下可通過fallocate(對應的posix接口為posix_fallocate)系統調用來實作,目前支援ext4/xfs。 windows 下可通過SetFilePointer() 和SetEndOfFile()或者SetFileValidData()實作。
網上的例子: lseek可以用于快速建立一個大檔案。
#include<stdio.h>
#include<unistd.h>
#include<fcntl.h>
#include<unistd.h>
int main(void)
{
int fd;
char buf1[]="1234567890";
char buf2[]="0987654321";
fd=open("file.hole",O_WRONLY|O_CREAT|O_TRUNC);
if(fd<0)perror("creat file fail");
if(write(fd,buf1,10)==-1)perror("could not write");
if(lseek(fd,1000,SEEK_CUR)==-1)
perror("could not sleek");
if(write(fd,buf2,10)==-1)perror("could not write");
if(lseek(fd,12,SEEK_SET)==-1)perror("could not sleek");
if(write(fd,"abcdefg",7)==-1)perror("could not write");
// fsync
// write content to file
// then fdatasync, fsync
return 0;
}
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 運作結果: [email protected]:~/arm$ gcc -o app kongdong.c [email protected]:~/arm$ ./app [email protected]:~/arm$ cat file.hole // 使用cat指令輸入沒有回車的,是以用od指令 1234567890abcdefg0987654321 [email protected]:~/arm$ [email protected]:~/arm$ od -c file.hole //od後面的-c表示ASCII碼 0000000 1 2 3 4 5 6 7 8 9 0 \0 \0 a b c d 0000020 e f g \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 //lseek移到開頭的第12個位元組 0000040 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 * 0001760 \0 \0 0 9 8 7 6 5 4 3 2 1 0001774 [email protected]:~/arm$ ++++++++++++++++++++++++++++++++++++++++++++++++++
其他例子:
/*迅速建立一個大檔案*/
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
int fd;
int ret;
char buf[]="h";
if((fd = open("2.txt",O_RDWR|O_CREAT)) < 0)
{
perror("open");
}
ret = lseek(fd,1023,SEEK_CUR);
if(ret == -1)
{
perror("lseek error");
}
printf("%d\n",ret);
write(fd,buf,1);
close(fd);
return;
}
fallocate例子:
使用lseek的注意事項 一 函數介紹: 函數名: lseek() 功 能: 移動檔案讀/寫指針 所需頭檔案: #include <sys/types.h> #include <unistd.h>
函數原型: off_t lseek(int fd, off_t offset, int whence);
重新定位已打開的檔案的偏移量,與whence的取值有關; 參數: fd:檔案描述符,對應已經打開的檔案; offset:指出偏移量; whence:指出偏移的方式,取值如下: SEEK_SET:偏移到offset位置處(相對檔案頭) SEEK_CUR:偏移到目前位置+offset位置處; SEEK_END:偏移到檔案尾+offset位置處; 傳回值: 調用成功則傳回最終的偏移量(從檔案頭開始數); 調用失敗則傳回-1,并設定相應的errno;
二 巧妙利用 1. 傳回目前的偏移量 off_t currpos; currpos = lseek(fd, 0, SEEK_CUR);
2. 傳回檔案大小 off_t currpos; currpos = lseek(fd, 0, SEEK_END);
3. 擴充檔案大小 lseek()方法允許偏移 這個技巧也可用于判斷我們是否可以改變某個檔案的偏移量。如果參數 fd(檔案描述符)指定的是 pipe(管道)、FIFO 或者 socket,lseek 傳回 -1 并且置 errno 為 ESPIPE。 對于普通檔案(regular file),cfo 是一個非負整數。但對于特殊裝置,cfo 有可能是負數。是以,我們不能簡單地測試 lseek 的傳回值是否小于 0 來判斷 lseek 成功與否,而應該測試 lseek 的傳回值是否等于 -1 來判斷 lseek 成功與否。 lseek 僅将 cfo 儲存于核心中,不會導緻任何 I/O 操作。這個 cfo 将被用于之後的讀寫操作。 如果 offset 比檔案的目前長度更大,下一個寫操作就會把檔案“撐大(extend)”。這就是所謂的在檔案裡創造“空洞(hole)”。沒有被實際寫入檔案的所有位元組由重複的 0 表示。空洞是否占用硬碟空間是由檔案系統(file system)決定的。
概念補充: 目前檔案偏移量(current file offset),以下簡稱為 cfo。cfo 通常是一個非負整數,用于表明檔案開始處到檔案目前位置的位元組數。讀寫操作通常開始于 cfo,并且使 cfo 增大,增量為讀寫的位元組數。檔案被打開時,cfo 會被初始化為 0,除非使用了 O_APPEND 。
【參考】 1. man 2 fallocate 2. man 2 lseek