procfs経由でデータの読み書き

今日はprocfs経由でデータの読み書きしようというのをやってみました。

ここを参考にしました。
The Linux Kernel Module Programming Guide

こんな感じの動作になります。

lisa:~# cat /proc/proc_test 
Hello, World
lisa:~# echo -n "foobarbaz" > /proc/proc_test 
lisa:~# cat /proc/proc_test 
foobarbaz

Makefileはこれです。

<BUILD_DIR := $(shell pwd)
VERBOSE = 0
KERNDIR    := /lib/modules/$(shell uname -r)/build

obj-m := proc_test.o

#proc_test-objs := proc_test.o

all:
	make -C $(KERNDIR) SUBDIRS=$(BUILD_DIR) KBUILD_VERBOSE=$(VERBOSE) modules

clean:
	rm -f *.o
	rm -f *.ko	
	rm -f *.mod.c
	rm -f *~

そして実装はこちらです。

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/proc_fs.h>
#include <linux/stat.h>
#include <linux/string.h>
#include <asm/uaccess.h>

MODULE_DESCRIPTION("proc file system test");
MODULE_AUTHOR("masami256");
MODULE_LICENSE("GPL");

static const char const module_name[] = "proc_test";

static int proc_test_read(char *page, char **start, off_t off,
		  int count, int *eof, void *data);

static int proc_test_write(struct file *file, const char __user *buffer,
		    unsigned long count, void *data);

static struct proc_dir_entry *pdir_entry;

static int proc_test_init(void)
{
	printk(KERN_INFO "%s\n", __FUNCTION__);

	pdir_entry = create_proc_entry(module_name, 0644, NULL);
	if (!pdir_entry) {
		remove_proc_entry(module_name, NULL);
		return -EFAULT;
	}

	pdir_entry->mode = S_IRUGO;
	pdir_entry->gid = 0;
	pdir_entry->uid = 0;
	pdir_entry->read_proc = (read_proc_t *) proc_test_read;
	pdir_entry->write_proc = (write_proc_t *) proc_test_write;

	return 0;
}

static void proc_test_cleanup(void)
{
	printk(KERN_INFO "%s\n", __FUNCTION__);
	remove_proc_entry(module_name, NULL);
}

static char proc_test_buf[256] = "Hello, World";

static int proc_test_read(char *page, char **start, off_t off,
		  int count, int *eof, void *data)
{
	int len = 0;

	len = sprintf(page, "%s\n", proc_test_buf);
	*peof = 1;

	return len;
}


static int proc_test_write(struct file *file, const char __user *buffer,
		    unsigned long count, void *data)
{
	if (count > 256)
		return -EINVAL;

	if (!buffer)
		return -EINVAL;
	
	memset(proc_test_buf, 0x0, sizeof(proc_test_buf));
	if (copy_from_user(proc_test_buf, buffer, count))
		return -EFAULT;

	return count;
}


module_init(proc_test_init);
module_exit(proc_test_cleanup);

最初と最後で必ずするのは、procfsへの登録と解除で、create_proc_entry()とremove_proc_entry()を使います。
これらの2番目の引数は、以下のようにparentとなっているので、/proc直下にファイルを作る分にはNULLで大丈夫です。
直下にしか作ってないので試してないですが・・・

struct proc_dir_entry *create_proc_entry(const char *name, mode_t mode,
                                         struct proc_dir_entry *parent)

procファイルシステムで使う構造体はstruct proc_dir_entryで、これのメンバ変数、read_procとwrite_procに関数を設定してあげます。
この構造体はinclude/linux/proc_fs.hで以下の用にtypedefされてます。

typedef int (read_proc_t)(char *page, char **start, off_t off,
                          int count, int *eof, void *data);
typedef int (write_proc_t)(struct file *file, const char __user *buffer,
                           unsigned long count, void *data);

ちなみに、read_proc()はfs/proc/generic.cの__proc_file_read()から呼ばれます。
write_procは同ファイルのproc_file_write()からです。

カーネルモジュールとしてロードするので、insmodすれば/procに/proc/proc_testが出来て、rmmodすれば無くなります。