今日の原書で学ぶ64bitアセンブラ入門(6)でsparse fileの話題が出て気になったので調べてみた。何を調べたかったかというと、sparse file全体の大きさ、実際に使っているサイズはどう管理されているのかというところ。
ちなみに、Sparse FileについてはSparse Fileとは?とか、Arch Linuxのwikiが作り方、サイズの調べ方など充実います。
まず、sparse fileを作ってその時点でのサイズを見てみます。 ddでinputとして/dev/zeroを、outputにfile.imgを指定します。そして、seekで10MiB目に移動してますがデータそのものはcount=0を指定しているので書き込みしません。 そうすると読み込みバイト数0、書き込みバイト数0という感じで処理が終わります。
masami@saga:~/sparse_file_test$ dd if=/dev/zero of=file.img bs=1 count=0 seek=10M 0+0 records in 0+0 records out 0 bytes (0 B) copied, 0.000267005 s, 0.0 kB/s
そして、lsで見てみるとサイズが10MiBのファイルができています。これはddのseek=10Mの結果ですね。
masami@saga:~/sparse_file_test$ ls -lah file.img -rw-r--r-- 1 masami masami 10M Jun 15 22:56 file.img
次にduを見てみるとdiskの使用量は0になってます。これはデータを何一つ書いていないので当然と言えば当然ですね。
masami@saga:~/sparse_file_test$ du -h file.img 0 file.img
次にduでバイト数単位の使用量を見てみると10MiBでddで作った通りのサイズになっています。
masami@saga:~/sparse_file_test$ du -bh file.img 10M file.img
ここまでで分かるのはファイル全体のサイズは10MiBだけど、実際のディスク使用量は0ということ。これはsparse fileなのでまあ当然の結果ですが、これらのサイズはどこで見れるんだろう?というのが今回調べたところ。
ファイルのサイズなのでstat構造体は使うだろうというのは想像できるんだけどst_sizeはどっちの値になるんだ?というのはありますよね。 調べた結果、st_sizeはファイル全体のサイズが設定されているのでfile.imgの場合は10485760になります。では実際に使用量は?というとこれはstat構造体にあるst_blocksを使います。 この変数はmanによるとファイルに割り当てられたブロック数(サイズは512B)とあります。 そこで、適当な文字列(foo)をfile.imgの先頭3byteに書き込んで実際にディスクを割り当てられた状態にしてからduして確認をしてみます。
duの結果4.0kとなり1block分使われたことがわかります(ファイルがあるのは普通に作ったext4環境なので1ブロックサイズは4KiBです)。
masami@saga:~/sparse_file_test$ du -h file.img 4.0K file.img
duの-bオプションの場合は当然数値は変わりません。
masami@saga:~/sparse_file_test$ du -bh file.img 10M file.img
stat(1)を実行すると以下のようになり8ブロック使用していることがわかります。ただし、manにも書かれていたとおりここの1ブロックのサイズは512Bです。なので4KiBが使用している領域の大きさになります。IO Blockはファイルシステムの1ブロックのサイズです。
masami@saga:~/sparse_file_test$ stat file.img File: ‘file.img’ Size: 10485760 Blocks: 8 IO Block: 4096 regular file Device: 813h/2067d Inode: 6291832 Links: 1
これでdu -hの場合はst_blocks * 512、du -bやlsの場合はst_sizeを表示しているということがわかりました。
↓はテストに使ったコードです。
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/mman.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <assert.h> #define TEST_FILE_NAME "./file.img" static void do_lstat(struct stat *st) { if (lstat(TEST_FILE_NAME, st)) { perror("lstat"); exit(-1); } } static void print_file_size(void) { struct stat st; do_lstat(&st); printf("Total file size is %ld\n", st.st_size); printf("Block size is %ld\n", st.st_blksize); printf("Number of allocated blocks is %ld: total bytes is %ld\n", st.st_blocks, st.st_blocks * 512); } static void write_data(void) { int fd =0; char *mapped = NULL; struct stat st; do_lstat(&st); fd = open(TEST_FILE_NAME, O_RDWR); if (fd < 0) { perror("open"); exit(-1); } mapped = mmap(NULL, st.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (mapped == (char *) MAP_FAILED) { perror("mapped"); exit(-1); } mapped[0] = 'f'; mapped[1] = 'o'; mapped[2] = 'o'; munmap(mapped, st.st_size); close(fd); printf("wrote \"foo\" to first three bytes\n"); print_file_size(); } static void usage(char *name) { printf("usage %s [sw]\n", name); printf("\ts: print file size\n"); printf("\tw: write data to test file\n"); exit(0); } int main(int argc, char **argv) { int opt; if (argc == 1) usage(argv[0]); while ((opt = getopt(argc, argv, "sw")) != -1) { switch (opt) { case 's': print_file_size(); break; case 'w': write_data(); break; default: usage(argv[0]); } } return 0; }
プロのための Linuxシステム・10年効く技術 (Software Design plus)
- 作者: 中井悦司
- 出版社/メーカー: 技術評論社
- 発売日: 2012/06/15
- メディア: 大型本
- 購入: 6人 クリック: 88回
- この商品を含むブログ (17件) を見る