配列でリングバッファ

こんなトコから復習せんと駄目なナニが微妙。kmalloc 使えば良いのでしょうが、vmware の中とかでないと微妙なので ...

出掛けて帰宅

その後、カンニングしつつでっち上げたのが以下。

#include <stdio.h>
#include <string.h>

#define ARRAY_SIZ 10

static int top = 0, end = 0;
static char array[ARRAY_SIZ];

#define next(a) (((a) + 1) % ARRAY_SIZ)

static int enqueue(char x)
{
	if(next(end) == top)
		return EOF;

	array[end] = x;
	end = next(end);

	return 0;
}

static int dequeue(char *buf)
{
	if(top == end)
		return EOF;

	buf[0] = array[top];
	top = next(top);
	
	return 0;
}

int read_ring_buffer(char *buf, int siz)
{
	int i;

	for(i = 0; i < siz; i++) {
		if(EOF == dequeue(&buf[i]))
			return EOF;
	}

	return 0;
}

int write_ring_buffer(const char *buf, int siz)
{
	int i;

	for(i = 0; i < siz; i++) {
		if(EOF == enqueue(buf[i]))
			return EOF;
	}
	
	return 0;
}

int main(void)
{
	char buf[50];
	memset(buf, '\0', sizeof(buf));

	if (EOF == read_ring_buffer(buf, 5))
		printf("if top == end then return EOF\n");

	write_ring_buffer("abcde", 5);
	if(EOF == write_ring_buffer("fghij", 5))
		printf("write_ring_buffer return EOF\n");

	memset(buf, '\0', sizeof(buf));
	read_ring_buffer(buf, 3);
	if(0 == strcmp(buf, "abc"))
		printf("read_ring_buffer returns \"abc\"\n");

	memset(buf, '\0', sizeof(buf));
	read_ring_buffer(buf, 3);
	if(0 == strcmp(buf, "def"))
		printf("read_ring_buffer returns \"def\"\n");

	memset(buf, '\0', sizeof(buf));
	read_ring_buffer(buf, 3);
	if(0 == strcmp(buf, "ghi"))
		printf("read_ring_buffer returns \"ghi\"\n");

	if (EOF == read_ring_buffer(buf, 5))
		printf("if top == end then return EOF\n");

	return 0;
}

なんとなくこの循環リストっぽいナニで何とかしてみる。

盛り込み

まだ実機でヤッてるんですが大丈夫なのか。例えば read だと定義は以下なカンジで良いのでしょうか。

static ssize_t yamanetoshi_read(struct file *file, char __user *buf, size_t size, loff_t *offset)
  • top == end の場合
    • 逆端が open してなかったら EOF 戻す
  • dequeue が EOF 戻したら sleep_on する
  • size 分繰り返して memcpy_tofs で buf にコピィ
    • 繰り返しの中で dequeue が EOF 戻したら sleep_on して current 保存 (順番逆?
  • 基本的には size 戻す方向で良いのかどうか

うーん。offset ガン無視なんだけどいいのかなぁ。
あるいは write は

static ssize_t yamanetoshi_write(struct file *file, const char __user *buf, size_t size, loff_t *offset)

の中ではバッファが一杯なソレの心配をせんと駄目なのかな。面倒だからこれはスルーで kmalloc なナニの盛り込みで云々。ヤる事は

  • バッファがあふれた時は sleep_on しないとマズい
    • ってコトは read 側で write な current を wake_up させないと云々 (面倒

考えながらコードをでっち上げてたら微妙なのができた。vmware 上で試験するか実機でヤるか考え中ッス。一応 compile にパスしたソレが以下

#include <linux/module.h>   // required by all modules
#include <linux/kernel.h>   // required by printk()
#include <linux/init.h>
#include <linux/fs.h>

MODULE_LICENSE("GPL");

#define EOF (-1)

#define DRIVERNAME "yamanetoshi"
int fifo_major = 130;

#define BUFSIZ 50

static int readdev, writedev;
static struct task_struct *read_task, *write_task;
static int write_sleep, read_sleep;
static wait_queue_head_t queue;

static int top ,end;
static char array[BUFSIZ];

#define next(a) (((a) + 1) % BUFSIZ)

static int enqueue(char x)
{
	if(next(end) == top)
		return EOF;

	array[end] = x;
	end = next(end);

	return 0;
}

static int dequeue(char *buf)
{
	if(top == end)
		return EOF;

	buf[0] = array[top];
	top = next(top);

	return 0;
}

static int yamanetoshi_open(struct inode *inode, struct file *file)
{
	printk("yamanetoshi_open\n");
	printk(" file->f_version     : %lu\n", file->f_version);
	printk(" MINOR(inode->i_rdev): %d\n", MINOR(inode->i_rdev));

	if(0 == (MINOR(inode->i_rdev % 2))) {
		read_task = current;
	} else {
		write_task = current;
	}

	return 0;
}

static int yamanetoshi_release(struct inode *inode, struct file *file)
{
	printk("yamanetoshi_close\n");
	printk(" file->f_version     : %lu\n", file->f_version);
	return 0;
}

static ssize_t yamanetoshi_read(struct file *file, char __user *buf, size_t size, loff_t *offset)
{
	int i;

	if(NULL == write_task)
		return EOF;

	for(i = 0; i < size; i++) {
		if(EOF == dequeue(&buf[i])) {
			read_sleep = 1;
			interruptible_sleep_on(&queue);
		}
	}

	if(!write_sleep) {
		write_sleep = 0;
		wake_up_process(write_task);
	}

	return size;
}

static ssize_t yamanetoshi_write(struct file *file, const char __user *buf, size_t size, loff_t *offset)
{
	int i;

	for(i = 0; i < size; i++) {
		if(EOF == enqueue(buf[i])) {
			write_sleep = 1;
			interruptible_sleep_on(&queue);
		}
	}

	if(!read_sleep) {
		read_sleep = 0;
		wake_up_process(read_task);
	}

	return size;
}

struct file_operations fifo_fops = {
	.owner = THIS_MODULE,
	.read = yamanetoshi_read,
	.write = yamanetoshi_write,
	.ioctl = NULL,
	.open = yamanetoshi_open,
	.release = yamanetoshi_release,
};

// Start/Init function
static int yamanetoshi_init(void) {
	int ret;

	printk("initialize the fifo device\n");
	ret = register_chrdev(fifo_major, DRIVERNAME, &fifo_fops);

	if(ret < 0)
		return ret;

	if(fifo_major == 0)
		fifo_major = ret;

    return 0;
}

// End/Cleanup function
static void yamanetoshi_exit(void) {
	printk("cleanup the fifo device\n");
	unregister_chrdev(fifo_major, DRIVERNAME);
}

module_init(yamanetoshi_init);
module_exit(yamanetoshi_exit);

とても怖くて実機で試験とかできそうにないので vmware な環境作ります。(何