relayfs是一个快速的转发(relay)数据的文件体系,它以其功用而得名。它为那些需求从内核空间转发很多数据到用户空间的东西和运用供给了快速有用的转发机制。
Channel是relayfs文件体系界说的一个首要概念,每一个channel由一组内核缓存组成,每一个CPU有一个对应于该channel 的内核缓存,每一个内核缓存用一个在relayfs文件体系中的文件文件表明,内核运用relayfs供给的写函数把需求转发给用户空间的数据快速地写入当时CPU上的channel内核缓存,用户空间运用经过规范的文件I/O函数在对应的channel文件中能够快速地获得这些被转宣布的数据mmap 来。写入到channel中的数据的格局彻底取决于内核中创立channel的模块或子体系。
relayfs的用户空间API:
relayfs完结了四个规范的文件I/O函数,open、mmap、poll和close.
open(),翻开一个channel在某一个CPU上的缓存对应的文件。
mmap(),把翻开的channel缓存映射到调用者进程的内存空间。
read (),读取channel缓存,随后的读操作将看不到被该函数耗费的字节,假如channel的操作形式为非掩盖写,那么用户空间运用在有内核模块写时仍 能够读取,可是假如channel的操作形式为掩盖式,那么在读操作期间假如有内核模块进行写,成果将无法预知,因此关于掩盖式写的channel,用户 应当在承认在channel的写彻底完毕后再进行读。
poll(),用于告诉用户空间运用转发数据跨过了子缓存的鸿沟,支撑的轮询标志有POLLIN、POLLRDNORM和POLLERR。
close(),封闭open函数回来的文件描述符,假如没有进程或内核模块翻开该channel缓存,close函数将开释该channel缓存。
留意:用户态运用在运用上述API时有必要确保现已挂载了relayfs文件体系,但内核在创立和运用channel时不需求relayfs现已挂载。下面指令将把relayfs文件体系挂载到/mnt/relay。
mount -t relayfs relayfs /mnt/relay
relayfs内核API:
relayfs供给给内核的API包括四类:channel办理、写函数、回调函数和辅佐函数。
Channel办理函数包括:
relay_open(base_filename, parent, subbuf_size, n_subbufs, overwrite, callbacks)
relay_close(chan)
relay_flush(chan)
relay_reset(chan)
relayfs_create_dir(name, parent)
relayfs_remove_dir(dentry)
relay_commit(buf, reserved, count)
relay_subbufs_consumed(chan, cpu, subbufs_consumed)
写函数包括:
relay_write(chan, data, length)
__relay_write(chan, data, length)
relay_reserve(chan, length)
回调函数包括:
subbuf_start(buf, subbuf, prev_subbuf_idx, prev_subbuf)
buf_mapped(buf, filp)
buf_unmapped(buf, filp)
辅佐函数包括:
relay_buf_full(buf)
subbuf_start_reserve(buf, length)
前面现已讲过,每一个channel由一组channel缓存组成,每个CPU对应一个该channel的缓存,每一个缓存又由一个或多个子缓存组成,每一个缓存是子缓存组成的一个环型缓存。
函数relay_open用于创立一个channel并分配对应于每一个CPU的缓存,用户空间运用经过在relayfs文件体系中对应的文件能够 拜访channel缓存,参数base_filename用于指定channel的文件名,relay_open函数将在relayfs文件体系中创立 base_filename0..base_filenameN-1,即每一个CPU对应一个channel文件,其间N为CPU数,缺省情况下,这些文件将建立在relayfs文件体系的根目录下,但假如参数parent非空,该函数将把channel文件创立于parent目录下,parent目录使 用函数relay_create_dir创立,函数relay_remove_dir用于删去由函数relay_create_dir创立的目录,谁创立的目录,谁就担任在不用时担任删去。参数subbuf_size用于指定channel缓存中每一个子缓存的巨细,参数n_subbufs用于指定 channel缓存包括的子缓存数,因此实践的channel缓存巨细为(subbuf_size x n_subbufs),参数overwrite用于指定该channel的操作形式,relayfs供给了两种写形式,一种是掩盖式写,另一种对错掩盖式 写。运用哪一种形式彻底取决于函数subbuf_start的完结,掩盖写将在缓存已满的情况下无条件地持续从缓存的开端写数据,而不论这些数据是否现已 被用户运用读取,因此写操作决不失利。在非掩盖写形式下,假如缓存满了,写将失利,但内核将在用户空间运用读取缓存数据时经过函数 relay_subbufs_consumed()告诉relayfs。假如用户空间运用没来得及耗费缓存中的数据或缓存已满,两种形式都将导致数据丢掉,仅有的区别是,前者丢掉数据在缓存最初,而后者丢掉数据在缓存结尾。一旦内核再次调用函数relay_subbufs_consumed(),已满的缓存将不再满,因此能够持续写该缓存。当缓存满了今后,relayfs将调用回调函数buf_full()来告诉内核模块或子体系。当新的数据太大无法写 入当时子缓存剩下的空间时,relayfs将调用回调函数subbuf_start()来告诉内核模块或子体系将需求运用新的子缓存。内核模块需求在该回调函数中完结下述功用:
初始化新的子缓存;
假如1正确,完结当时子缓存;
假如2正确,回来是否正确完结子缓存切换;
在非掩盖写形式下,回调函数subbuf_start()应该如下完结:
static int subbuf_start(struct rchan_buf *buf, void *subbuf, void *prev_subbuf, unsigned intprev_padding)
{
if (prev_subbuf)
*((unsigned *)prev_subbuf) = prev_padding;
if (relay_buf_full(buf))
return 0;
subbuf_start_reserve(buf, sizeof(unsigned int));
return 1;
}
假如当时缓存满,即一切的子缓存都没读取,该函数回来0,指示子缓存切换没有成功。当子缓存经过函数relay_subbufs_consumed ()被读取后,读取者将担任告诉relayfs,函数relay_buf_full()在现已有读者读取子缓存数据后回来0,在这种情况下,子缓存切换成 功进行。
在掩盖写形式下, subbuf_start()的完结与非掩盖形式相似:
staTIc int subbuf_start(struct rchan_buf *buf, void *subbuf, void *prev_subbuf, unsigned int prev_padding)
{
if (prev_subbuf)
*((unsigned *)prev_subbuf) = prev_padding;
subbuf_start_reserve(buf, sizeof(unsigned int));
return 1;
}
仅仅不做relay_buf_full()查看,因为此形式下,缓存是环行的,能够无条件地写。因此在此形式下,子缓存切换必定成功,函数 relay_subbufs_consumed() 也无须调用。假如channel写者没有界说subbuf_start(),缺省的完结将被运用。 能够经过在回调函数subbuf_start()中调用辅佐函数subbuf_start_reserve()在子缓存中预留头空间,预留空间能够保存任 何需求的信息,如上面比如中,预留空间用于保存子缓存填充字节数,在subbuf_start()完结中,前一个子缓存的填充值被设置。前一个子缓存的填 充值和指向前一个子缓存的指针一道作为subbuf_start()的参数传递给subbuf_start(),只要在子缓存完结后,才干知道填充值。 subbuf_start()也被在channel创立时分配每一个channel缓存的榜首个子缓存时调用,以便预留头空间,但在这种情况下,前一个子 缓存指针为NULL。
内核模块运用函数relay_write()或__relay_write()往channel缓存中写需求转发的数据,它们的区别是前者失效了本 地中止,而后者只抢占失效,因此前者能够在任何内核上下文安全运用,而后者应当在没有任何中止上下文将写channel缓存的情况下运用。这两个函数没有 回来值,因此用户不能直接确认写操作是否失利,在缓存满且写形式为非掩盖形式时,relayfs将经过回调函数buf_full来告诉内核模块。
函数relay_reserve()用于在channel缓存中预留一段空间以便今后写入,在那些没有暂时缓存而直接写入channel缓存的内核 模块或许需求该函数,运用该函数的内核模块在实践写这段预留的空间时能够经过调用relay_commit()来告诉relayfs。当一切预留的空间全 部写完并经过relay_commit告诉relayfs后,relayfs将调用回调函数deliver()告诉内核模块一个完好的子缓存现已填满。因为预留空间的操作并不在写channel的内核模块彻底操控之下,因此relay_reserve()不能很好地维护缓存,因此当内核模块调用 relay_reserve()时有必要采纳恰当的同步机制。
当内核模块完毕对channel的运用后需求调用relay_close() 来封闭channel,假如没有任何用户在引证该channel,它将和对应的缓存悉数被开释。
函数relay_flush()强制在一切的channel缓存上做一个子缓存切换,它在channel被封闭前运用来停止和处理最终的子缓存。
函数relay_reset()用于将一个channel康复到初始状况,因此不用开释现存的内存映射并重新分配新的channel缓存就能够运用channel,可是该调用只要在该channel没有任何用户在写的情况下才干够安全运用。
回调函数buf_mapped() 在channel缓存被映射到用户空间时被调用。
回调函数buf_unmapped()在开释该映射时被调用。内核模块能够经过它们触发一些内核操作,如开端或完毕channel写操作。
在源代码包中给出了一个运用relayfs的示例程序relayfs_exam.c,它只包括一个内核模块,关于杂乱的运用,需求运用程序合作。该模块完结了相似于文章中seq_file示例完结的功用。
当然为了运用relayfs,用户有必要让内核支撑relayfs,而且要mount它,下面是作者体系上的运用该模块的输出信息:
$ mkdir -p /relayfs
$ insmod 。/relayfs-exam.ko
$ mount -t relayfs relayfs /relayfs
$ cat /relayfs/example0
…
$
relayfs是一种比较杂乱的内核态与用户态的数据交换方法,本比如程序只供给了一个较简略的运用方法,关于杂乱的运用,请参阅relayfs用例页面http://relayfs.sourceforge.net/examples.html。
//kernel module: relayfs-exam.c
#include 《linux/module.h》
#include 《linux/relayfs_fs.h》
#include 《linux/string.h》
#include 《linux/sched.h》
#define WRITE_PERIOD (HZ * 60)
staTIc struct rchan * chan;
staTIc size_t subbuf_size = 65536;
staTIc size_t n_subbufs = 4;
static char buffer[256];
void relayfs_exam_write(unsigned long data);
static DEFINE_TIMER(relayfs_exam_timer, relayfs_exam_write, 0, 0);
void relayfs_exam_write(unsigned long data)
{
int len;
task_t * p = NULL;
len = sprintf(buffer, “Current all the processes:\n”);
len += sprintf(buffer + len, “process name\t\tpid\n”);
relay_write(chan, buffer, len);
for_each_process(p) {
len = sprintf(buffer, “%s\t\t%d\n”, p-》comm, p-》pid);
relay_write(chan, buffer, len);
}
len = sprintf(buffer, “\n\n”);
relay_write(chan, buffer, len);
relayfs_exam_timer.expires = jiffies + WRITE_PERIOD;
add_timer(&relayfs_exam_timer);
}
/*
* subbuf_start() relayfs callback.
*
* Defined so that we can 1) reserve padding counts in the sub-buffers, and
* 2) keep a count of events dropped due to the buffer-full condition.
*/
static int subbuf_start(struct rchan_buf *buf,
void *subbuf,
void *prev_subbuf,
unsigned int prev_padding)
{
if (prev_subbuf)
*((unsigned *)prev_subbuf) = prev_padding;
if (relay_buf_full(buf))
return 0;
subbuf_start_reserve(buf, sizeof(unsigned int));
return 1;
}
/*
* relayfs callbacks
*/
static struct rchan_callbacks relayfs_callbacks =
{
.subbuf_start = subbuf_start,
};
/**
* module init – creates channel management control files
*
* Returns 0 on success, negative otherwise.
*/
static int init(void)
{
chan = relay_open(“example”, NULL, subbuf_size,
n_subbufs, &relayfs_callbacks);
if (!chan) {
printk(“relay channel creation failed.\n”);
return 1;
}
relayfs_exam_timer.expires = jiffies + WRITE_PERIOD;
add_timer(&relayfs_exam_timer);
return 0;
}
static void cleanup(void)
{
del_timer_sync(&relayfs_exam_timer);
if (chan) {
relay_close(chan);
chan = NULL;
}
}
module_init(init);
module_exit(cleanup);
MODULE_LICENSE(“GPL”);