Linux カーネル API: anon_inode_getfd, misc_register

KVM のソースを呼んでる中で anon_inode_getfd という API を知ったので、 KVMソースコードを写経しながらカーネルモジュールを作ってみた

github.com

... といってもユーザランドに向けて ioctl(2) のインタフェースを作るだけのモジュールで実用的なものでない。KVM API を理解するための写経 📝

デモ

モジュールを make して insmod すると /dev/hiboma が生える

$ sudo make
make -C /lib/modules/3.10.0-327.36.3.el7.x86_64/build SUBDIRS=/vagrant/kernel_module_scratch/anon_inode_getfile modules
make[1]: ディレクトリ `/usr/src/kernels/3.10.0-327.36.3.el7.x86_64' に入ります
  CC [M]  /vagrant/kernel_module_scratch/anon_inode_getfile/anon_inode_getfile.o
  Building modules, stage 2.
  MODPOST 1 modules
  CC      /vagrant/kernel_module_scratch/anon_inode_getfile/anon_inode_getfile.mod.o
  LD [M]  /vagrant/kernel_module_scratch/anon_inode_getfile/anon_inode_getfile.ko
make[1]: ディレクトリ `/usr/src/kernels/3.10.0-327.36.3.el7.x86_64' から出ます
$ sudo make insmod
insmod anon_inode_getfile.ko
$ ls -hal /dev/hiboma 
crw-------. 1 root root 10, 232 11月 18 12:35 /dev/hiboma

おまけで、dmesg にはこんなログを残す

[ 3232.721279] registerd /dev/hiboma 
[ 3269.707282] deregisterd /dev/hiboma 

/dev/hiboma を open(2) して ioctl(2) すると anon_inode デスクリプタが返ってくる。このデスクリプタに ioctl(2) を呼ぶとあれこれできる

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <err.h>

#define HIBOMA_GET_VERSION 0
#define HIBOMA_OPEN_FD     1

int main()
{
    int fd = open("/dev/hiboma", O_RDONLY|O_WRONLY);
    if ( fd < 0) {
        perror("failed to open /dev/hiboma");
        return 1;
    }

    int r = ioctl(fd, HIBOMA_GET_VERSION);
    if (r < 0 ) {
        perror("ioctl");
        return EXIT_FAILURE;
    }
    printf("version = %d\n", r);

    r = ioctl(fd, HIBOMA_OPEN_FD);
    if (r < 0 ) {
        perror("ioctl");
        return EXIT_FAILURE;
    }
    printf("fd = %d\n", r);

    r = ioctl(fd, HIBOMA_OPEN_FD);
    if (r < 0 ) {
        perror("ioctl");
        return EXIT_FAILURE;
    }
    printf("fd = %d\n", r);

    char command[32];
    snprintf(command, sizeof(command), "ls -hal /proc/%d/fd", getpid());
    system(command);

    exit(EXIT_SUCCESS);
}

/proc/$pid/fd でデスクリプタを見ると、キャラクタデバイスと anon_inode: プレフィクスを持つデスクリプタが見える。

dr-x------. 2 root root  0 1118 09:19 .
dr-xr-xr-x. 9 root root  0 1118 09:19 ..
lrwx------. 1 root root 64 1118 09:19 0 -> /dev/pts/0
lrwx------. 1 root root 64 1118 09:19 1 -> /dev/pts/0
lrwx------. 1 root root 64 1118 09:19 2 -> /dev/pts/0
l-wx------. 1 root root 64 1118 09:19 3 -> /dev/hiboma 🍣
lrwx------. 1 root root 64 1118 09:19 4 -> anon_inode:hiboma-anon 🍣
lrwx------. 1 root root 64 1118 09:19 5 -> anon_inode:hiboma-anon 🍣

面白い

「キャラクタデバイス /dev/kvm を open(2) + ioctl(2) すると anon_inode のデスクリプタを返し、anon_inode デスクリプタを ioctl(2) すると VM や vCPU を操作できる 」のが KVM の基調となるインタフェースで、ここでやっていることと設計は一緒 (真似してコピペで作ってるから当然なんだけどね! )

miscdevice と anon_inode

/dev/hiboma キャラクタデバイスは下記の API で作っている

int misc_register(struct miscdevice * misc,)

ioctl(2) が返す anon_inode のデスクリプタは下記の API で作れる

int anon_inode_getfd(const char *name, const struct file_operations *fops,
             void *priv, int flags)

anon_inode_getfd が返すファイルディスクリプタは、 anon_inodefs* という疑似ファイルシステムから割り当てられた inode を指す

miscdevice についても miscキャラクタデバイス - Linuxの備忘録とか・・・(目次へ) を読むのがよい