LinuxカーネルHack: LZOリアルタイムデータ圧縮ライブラリをカーネルモジュールから使う実験
前回、カーネルモジュールをUMLでいじれるようになったので、次は何をしようかと思って、今回はLZOで遊んでみることにした。最初は、zlibで遊ぼうかと思ったけれども、ちょっと違う方が面白そうだったので。それに、BtrfsのWikiの「Project ideas」(https://btrfs.wiki.kernel.org/index.php/Project_ideas)にLZO圧縮について書いてあって、ちょっと興味が出たから。うまくいけば、BtrfsのLZO組対応も今後検討してみたいから。
LZOとは
ロスレスの圧縮/展開ライブラリ。高速にリアルタイムで圧縮/展開ができるのが特徴。
http://www.oberhumer.com/opensource/lzo/
ちなみに、Btrfsではzlibを使っていて、LZOよりも遅い。BtrfsのWikiの「Project ideas」にも書いてあるけれども、BtrfsがLZOを対応できれば、より多くのユーザーが圧縮機能を有効にする可能性が高まりそう。
準備
LZOのソースを落とす。lzoのexamples/simple.cを見たら大体使い方はわかった。zlibと大してかわらない。むしろ、zlibよりもシンプル。
ビルドして、example/simple.cを動かしてみる。simple.cは、131072(128*1024)バイトの0でうまったバイト列を単に圧縮し、圧縮した結果を、逆に展開しているだけ。
% wget http://www.oberhumer.com/opensource/lzo/download/lzo-2.03.tar.gz % tar xvf lzo-2.03.tar.gz % lzo-2.03 % make % cd examples % ./simple LZO real-time data compression library (v2.03, Apr 30 2008). Copyright (C) 1996-2008 Markus Franz Xaver Johannes Oberhumer All Rights Reserved. compressed 131072 bytes into 526 bytes decompressed 526 bytes back into 131072 bytes Simple compression test passed.
カーネルからLZOを使うには、まずはLZOをカーネルに組み込む必要があるから、面倒そう。まさか無いだろうなと思いつつ、カーネルのlib以下を見てたら、ラッキーにもlzoを発見。すでにカーネルに取り込まれている。手間が省けた。以下のソースを読んで、ざっくり使い方をつかんだ。
- include/linux/lzo.h
- lib/lzo/*
- crypto/lzo.c
- drivers/staging/ramzswap/ramzswap_drv.c
- fs/jffs2/compr_lzo.c
LZOはデフォルトではUMLカーネルに組み込まれてない様子。menuconfigでそれらしきものを探す。Cryptographic API -> LZO compression algorithmをカーネルに組み込んでみた。
カーネルをビルドしてみる。途中経過を見ると、lib/lzo以下がビルドされてる。いけてそう。
% time make -j 3 ARCH=um scripts/kconfig/conf --silentoldconfig arch/um/Kconfig.x86 CHK include/linux/version.h make[1]: `arch/um/sys-i386/user-offsets.s' は更新済みです CHK arch/um/include/shared/user_constants.h CHK include/generated/utsrelease.h CALL scripts/checksyscalls.sh CHK include/generated/compile.h QUOTE arch/um/kernel/config.tmp QUOTE arch/um/kernel/config.c CC arch/um/kernel/config.o LD arch/um/kernel/built-in.o GZIP kernel/config_data.gz IKCFG kernel/config_data.h CC kernel/configs.o LD kernel/built-in.o CC crypto/lzo.o LD crypto/built-in.o CC lib/lzo/lzo1x_compress.o CC lib/lzo/lzo1x_decompress.o LD lib/lzo/lzo_compress.o LD lib/lzo/lzo_decompress.o LD lib/lzo/built-in.o LD lib/built-in.o LD vmlinux.o MODPOST vmlinux.o GEN .version CHK include/generated/compile.h UPD include/generated/compile.h CC init/version.o LD init/built-in.o LD .tmp_vmlinux1 KSYM .tmp_kallsyms1.S AS .tmp_kallsyms1.o LD .tmp_vmlinux2 KSYM .tmp_kallsyms2.S AS .tmp_kallsyms2.o LD .tmp_vmlinux3 KSYM .tmp_kallsyms3.S AS .tmp_kallsyms3.o LD vmlinux SYSMAP System.map SYSMAP .tmp_System.map LINK linux Building modules, stage 2. MODPOST 16 modules real 1m22.406s user 0m7.064s sys 0m27.190s
LZOが使えるようになったことをGDBで確認。LZOが組み込まれた!
% gdb ./linux (gdb) b lzo (タブを入力) lzo.c lzo1x_decompress_safe lzo_exit lzo1x_1_compress lzo_compress lzo_init lzo1x_compress.c lzo_ctx lzo_mod_fini lzo1x_decompress.c lzo_decompress lzo_mod_init
コード
lzoのexamples/simple.cとfs/jffs2/compr_lzo.cを参考に作ってみた。example/simple.cと同じく、0バイトのバイト列を圧縮するという無意味な。。
% cat lzo/lzo.c #include <linux/module.h> #include <linux/init.h> #include <linux/vmalloc.h> #include <linux/lzo.h> MODULE_LICENSE("GPL"); #define IN_LEN (128*1024L) static void *in_buf; static void *lzo_mem; static void *lzo_compress_buf; static void *lzo_decompress_buf; static int lzo_init(void) { int ret; size_t compress_len; size_t in_len; size_t out_len; printk(KERN_ALERT "enter lzo_init\n"); in_len = IN_LEN; in_buf = vmalloc(in_len); lzo_mem = vmalloc(LZO1X_MEM_COMPRESS); lzo_compress_buf = vmalloc(lzo1x_worst_compress(in_len)); lzo_decompress_buf = vmalloc(in_len); if (!in_buf || !lzo_mem || !lzo_compress_buf || !lzo_decompress_buf) { printk(KERN_ALERT "failed to allocate memory"); return -ENOMEM; } /* Fill with zero */ memset(in_buf, 0, in_len); /* Complession */ ret = lzo1x_1_compress(in_buf, in_len, lzo_compress_buf, &compress_len, lzo_mem); if (ret != LZO_E_OK) { printk(KERN_ALERT "failed to compress memory"); return -1; } if (compress_len >= in_len) { printk(KERN_ALERT "this block comtains incompressible data"); return -1; } printk(KERN_ALERT "in_len = %d, compress_len = %d", in_len, compress_len); /* Decompression */ out_len = in_len; ret = lzo1x_decompress_safe(lzo_compress_buf, compress_len, lzo_decompress_buf, &out_len); if (ret != LZO_E_OK || out_len != in_len) { printk(KERN_ALERT "failed to decompress memory ret=%d, out_len=%d, in_len=%d", ret, out_len, in_len); return -1; } return 0; } static void lzo_exit(void) { printk(KERN_ALERT "enter lzo_exit\n"); vfree(in_buf); vfree(lzo_mem); vfree(lzo_compress_buf); vfree(lzo_decompress_buf); } module_init(lzo_init); module_exit(lzo_exit);
ビルド&実行
前回と同じ方法でビルド。実行すると、131072(128*1024)バイトから526に圧縮されたと。これはexamples/simple.cを実行した時と同じ結果。
% make ARCH=um -C /mnt/home/xxx/linux-2.6/ M=`pwd` modules # dmesg -c # insmod lzo.ko # rmsmod lzo.ko # dmesg -c enter lzo_init in_len = 131072, compress_len = 526 enter lzo_exit
さいごに
BtrfsのLZOへの対応はそれほど手間をかけずにできそうな感触。ただ、fs/btrfs/zlib.cはそれなりに複雑なので、もう少し理解を深めてからでないと難しそう。