从Linux-2.6.14内核(2.6.12需要打补丁,2.6.13的内核手头没有,不知道)开始,relayfs开始作为内核中File System选项中伪文件系统(Pseudo File System)来出现,这是一个新特性。
File System--->
Pseudo filesystems---->
<>Relayfs File System Support
我们知道,Pseduo File System 另外一个很有名的东西是Proc File System,几乎每个学习Linux的都知道使用这个文件系统来查看cpu型号,内存容量等等其它很多的runtime information。Proc FS为users提供了一个方便的接口来查询很多只有内核才能查看的信息,比如:cpuinfo,meminfo,interrupts等等,这些都只是 kernel管理的对象,但是我们可以以一个普通users的身份也可以查看,proc FS将内核信息可以动态地传递出来,供普通的process随时查看,某些情况下,用户也可以将信息传递到内核空间,比如:echo 1>/proc/sys/net/ipv4/ip_forward。同样地,relayfs也是可以一种内核和用户空间交换数据的工具,不同的是,它支持大容量的数据交换。
relayfs中有一个很重要的概念叫做”channel“,具体来说,一个channel就是由很多个内核的buffer组成的一个集合,这些内核的buffer在relayfs中就体现为一个个的文件。 当kernel中的程序把数据写入某个channel时,这些数据实际上自动填入这些channel的buffer。 用户空间的应用程序mmap()将relayfs中的这些文件做个映射,然后在适当的时候把数据提取出来。
写入channel的数据格式完全取决于最终从channel中提取数据的程序,relayfs可以提取一些hook程序,这些hook程序允许relayfs的数据提取程序(relayfs的客户端)为buffer中的数据增加一些数据结构。这个过程,就像解码跟编码的关系一样,你使用的编码程序和解码程序只有对应就可以,与传输程序无关,当然,你在传输的同时也可以对它进行一些编码,但是这些取决于你最终的解码。 但是,relayfs不提供任何形式的数据过滤,这些任务留给relayfs客户端去完成。 relayfs的设计目标就是尽可能地简单。
每一个relayfs channel都有一个buffer(单CPU情况),每一个buffer又有一个或者多个二级buffer。 消息是从第一个二级buffer开始写入的,直到这个buffer满为止。然后如果第二个二级buffer可用,就写入第二个二级部分反而,一次类推。 所以,如果第一个二级buffer被填满,那么就会通知用户空间,同时,kernel就会去写第二个二级buffer。
如果kernel发出通知说一个二级buffer被填满了,那么kernel肯定知道填了多少字节。userspace根据这个数字就可以仅仅拷贝合法的数据。拷贝完毕,userpsace通知kernel说一个二级buffer已经被使用了。
relayfs采用这么一种模式,它会直接去覆盖数据即使这些数据还没有被userspace所收集。(一个问题,如何保持传输的完整性,目前我也不知道)
下面说说relayfs的user space API:
relayfs为了使得空间程序可以访问channel里面的buffer数据,实现了基本的文件操作。文件操作函数如下:
open 打开一个存在的buffer
mmap 可以使得channel的buffer被映射到调用函数的内存空间,注意,你不能部分映射,而是要映射整个文件。
reald 读取channel buffer的内容
poll 通知用户空间程序二级buffer空间已满
close 关闭
为了使得用户空间的程序可以使用relayfs文件,relayfs必须被mount,格式跟proc差不多,like this:
mount -t relayfs relayfs /mnt/relay/
下面是kernel空间的一些API:
relay_open(base_filename, parent, subbuf_size, n_subbufs, callbacks)
relay_close(chan)
relay_flush(chan)
relay_reset(chan)
relayfs_create_dir(name, parent)
relayfs_remove_dir(dentry)
relayfs_create_file(name, parent, mode, fops, data)
relayfs_remove_file(dentry)
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, prev_padding)
buf_mapped(buf, filp)
buf_unmapped(buf, filp)
create_buf_file(filename, parent, mode, buf, is_global)
remove_buf_file(dentry)