Tracefs 环形缓冲区内存映射¶
- 作者:
Vincent Donnefort <vdonnefort@google.com>
概述¶
Tracefs 环形缓冲区内存映射提供了一种高效的数据流传输方法,因为不需要内存拷贝。映射该环形缓冲区的应用程序随后会成为该环形缓冲区的消费者,其方式类似于 trace_pipe。
内存映射设置¶
该映射通过 trace_pipe_raw 接口的 mmap() 实现。
映射的第一个系统页面包含环形缓冲区的统计信息和描述。它被称为元页面 (meta-page)。元页面中最重要的字段之一是 reader。它包含可由映射器安全读取的子缓冲区 ID(参见 无锁环形缓冲区设计)。
元页面之后是所有子缓冲区,按 ID 升序排列。因此,可以毫不费力地知道读取器在映射中的起始位置。
reader_id = meta->reader->id;
reader_offset = meta->meta_page_size + reader_id * meta->subbuf_size;
当应用程序处理完当前的读取器时,它可以使用 trace_pipe_raw 的 ioctl() TRACE_MMAP_IOCTL_GET_READER 获取一个新的。此 ioctl 还会更新元页面字段。
限制¶
当 Tracefs 环形缓冲区上存在映射时,将无法调整其大小(无论是增加环形缓冲区的整体大小还是每个子缓冲区的大小)。同时也无法使用快照 (snapshot),并且这会导致 splice 复制环形缓冲区数据,而不是使用从环形缓冲区进行的无拷贝交换。
允许并发读取器(无论是映射该环形缓冲区的另一个应用程序,还是使用 trace_pipe 的内核),但不建议这样做。它们将竞争环形缓冲区,导致输出不可预测,就像 trace_pipe 上的并发读取器一样。
示例¶
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <linux/trace_mmap.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#define TRACE_PIPE_RAW "/sys/kernel/tracing/per_cpu/cpu0/trace_pipe_raw"
int main(void)
{
int page_size = getpagesize(), fd, reader_id;
unsigned long meta_len, data_len;
struct trace_buffer_meta *meta;
void *map, *reader, *data;
fd = open(TRACE_PIPE_RAW, O_RDONLY | O_NONBLOCK);
if (fd < 0)
exit(EXIT_FAILURE);
map = mmap(NULL, page_size, PROT_READ, MAP_SHARED, fd, 0);
if (map == MAP_FAILED)
exit(EXIT_FAILURE);
meta = (struct trace_buffer_meta *)map;
meta_len = meta->meta_page_size;
printf("entries: %llu\n", meta->entries);
printf("overrun: %llu\n", meta->overrun);
printf("read: %llu\n", meta->read);
printf("nr_subbufs: %u\n", meta->nr_subbufs);
data_len = meta->subbuf_size * meta->nr_subbufs;
data = mmap(NULL, data_len, PROT_READ, MAP_SHARED, fd, meta_len);
if (data == MAP_FAILED)
exit(EXIT_FAILURE);
if (ioctl(fd, TRACE_MMAP_IOCTL_GET_READER) < 0)
exit(EXIT_FAILURE);
reader_id = meta->reader.id;
reader = data + meta->subbuf_size * reader_id;
printf("Current reader address: %p\n", reader);
munmap(data, data_len);
munmap(meta, meta_len);
close (fd);
return 0;
}