proc

試験の環境用意せんとなぁ、と思いつつモジュールの課題に着手してみる。
課題は

/proc 内で clock ファイルを実装するモジュールを設計、構築せよ。

というナニなんですが、proc の取り扱い方から、な世界です。とりあえず module が load されたら /proc にファイルを作って unload されたら削除、というナニからトライ。
ええと、_Linux カーネル 2.6 解読室_の proc ファイルシステムなサンプルをパクッてとりあえず以下をでっち上げてみた。

#include <linux/module.h>   // required by all modules
#include <linux/kernel.h>   // required by printk()
#include <linux/seq_file.h> // required by struct seq_operations
#include <linux/fs.h>       // required by struct file_operations
#include <linux/proc_fs.h>  // required by {create, remove}_proc_entry
#include <linux/init.h>

MODULE_LICENSE("GPL");

struct point {
	long x;
	long y;
	struct point *next;
};

static struct point *point_head;

static void *point_next(struct seq_file *m, void *v, loff_t *pos)
{
	struct point *p = v;

	(*pos)++;
	return p->next;
}

static void *point_start(struct seq_file *m, loff_t *pos)
{
	struct point *p = point_head;
	loff_t n = *pos;

	if (n == 0) {
っ		seq_printf(m, "x      y\n");
	}
	while (n--) {
		p = p->next;
		if (!p) {
			return NULL;
		}
	}
	return p;
}

static void point_stop(struct seq_file *m, void *v)
{
}

static int point_show(struct seq_file *m, void *v)
{
	struct point *p = v;
	
	seq_printf(m, "%ld     %ld\n", p->x, p->y);
	return 0;
}

struct seq_operations point_op = {
	.start = point_start,
	.next = point_next,
	.stop = point_stop,
	.show = point_show
};

static int point_open(struct inode *inode, struct file *file)
{
	return seq_open(file, &point_op);
}

static struct file_operations proc_point_operations = {
	.open = point_open,
	.read = seq_read,
	.llseek = seq_lseek,
	.release = seq_release
};


// Start/Init function
static int yamanetoshi_init(void) {
    struct proc_dir_entry *entry;
    
    entry = create_proc_entry("yamanetoshi", 0, NULL);
    if (entry) {
       entry->proc_fops = &proc_point_operations;
    }
    return 0;
}

// End/Cleanup function
static void yamanetoshi_exit(void) {
	remove_proc_entry("yamanetoshi", NULL);
}

module_init(yamanetoshi_init);
module_exit(yamanetoshi_exit);

中身はほぼ_Linux カーネル 2.6 解読室_と同様なんですが、module なので unload なソレを追加。
ぢつはコンパイルが通るまでコツコツとヘッダの include をナニ。
一応 insmod したら lsmod で出力されて中身は無いけど /proc にもファイルがございました。無論、rmmod した時の動作確認もしとります。

解析

とりあえず写経ベイスなので意味合いを確認してみる方が良いな。ちなみに kernel なバージョンは 2.6.20 ッス。

エントリポイント

create_proc_entry 関数の実体は fs/proc/generic.c で定義されている模様。

struct proc_dir_entry *create_proc_entry(const char *name, mode_t mode,
					 struct proc_dir_entry *parent)
{
	struct proc_dir_entry *ent;
	nlink_t nlink;

	if (S_ISDIR(mode)) {
		if ((mode & S_IALLUGO) == 0)
			mode |= S_IRUGO | S_IXUGO;
		nlink = 2;
	} else {
		if ((mode & S_IFMT) == 0)
			mode |= S_IFREG;
		if ((mode & S_IALLUGO) == 0)
			mode |= S_IRUGO;
		nlink = 1;
	}

	ent = proc_create(&parent,name,mode,nlink);
	if (ent) {
		if (S_ISDIR(mode)) {
			ent->proc_fops = &proc_dir_operations;
			ent->proc_iops = &proc_dir_inode_operations;
		}
		if (proc_register(parent, ent) < 0) {
			kfree(ent);
			ent = NULL;
		}
	}
	return ent;
}

ええと今テキストにしている_実習 Linux カーネル_では自分で領域を allocate して (サンプルは static な領域でしたが) それを proc_register している流れになったいましたが、恐らく create_proc_entry はそうしたナニを wrap というか抽象化しているのだろうな、と判断。
で、proc_create 関数見てみるに以下 (主要な部分のみ

	len = strlen(fn);

	ent = kmalloc(sizeof(struct proc_dir_entry) + len + 1, GFP_KERNEL);
	if (!ent) goto out;

	memset(ent, 0, sizeof(struct proc_dir_entry));
	memcpy(((char *) ent) + sizeof(struct proc_dir_entry), fn, len + 1);
	ent->name = ((char *) ent) + sizeof(*ent);
	ent->namelen = len;
	ent->mode = mode;
	ent->nlink = nlink;

これってディレクトリエントリを直接ナニ??
う、ちょっと違いそげ。今日は限界かな、と言いつつ詳細を掘るのは微妙ですが、何らかの文字列をセットできるようになってないと課題は完了しませんな。

読む

基本、メモリに設定されたナニを出力する形になってれば良いはず、というのは理解できてるんですが、どうしたものか、という世界で手が動かぬ状態ッス。
サケ飲んでヤッたら微妙だ。(何