コアダンプの数だけ強くなれるよ

見習いエンジニアの備忘log

proc経由でkernelとやり取りしてみる

procインタフェースを利用してユーザランドのアプリケーションとカーネルでデータをやりとりするサンプルです。

具体的には下記のようにechoでリダイレクトした文字列をカーネルで受信して、catで覗くとリダイレクトした文字列が表示されるような簡単なカーネルモジュールを作ります。開発環境はCentOS7です。

完成イメージ

$ echo "hello" > /proc/example 
$ cat /proc/example 
hello
$


ソースコード

procmod.c

#include <linux/module.h>
#include <linux/init.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/string.h>

static char example[64] = {0};

static ssize_t
example_write(
    struct file *filp,
    const char __user *buff,
    size_t user_len,
    loff_t *offset )
{
    size_t len = 0;

    if (0 != *offset)
        return -EINVAL;

    memset(example, 0, sizeof(example));

    len = user_len > sizeof(example) ? sizeof(example) : user_len;

    memcpy(example, buff, len);
    example[len-1] = '\n';

    printk(KERN_INFO "example: wrote - %s", example);

    return user_len;
}

/* /proc/exampleの内容を表示 */
static int
example_show(
    struct seq_file *p,
    void *v)
{
    seq_printf(p, "%s", example);
    printk(KERN_INFO "example: show - %s", example);
    return 0;
}

/* cat /proc/exampleで実行される */
static int
example_open(
    struct inode *inode,
    struct file *file)
{
    return single_open(file, example_show, NULL);
}

static const struct file_operations proc_example_operations = {
    .owner      = THIS_MODULE,
    .open       = example_open,
    .read       = seq_read,
    .llseek     = seq_lseek,
    .release    = single_release,
    .write      = example_write,
};

/* insmod実行時に呼び出される */
static int proc_example_init(void)
{
    proc_create("example", S_IRUGO | S_IWUGO, NULL, &proc_example_operations);
    printk(KERN_INFO "example: Module loaded, and created /proc/example\n");
    return 0;
}

/* rmmod実行時に呼び出される */
static void proc_example_exit(void)
{
    remove_proc_entry("example", NULL);
    printk(KERN_INFO "example: Module unloaded.\n");
}

module_init(proc_example_init);
module_exit(proc_example_exit);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Example Kernel Module");


Makefile

obj-m := procmod.o

all:
        make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

clean:
        make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean


実行結果

ビルドしてカーネルモジュールをロードします。

$ make
make -C /lib/modules/3.10.0-514.26.2.el7.x86_64/build M=/home/user/driver/proc modules
make[1]: Entering directory `/usr/src/kernels/3.10.0-514.26.2.el7.x86_64'
  CC [M]  /home/user/driver/proc/procmod.o
  Building modules, stage 2.
  MODPOST 1 modules
  CC      /home/user/driver/proc/procmod.mod.o
  LD [M]  /home/user/driver/proc/procmod.ko
make[1]: Leaving directory `/usr/src/kernels/3.10.0-514.26.2.el7.x86_64'
$
$ sudo insmod procmod.ko
$ lsmod |grep procmod
procmod                12702  0


f:id:segmentation-fault:20180106230106p:plain


ちゃんとアプリ、カーネル間でデータをやりとりできました。

次回は/dev/にスペシャルファイルを作ってアプリとカーネル間でepoll,ioctlを使った通信を試したいと思います。