KVMはioctl(2)でVMを作ったりvcpuを作ったりします。この時に使うfdは/dev/kvmファイルに対してioctl(2)を実行した時の戻り値です。ioctl(2)の戻り値がioctl(2)可能なfdとなってます。ユーザーランドに返すfdはどうやって作るのかというのが今回のめもです。
早速ですが、出来たコードはこうなります。
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/module.h> #include <linux/kernel.h> #include <linux/slab.h> #include <linux/anon_inodes.h> #include <linux/string.h> #include <linux/miscdevice.h> #include <linux/fs.h> #include <linux/file.h> MODULE_DESCRIPTION("ioctl test"); MODULE_AUTHOR("masami256"); MODULE_LICENSE("GPL"); struct fd_test_data { unsigned long id; }; static long fd_test_anon_fd_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg) { int r = -1; struct fd_test_data *p = filp->private_data; pr_info("%s: ioctl is %d\n", __func__, ioctl); switch (ioctl) { case 0: r = p->id; break; default: pr_warn("unkown ioctl %ud\n", ioctl); } return r; } static struct file_operations fd_test_anon_fd_fops = { .unlocked_ioctl = fd_test_anon_fd_ioctl, .compat_ioctl = fd_test_anon_fd_ioctl, .llseek = noop_llseek, }; static int create_file(unsigned long id) { struct fd_test_data *p = kmalloc(sizeof(*p), GFP_KERNEL); int fd; char name[256] = { 0x0 }; struct file *filp; if (!p) return -ENOMEM; p->id = id; snprintf(name, sizeof(name), "fd_test-%ld\n", id); filp = anon_inode_getfile(name, &fd_test_anon_fd_fops, p, O_RDWR); if (IS_ERR(filp)) return -1; fd = get_unused_fd_flags(O_CLOEXEC); if (fd < 0) return -1; fd_install(fd, filp); return fd; } static long fd_test_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg) { long r = -EINVAL; pr_info("%s: ioctl is %d\n", __func__, ioctl); switch (ioctl) { case 0: r = create_file(arg); break; default: pr_warn("unkown ioctl %ud\n", ioctl); } return r; } static struct file_operations fd_test_chardev_fops = { .unlocked_ioctl = fd_test_ioctl, .compat_ioctl = fd_test_ioctl, .llseek = noop_llseek, }; static struct miscdevice fd_test_chardev = { .minor = 243, .name = "fd_test", .fops = &fd_test_chardev_fops, }; static int fd_test_init(void) { int fd; fd = misc_register(&fd_test_chardev); if (fd < 0) { pr_warn("failed to create miscdevice\n"); return fd; } pr_info("device file created\n"); return fd; } static void fd_test_exit(void) { misc_deregister(&fd_test_chardev); pr_info("finish %s\n", __func__); } module_init(fd_test_init); module_exit(fd_test_exit);
まずはmiscデバイスを作っていて、最初にこのファイルを開いてioctl(2)を実行します。そうするとioctl(2)を実行できるfdが返るという感じです。 このfdの作り方は以下の3ステップでできます。
- fdを新たに割り当て
- file構造体を作成
- fdとファイル構造体を関連付ける
あ、上記のコードだと1と2は逆ですが。
1のfdの割り当てはget_unused_fd_flags()で行います。 2はanon_inode_getfile()です。この関数の3番目の引数はfile構造体のprivate_data変数に設定するデータです。上記のコードだとfd_test_data構造体を設定してます。 最後の3はfd_install()で、見ての通りfdとfile構造体を渡してます。 そして、fdをユーザーランドに返せば完了です。
テストコードはこんな感じです。
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/ioctl.h> int main(int argc, char **argv) { int fd; int anon_fd; int r; unsigned long id = argc == 2 ? strtoul(argv[1], NULL, 10) : 1234UL; fd = open("/dev/fd_test", O_RDWR); if (fd < 0) { perror("open"); exit(-1); } anon_fd = ioctl(fd, 0, id); if (anon_fd < 0) { perror("ioctl"); exit(-1); } r = ioctl(anon_fd, 0, 0UL); printf("id is %d\n", r); close(fd); }
これをコンパイルして動かすとこんなふうになります。
masami@kerntest:~/fd_test$ sudo ./a.out 1234 id is 1234 masami@kerntest:~/fd_test$ sudo ./a.out 12345678 id is 12345678 masami@kerntest:~/fd_test$
( ´ー`)フゥー...
ふつうのLinuxプログラミング 第2版 Linuxの仕組みから学べるgccプログラミングの王道
- 作者: 青木峰郎
- 出版社/メーカー: SBクリエイティブ
- 発売日: 2017/09/22
- メディア: Kindle版
- この商品を含むブログを見る