Chinaunix

标题: relay [打印本页]

作者: gxy_0202    时间: 2008-11-17 16:24
标题: relay
Relay:一种内核到用户空间的高效数据传输技术




文档选项


未显示需要 JavaScript
的文档选项



打印本页



将此页作为电子邮件发送
级别: 中级
桂 剑
(
[email=guijian@cn.ibm.com?subject=Relay%EF%BC%9A%E4%B8%80%E7%A7%8D%E5%86%85%E6%A0%B8%E5%88%B0%E7%94%A8%E6%88%B7%E7%A9%BA%E9%97%B4%E7%9A%84%E9%AB%98%E6%95%88%E6%95%B0%E6%8D%AE%E4%BC%A0%E8%BE%93%E6%8A%80%E6%9C%AF]guijian@cn.ibm.com[/email]
), IBM 中国开发中心,Linux Performance 项目软件工程师
2006 年  12 月  28 日
Relay
是一种从 Linux 内核到用户空间的高效数据传输技术。通过用户定义的 relay
通道,内核空间的程序能够高效、可靠、便捷地将数据传输到用户空间。Relay
特别适用于内核空间有大量数据需要传输到用户空间的情形,目前已经广泛应用在内核调试工具如 SystemTap中。本文介绍了 Relay
的历史和原理,并且用一个简单的实例介绍了 Relay 的具体用法。
       
Relay 要解决的问题
对于任何在内核工作的程序而言,如何把大量的调试信息从内核空间传输到用户空间都是一个大麻烦,对于运行中的内核更是如此。特别是对于哪些用于调试内核性能的工具,更是如此。
对于这种大量数据需要在内核中缓存并传输到用户空间需求,很多传统的方法都已到达了极限,例如内核程序员很熟悉的 printk() 调用。此外,如果不同的内核子系统都开发自己的缓存和传输代码,造成很大的代码冗余,而且也带来维护上的困难。
这些,都要求开发一套能够高效可靠地将数据从内核空间转发到用户空间的系统,而且这个系统应该独立于各个调试子系统。
这样就诞生了 RelayFS。




回页首
Relay的发展历史
Relay 的前身是 RelayFS,即作为 Linux
的一个新型文件系统。2003年3月,RelayFS的第一个版本的代码被开发出来,在7月14日,第一个针对2.6内核的版本也开始提供下载。经过广泛
的试用和改进,直到2005年9月,RelayFS才被加入mainline内核(2.6.14)。同时,RelayFS也被移植到2.4内核中。在
2006年2月,从2.6.17开始,RelayFS不再作为单独的文件系统存在,而是成为内核的一部分。它的源码也从fs/目录下转移到kernel
/relay.c中,名称中也从RelayFS改成了Relay。
RelayFS目前已经被越来越多的内核工具使用,包括内核调试工具SystemTap、LTT,以及一些特殊的文件系统例如DebugFS。




回页首
Relay的基本原理
总的说来,Relay提供了一种机制,使得内核空间的程序能够通过用户定义的relay通道(channel)将大量数据高效的传输到用户空间。
一个relay通道由一组和CPU一一对应的内核缓冲区组成。这些缓冲区又被称为relay缓冲区(buffer),其中的每一个在用户空间都用一
个常规文件来表示,这被叫做relay文件(file)。内核空间的用户可以利用relay提供的API接口来写入数据,这些数据会被自动的写入当前的
CPU
id对应的那个relay缓冲区;同时,这些缓冲区从用户空间看来,是一组普通文件,可以直接使用read()进行读取,也可以使用mmap()进行映
射。Relay并不关心数据的格式和内容,这些完全依赖于使用relay的用户程序。Relay的目的是提供一个足够简单的接口,从而使得基本操作尽可能
的高效。
Relay将数据的读和写分离,使得突发性大量数据写入的时候,不需要受限于用户空间相对较慢的读取速度,从而大大提高了效率。Relay作为写入和读取的桥梁,也就是将内核用户写入的数据缓存并转发给用户空间的程序。这种转发机制也正是Relay这个名称的由来。
下面这个图给出了Relay的基本结构和典型操作:


Relay的基本结构和典型操作



可以看到,这里的relay通道由四个relay缓冲区(kbuf0到kbuf3)组成,分别对应于系统中的cpu0到cpu1。每个CPU上的代
码调用relay_write()的时候将数据写入自己对应的relay缓冲区内。每个relay缓冲区称一个relay文件,即/cpu0到
/cpu3。当文件系统被mount到/mnt/以后,这个relay文件就被映射成映射到用户空间的地址空间。一旦数据可用,用户程序就可以把它的数据
读出来写入到硬盘上的文件中,即cpu0.out到cpu3.out。




回页首
Relay的主要API
前面提到的 relay_write() 就是 relay API 之一。除此以外,Relay 还提供了更多的 API来支持用户程序完整的使用 relay。这些 API,主要按照面向用户空间和面向内核空间分为两大类,下面我们来分别进行介绍。
面向用户空间的 API
这些 Relay 编程接口向用户空间程序提供了访问 relay 通道缓冲区数据的基本操作的入口,包括:

       
面向内核空间的 API
这些API接口向位于内核空间的用户提供了管理relay通道、数据写入等功能。下面介绍其中主要的部分,完整的API接口列表请参见这里。

       




回页首
Relay的例子
我们用一个最简单的例子来介绍怎么使用Relay。这个例子由两部分组成:一部分是位于内核空间将数据写入relay文件的程序,使用时需要作为一个内核模块被加载;另一部分是位于用户空间从relay文件中读取数据的程序,使用时作为普通用户态程序运行。
内核空间的程序主要操作是:

       
       
程序内容:
/*
* hello-mod.c
* a kernel-space client example of relayfs filesystem
*/
#include
#include
static struct rchan *hello_rchan;
int init_module(void)
{
        const char *msg="Hello world\n";
        hello_rchan = relay_open("cpu", NULL, 8192, 2, NULL);
        if(!hello_rchan){
                printk("relay_open() failed.\n");
                return -ENOMEM;
        }
        relay_write(hello_rchan, msg, strlen(msg));
        return 0;
}
void cleanup_module(void)
{
        if(hello_rchan) {
                relay_close(hello_rchan);
                hello_rchan = NULL;
        }
        return;
}
MODULE_LICENSE ("GPL");
MODULE_DESCRIPTION ("Simple example of Relay");
用户空间的函数主要操作是:

       
程序内容:
/*
* audience.c
* a user-space client example of relayfs filesystem
*/
#include
#include
#include
#include
#include
#include
#include
#define MAX_BUFLEN 256
const char filename_base[]="/mnt/relay/cpu";
// implement your own get_cputotal() before compilation
static int get_cputotal(void);
int main(void)
{
        char filename[128]={0};
        char buf[MAX_BUFLEN];
        int fd, c, i, bytesread, cputotal = 0;
        if(mount("relayfs", "/mnt/relay", "relayfs", 0, NULL)
                        && (errno != EBUSY)) {
                printf("mount() failed: %s\n", strerror(errno));
                return 1;
        }
        cputotal = get_cputotal();
        if(cputotal  0) {
                        buf[bytesread] = '\0';
                        puts(buf);
                        bytesread = read(fd, buf, MAX_BUFLEN);
                };
                // close per-cpu file
                if(fd > 0) {
                        close(fd);
                        fd = 0;
                }
        }
        if(umount("/mnt/relay") && (errno != EINVAL)) {
                printf("umount() failed: %s\n", strerror(errno));
                return 1;
        }
        return 0;
}
上面这个例子给出了使用relay的一个最简单的情形,并没有实际用处,但是形象描述了从用户空间和内核空间两个方面使用relay的基本流程。实际应用中对relay的使用当然要比这复杂得多。更多的例子请参见relay的主页。
               
               
               
               

本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u2/79570/showart_1420576.html




欢迎光临 Chinaunix (http://bbs.chinaunix.net/) Powered by Discuz! X3.2