kfifo(その1)
kernel fifoをkfifoと言います。fifoはパイプそのもので、スペシャルファイルのiノードにpipeバッファを紐付け、読み/書きするプロセスがこのパイプバッファを参照することで実現しています。従ってその処理はvfs下に依存し、read/write単位の処理となります。(VFSで実装故、ユーザプロセスも利用できる事になります。)
kfifoは実装をvfsでなく、リングバッファのように読み手/書き手に参照ポイントを有し、読み手/書き手間でロックする事なく参照を実現し、また、FIFO単位(文字列/int/float等)を自由に設定できるようになっています。(それ故、マクロを多用した実装になっています。)
以下は、カーネルソースにあるkfifoのサンプルです。サンプルとしては冗長ぎみかな?って感じがしますが、以下の内容で検証をおこなっています。
kfifoは実装をvfsでなく、リングバッファのように読み手/書き手に参照ポイントを有し、読み手/書き手間でロックする事なく参照を実現し、また、FIFO単位(文字列/int/float等)を自由に設定できるようになっています。(それ故、マクロを多用した実装になっています。)
以下は、カーネルソースにあるkfifoのサンプルです。サンプルとしては冗長ぎみかな?って感じがしますが、以下の内容で検証をおこなっています。
・helloを書き込む。 ・0から9まで数値を書き込む。 ・5バイト読み出す。 ・2バイト読み出す。そしてその内容を書き込む。 ・1バイトスキップする。 ・20からバッファーフルまで順に書き込むもし、途中の読み込み処理をスキップすれば、test,0,1・・・8,9,20,21・・・42の順でfifoにはデータが蓄えられまが、fifo故、expected_result[]となりますよ。って事です。
#include <linux/init.h> #include <linux/module.h> #include <linux/proc_fs.h> #include <linux/mutex.h> #include <linux/kfifo.h> /* fifo size in elements (bytes) */ #define FIFO_SIZE 32 /* name of the proc entry */ #define PROC_FIFO "bytestream-fifo" /* lock for procfs read access */ static DEFINE_MUTEX(read_lock); /* lock for procfs write access */ static DEFINE_MUTEX(write_lock); #if 0 #define DYNAMIC #endif #ifdef DYNAMIC static struct kfifo test; #else static DECLARE_KFIFO(test, unsigned char, FIFO_SIZE); #endif static const unsigned char expected_result[FIFO_SIZE] = { 3, 4, 5, 6, 7, 8, 9, 0, 1, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, }; static int __init testfunc(void) { unsigned char buf[6]; unsigned char i, j; unsigned int ret; printk(KERN_INFO "byte stream fifo test start\n"); /* put string into the fifo */ kfifo_in(&test, "hello", 5); /* put values into the fifo */ for (i = 0; i != 10; i++) kfifo_put(&test, &i); /* show the number of used elements */ printk(KERN_INFO "fifo len: %u\n", kfifo_len(&test)); /* get max of 5 bytes from the fifo */ i = kfifo_out(&test, buf, 5); printk(KERN_INFO "buf: %.*s\n", i, buf); /* get max of 2 elements from the fifo */ ret = kfifo_out(&test, buf, 2); printk(KERN_INFO "ret: %d\n", ret); /* and put it back to the end of the fifo */ ret = kfifo_in(&test, buf, ret); printk(KERN_INFO "ret: %d\n", ret); /* skip first element of the fifo */ printk(KERN_INFO "skip 1st element\n"); kfifo_skip(&test); /* put values into the fifo until is full */ for (i = 20; kfifo_put(&test, &i); i++) ; printk(KERN_INFO "queue len: %u\n", kfifo_len(&test)); /* show the first value without removing from the fifo */ if (kfifo_peek(&test, &i)) printk(KERN_INFO "%d\n", i); /* check the correctness of all values in the fifo */ j = 0; while (kfifo_get(&test, &i)) { printk(KERN_INFO "item = %d\n", i); if (i != expected_result[j++]) { printk(KERN_WARNING "value mismatch: test failed\n"); return -EIO; } } if (j != ARRAY_SIZE(expected_result)) { printk(KERN_WARNING "size mismatch: test failed\n"); return -EIO; } printk(KERN_INFO "test passed\n"); return 0; } static ssize_t fifo_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { int ret; unsigned int copied; if (mutex_lock_interruptible(&write_lock)) return -ERESTARTSYS; ret = kfifo_from_user(&test, buf, count, &copied); mutex_unlock(&write_lock); return ret ? ret : copied; } static ssize_t fifo_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { int ret; unsigned int copied; if (mutex_lock_interruptible(&read_lock)) return -ERESTARTSYS; ret = kfifo_to_user(&test, buf, count, &copied); mutex_unlock(&read_lock); return ret ? ret : copied; } static const struct file_operations fifo_fops = { .owner = THIS_MODULE, .read = fifo_read, .write = fifo_write, .llseek = noop_llseek, }; static int __init example_init(void) { #ifdef DYNAMIC int ret; ret = kfifo_alloc(&test, FIFO_SIZE, GFP_KERNEL); if (ret) { printk(KERN_ERR "error kfifo_alloc\n"); return ret; } #else INIT_KFIFO(test); #endif if (testfunc() < 0) { #ifdef DYNAMIC kfifo_free(&test); #endif return -EIO; } if (proc_create(PROC_FIFO, 0, NULL, &fifo_fops) == NULL) { #ifdef DYNAMIC kfifo_free(&test); #endif return -ENOMEM; } return 0; } static void __exit example_exit(void) { remove_proc_entry(PROC_FIFO, NULL); #ifdef DYNAMIC kfifo_free(&test); #endif } module_init(example_init); module_exit(example_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Stefani Seibold <stefani@seibold.net>");
[root@localhost ~]# dmesg [11715.316317] byte stream fifo test start [11715.316329] fifo len: 15 <-データ数(test,0,1,2・・・9) [11715.316334] buf: hello [11715.316337] ret: 2 <-読み込んだバイト数 [11715.316363] ret: 2 <-書き込んだバイト数 [11715.316367] skip 1st element [11715.316370] queue len: 32 <-データ数(バッファフルまで書き込んだ後。=FIFO_SIZE [11715.316373] 3 <-バッファ先頭の内容 [11715.316376] item = 3 [11715.316379] item = 4 [11715.316381] item = 5 [11715.316384] item = 6 [11715.316386] item = 7 [11715.316389] item = 8 [11715.316391] item = 9 [11715.316394] item = 0 [11715.316396] item = 1 [11715.316399] item = 20 [11715.316401] item = 21 [11715.316404] item = 22 [11715.316406] item = 23 [11715.316409] item = 24 [11715.316411] item = 25 [11715.316414] item = 26 [11715.316416] item = 27 [11715.316419] item = 28 [11715.316421] item = 29 [11715.316423] item = 30 [11715.316426] item = 31 [11715.316428] item = 32 [11715.316431] item = 33 [11715.316433] item = 34 [11715.316435] item = 35 [11715.316438] item = 36 [11715.316440] item = 37 [11715.316443] item = 38 [11715.316445] item = 39 [11715.316448] item = 40 [11715.316450] item = 41 [11715.316453] item = 42 [11715.316455] test passedprocfsでbytestream-fifoを作成し、ユーザプロセスからもこのkfifoを参照可能としています。