ファイルシステムの凍結
ファイルシステムの凍結は、ファイルシステム単位で書き込む処理をウエイトさせる。これはデータベース等のバックアップを行う場合、バックアップ処理においてレコードの整合性が失われないように、ファイルシステム単位で書き込みを禁止するものである。
以下はその検証を行うサンプル。ここで注意しなければならない事は、凍結するファイルシステムはルートファイルシステムであってはいけない。ファイルへの書き込みを必要とする、システムとして動作しなければならないカーネル処理も、ウエイトされ、一種のデッドロック状態に陥る。(当たり前と言えばあたりまえ)
ioctlのファイルIDからFILE構造体が、そしてスーパブロック構造体が取得さる。ioctl_fsfreezeではまず、ケーパビリティおよびファイルシステム自身が凍結をサポートするかどうかのチェックが行われたのち、freeze_super()をコールする。
ファイルの書き込みコールバック関数は、カーネルから直接呼ばれることはなく、カーネル内のラッパー関数を通してコールされている。そうなると、ファイルシステムの凍結は、カーネル内で処理できるのでは?と。たぶんメモリーマップ処理のケースを考慮しての事ではと・・・。
以下はその検証を行うサンプル。ここで注意しなければならない事は、凍結するファイルシステムはルートファイルシステムであってはいけない。ファイルへの書き込みを必要とする、システムとして動作しなければならないカーネル処理も、ウエイトされ、一種のデッドロック状態に陥る。(当たり前と言えばあたりまえ)
#include <fcntl.h> #include <sys/ioctl.h> #include <linux/fs.h> #include <stdio.h> #include <string.h> int main(int argc, char* argv[]) { pid_t pid; int i, fid, ret; char* p; p = "fsfreez test\n"; fid = open("/mnt/test/aaa", O_RDWR); ret = ioctl(fid, FIFREEZE, NULL); pid = fork(); if(!pid) { for (i = 0; i < 5; i++) { printf("sleep:%d\n", i); sleep(i); } ret = ioctl(fid, FITHAW, NULL); return 0; } write(fid, p, strlen(p)); printf("writed\n"); close(fid); }ループバックデバイスで、データ用ファイルシステムを作成しマウントする。このファイルシステムに空のファイルaaaを作成する。
[root@localhost test]# dd if=/dev/zero of=disk bs=1M count=10 10+0 レコード入力 10+0 レコード出力 10485760 バイト (10 MB) コピーされました、 0.766505 秒、 13.7 MB/秒 : [root@localhost test]# mkfs.ext3 disk mke2fs 1.41.14 (22-Dec-2010) disk is not a block special device. Proceed anyway? (y,n) y : [root@localhost test]# mount -o loop disk /mnt/test/ [root@localhost test]# touch /mnt/test/aaaサンプルを動作。子プロセスがファイルシステムを解凍するまで、親プロセスのファイル書き込みがウエイトされている事がわかる。
[root@localhost test]# ./fsfreez sleep:0 sleep:1 sleep:2 sleep:3 sleep:4 writed [root@localhost test]# cat /mnt/test/aaa fsfreez test凍結するファイルIDで、ioctlを呼び出しているが、これはそのファイルを凍結するのでなく、このファイルが属しているファイルシステムを凍結することになる。
実装
ioctlシステムコールが呼ばれると、do_vfs_ioctl()がコールされ、FIFREEZEならioctl_fsfreeze()、FITHAWならioctl_fsthaw()がコールされ、かかる処理が行われる。ioctlのファイルIDからFILE構造体が、そしてスーパブロック構造体が取得さる。ioctl_fsfreezeではまず、ケーパビリティおよびファイルシステム自身が凍結をサポートするかどうかのチェックが行われたのち、freeze_super()をコールする。
ファイルの書き込みコールバック関数は、カーネルから直接呼ばれることはなく、カーネル内のラッパー関数を通してコールされている。そうなると、ファイルシステムの凍結は、カーネル内で処理できるのでは?と。たぶんメモリーマップ処理のケースを考慮しての事ではと・・・。
static int ioctl_fsfreeze(struct file *filp) { struct super_block *sb = filp->f_path.dentry->d_inode->i_sb; if (!capable(CAP_SYS_ADMIN)) return -EPERM; /* If filesystem doesn't support freeze feature, return. */ if (sb->s_op->freeze_fs == NULL) return -EOPNOTSUPP; /* Freeze */ return freeze_super(sb); }sb->s_frozen = SB_FREEZE_WRITEでファイルシステムを書き込み禁止とし、sync_filesystemで汚れているページブロックを更新する。次にsb->s_frozen = SB_FREEZE_TRANSでジャーナルの書き込みを禁止し、メモリマップされている領域を更新する。もしファイルシステムにs_op->freeze_fsが定義されていたら、それをコールする。ext3/4に関してはジャーナルの書き込み処理行っている。
int freeze_super(struct super_block *sb) { : : sb->s_frozen = SB_FREEZE_WRITE; smp_wmb(); sync_filesystem(sb); sb->s_frozen = SB_FREEZE_TRANS; smp_wmb(); sync_blockdev(sb->s_bdev); if (sb->s_op->freeze_fs) { ret = sb->s_op->freeze_fs(sb); if (ret) { printk(KERN_ERR "VFS:Filesystem freeze failed\n"); sb->s_frozen = SB_UNFROZEN; smp_wmb(); wake_up(&sb->s_wait_unfrozen); deactivate_locked_super(sb); return ret; } } up_write(&sb->s_umount); return 0; }__generic_file_aio_writeは、ファイルシステムに関係なく、データをファイルに書き込む時にコールされ、最初にvfs_check_frozenマクロで、ファイルシステムが凍結されているかどうかチェックし、凍結しているなら解凍されるまで待機することになる。
ssize_t __generic_file_aio_write(struct kiocb *iocb, const struct iovec *iov, unsigned long nr_segs, loff_t *ppos) { struct inode *inode = mapping->host; : vfs_check_frozen(inode->i_sb, SB_FREEZE_WRITE); : : } #define vfs_check_frozen(sb, level) \ wait_event((sb)->s_wait_unfrozen, ((sb)->s_frozen < (level)))