Relay 的原理Relay 提供了一种机制,使得内核空间的程序能够通过用户定义的 Relay 通道(channel)将 大量数据高效地传输到用户空间。一个 Relay 通道由一组和 CPU 一一对应的内核缓冲区组成。这 些缓冲区又被称为 Relay 缓冲区(buffer),其中的每一个在用户空间都用一个常规文件来表示, 叫做 Relay 文件(file)。内核空间的用户可以利用 Relay 提供的 API 接口来写入数据,这些数据 会被自动写入当前的 CPU ID 对应的那个 Relay 缓冲区;同时,这些缓冲区从用户空间看来,是 一组普通文件,可以直接使用 read()进行读取,也可以使用 mmap()进行映射。Relay 并不关心数 据的格式和内容,这些完全依赖于使用 Relay 的用户程序。Relay 的目的是提供一个足够简单的 接口,从而使得基本操作尽可能高效。 Relay 实现了对数据读和写的分离,使得大量突发性数据写入的时候,不需要受限于用户空 间相对较慢的读取速度,从而大大提高了效率。Relay 作为写入和读取的桥梁,也就是将内核用 户写入的数据缓存并转发给用户空间的程序。这种转发机制正是 Relay 这个名称的由来。
open():允许用户打开一个已经存在的通道缓冲区。
mmap():使通道缓冲区被映射到位于用户空间的调用者的地址空间。要特别注意的是, 我们不能仅对局部区域进行映射。也就是说,必须映射整个缓冲区文件,其大小是 CPU 的个数和单个 CPU 缓冲区大小的乘积。
read():读取通道缓冲区的内容。这些数据一旦被读出,就意味着它们被用户空间的程序 消费掉了,不能被之后的读操作看到。
sendfile():将数据从通道缓冲区传输到一个输出文件描述符。其中可能的填充字符会被自 动去掉,不会被用户看到。
poll():支持 POLLIN/POLLRDNORM/POLLERR 信号。每次子缓冲区的边界被越过时, 等待着的用户空间程序会得到通知。
close():将通道缓冲区的引用数减 1。当引用数减为 0 时,表明没有进程或者内核用户需 要打开它,从而这个通道缓冲区被释放。
relay_open():创建一个 Relay 通道,包括创建每个 CPU 对应的 Relay 缓冲区。
relay_close():关闭一个 Relay 通道,包括释放所有的 Relay 缓冲区,在此之前会调用 relay_switch()来处理这些 Relay 缓冲区以保证已读取但是未满的数据不会丢失。
relay_write():将数据写入到当前 CPU 对应的 Relay 缓冲区内。由于它使用了 local_irqsave() 保护,因此也可以在中断上下文中使用。
relay_reserve(): Relay 通道中保留一块连续的区域来留给未来的写入操作。在通常用于那 些希望直接写入到 Relay 缓冲区的用户。考虑到性能或者其他因素,这些用户不希望先把 数据写到一个临时缓冲区中,然后再通过 relay_write()进行写入。